Skip to content

Commit

Permalink
Merge pull request #27 from ortus-solutions-private/feature/onmissing…
Browse files Browse the repository at this point in the history
…method

Add onMissingMethod support
  • Loading branch information
bdw429s authored Jan 6, 2024
2 parents 35b8420 + 43f0883 commit 902e38b
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 41 deletions.
3 changes: 3 additions & 0 deletions src/main/java/ortus/boxlang/runtime/scopes/Key.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ public class Key {
public static final Key start = Key.of( "start" );
public static final Key length = Key.of( "length" );
public static final Key object = Key.of( "object" );
public static final Key onMissingMethod = Key.of( "onMissingMethod" );
public static final Key missingMethodName = Key.of( "missingMethodName" );
public static final Key missingMethodArguments = Key.of( "missingMethodArguments" );

public static final Key contains = Key.of( "contains" );
public static final Key find = Key.of( "find" );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,30 +256,34 @@ public Object dereferenceAndInvoke( IBoxContext context, Key name, Object[] posi
// TODO: component member methods?
Object value = thisScope.get( name );
if ( value != null ) {
if ( value instanceof Function function ) {
FunctionBoxContext functionContext = Function.generateFunctionContext(
function,
// Function contexts' parent is the caller. The function will "know" about the CFC it's executing in
// because we've pushed the CFC onto the template stack in the function context.
context,
name,
function.createArgumentsScope( positionalArguments )
);
functionContext.pushTemplate( this );
try {
return function.invoke( functionContext );
} finally{
functionContext.popTemplate();
}
} else {
throw new BoxRuntimeException(
"key '" + name.getName() + "' of type '" + value.getClass().getName() + "' is not a function " );
if ( value instanceof Function function ) {
FunctionBoxContext functionContext = Function.generateFunctionContext(
function,
// Function contexts' parent is the caller. The function will "know" about the CFC it's executing in
// because we've pushed the CFC onto the template stack in the function context.
context,
name,
function.createArgumentsScope( positionalArguments )
);
functionContext.pushTemplate( this );
try {
return function.invoke( functionContext );
} finally{
functionContext.popTemplate();
}
}
throw new BoxRuntimeException( "Method '" + name.getName() + "' not found" );
if ( value != null ) {
throw new BoxRuntimeException(
"key '" + name.getName() + "' of type '" + value.getClass().getName() + "' is not a function " );
}
if( thisScope.get( Key.onMissingMethod ) != null ){
return dereferenceAndInvoke( context, Key.onMissingMethod, new Object[]{ name.getName(), positionalArguments }, safe );
}
throw new BoxRuntimeException( "Method '" + name.getName() + "' not found" );
}
/**
Expand All @@ -294,30 +298,36 @@ public Object dereferenceAndInvoke( IBoxContext context, Key name, Object[] posi
public Object dereferenceAndInvoke( IBoxContext context, Key name, Map<Key, Object> namedArguments, Boolean safe ) {
Object value = thisScope.get( name );
if ( value != null ) {
if ( value instanceof Function function ) {
FunctionBoxContext functionContext = Function.generateFunctionContext(
function,
// Function contexts' parent is the caller. The function will "know" about the CFC it's executing in
// because we've pushed the CFC onto the template stack in the function context.
context,
name,
function.createArgumentsScope( namedArguments )
);
functionContext.pushTemplate( this );
try {
return function.invoke( functionContext );
} finally{
functionContext.popTemplate();
}
} else {
throw new BoxRuntimeException(
"key '" + name.getName() + "' of type '" + value.getClass().getName() + "' is not a function "
if ( value instanceof Function function ) {
FunctionBoxContext functionContext = Function.generateFunctionContext(
function,
// Function contexts' parent is the caller. The function will "know" about the CFC it's executing in
// because we've pushed the CFC onto the template stack in the function context.
context,
name,
function.createArgumentsScope( namedArguments )
);
functionContext.pushTemplate( this );
try {
return function.invoke( functionContext );
} finally{
functionContext.popTemplate();
}
}
throw new BoxRuntimeException( "Method '" + name.getName() + "' not found" );
if ( value != null ) {
throw new BoxRuntimeException(
"key '" + name.getName() + "' of type '" + value.getClass().getName() + "' is not a function " );
}
if( thisScope.get( Key.onMissingMethod ) != null ){
Map<Key, Object> args = new HashMap<Key, Object>();
args.put( Key.missingMethodName, name.getName() );
args.put( Key.missingMethodArguments, namedArguments );
return dereferenceAndInvoke( context, Key.onMissingMethod, args, safe );
}
throw new BoxRuntimeException( "Method '" + name.getName() + "' not found" );
}
Expand Down
14 changes: 14 additions & 0 deletions src/test/java/TestCases/phase3/ClassTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,20 @@ public void testlegacyMeta() {
assertThat( meta.get( Key.of( "accessors" ) ) ).isEqualTo( false );
}

@DisplayName( "It should call onMissingMethod" )
@Test
public void testOnMissingMethod() {

instance.executeStatement(
"""
cfc = new src.test.java.TestCases.phase3.OnMissingMethod();
result = cfc.someFunc();
""", context );

String res = variables.getAsString( result );
assertThat( res ).isEqualTo( "someFunc" );
}

@DisplayName( "box meta" )
@Test
public void testBoxMeta() {
Expand Down
5 changes: 5 additions & 0 deletions src/test/java/TestCases/phase3/OnMissingMethod.cfc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
component {
public any function onMissingMethod( string missingMethodName, any missingMethodArguments ){
return missingMethodName;
}
}

0 comments on commit 902e38b

Please sign in to comment.