Skip to content

Commit

Permalink
CommandTrigger handles local variable injection with @optional (param…
Browse files Browse the repository at this point in the history
…eters are working as well with static var replacement)
  • Loading branch information
FrancisBourre committed Dec 20, 2017
1 parent cb5438d commit da13dee
Show file tree
Hide file tree
Showing 3 changed files with 242 additions and 36 deletions.
77 changes: 43 additions & 34 deletions src/hex/control/trigger/CommandTriggerBuilder.hx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import hex.annotation.AnnotationReplaceBuilder;
import hex.control.payload.ExecutionPayload;
import hex.control.trigger.Command;
import hex.di.IDependencyInjector;
import hex.error.PrivateConstructorException;
import hex.module.IContextModule;
import hex.util.MacroUtil;

Expand All @@ -26,20 +25,12 @@ class CommandTriggerBuilder
{
public static inline var MapAnnotation = "Map";

/** @private */
function new()
{
throw new PrivateConstructorException( "This class can't be instantiated." );
}
/** @private */ function new() throw new hex.error.PrivateConstructorException( "This class can't be instantiated." );

macro static public function build() : Array<Field>
{
var fields = Context.getBuildFields();

if ( Context.getLocalClass().get().isInterface )
{
return fields;
}
if ( Context.getLocalClass().get().isInterface ) return fields;

var CommandClassType = MacroUtil.getClassType( Type.getClassName( Command ) );
var MacroCommandClassType = MacroUtil.getClassType( Type.getClassName( MacroCommand ) );
Expand All @@ -58,7 +49,6 @@ class CommandTriggerBuilder
if ( isMapped )
{
var className = Context.getLocalModule();

if ( m.length > 1 )
{
Context.error( "'" + f.name + "' method defines more than one command mapping (with '@" +
Expand Down Expand Up @@ -171,12 +161,7 @@ class CommandTriggerBuilder
func.expr = macro
{
var injections : Array<{value: Dynamic, className: String, mapName: String}> = $a { arguments };
var payloads = [];
for ( injected in injections )
{
payloads.push( new hex.control.payload.ExecutionPayload( injected.value, null, injected.mapName ).withClassName( injected.className ) );
}

var payloads = [ for ( injected in injections ) new hex.control.payload.ExecutionPayload( injected.value, null, injected.mapName ).withClassName( injected.className ) ];
hex.control.payload.PayloadUtil.mapPayload( payloads, this.injector );
var command = this.injector.instantiateUnmapped( $p { className } );
hex.control.payload.PayloadUtil.unmapPayload( payloads, this.injector );
Expand Down Expand Up @@ -228,20 +213,14 @@ class CommandTriggerBuilder
{
switch( arg.expr )
{
case EMeta( meta1, _.expr => EMeta( meta2, _.expr => EVars( vars ) ) ) if ( meta1.name == "Inject" && meta2.name == "Optional" ):
_generateInjectionCode( meta1, arg, vars, _shouldThrowAnError( meta2 ) );

case EMeta( meta1, _.expr => EMeta( meta2, _.expr => EVars( vars ) ) ) if ( meta1.name == "Optional" && meta2.name == "Inject" ):
_generateInjectionCode( meta2, arg, vars, _shouldThrowAnError( meta1 ) );

case EMeta( s, _.expr => EVars( vars ) ) if ( s.name == "Inject" ):
var mapName = if ( s.params.length > 0 )
{
var transformed = AnnotationReplaceBuilder.processParam(s.params[ 0 ]);
switch(transformed.expr)
{
case EConst(CString(name)): name;
case _: "";
}
} else "";

var varName = vars[0].name;
var varType = vars[0].type;
arg.expr = (macro var $varName : $varType = this.injector.getInstanceWithClassName( $v{ MacroUtil.getFQCNFromComplexType( varType ) }, $v{mapName} )).expr;
_generateInjectionCode( s, arg, vars );

case _:
CommandTriggerBuilder._searchForInjection( arg );
Expand All @@ -252,12 +231,42 @@ class CommandTriggerBuilder
}
}

static function hasMetaValue( meta, metaName : String )
static function _shouldThrowAnError( s : MetadataEntry ) : Bool
{
if ( s.params.length > 0 )
{
var transformed = AnnotationReplaceBuilder.processParam( s.params[ 0 ] );
return switch( transformed.expr )
{
case EConst(CIdent( "true" )): false;
case EConst(CIdent("false")): true;
case _: false;
}
}

return false;
}

static function _generateInjectionCode( s : MetadataEntry, arg : Expr, vars : Array<Var>, shouldThrowAnError = true )
{
var meta = Lambda.find( meta, function(m) return m.name == metaName );
return ( meta != null );
var mapName = if ( s.params.length > 0 )
{
var transformed = AnnotationReplaceBuilder.processParam( s.params[ 0 ] );
switch( transformed.expr )
{
case EConst(CString(name)): name;
case _: "";
}
} else "";

var varName = vars[0].name;
var varType = vars[0].type;
arg.expr = (macro var $varName : $varType = this.injector.getInstanceWithClassName( $v{ MacroUtil.getFQCNFromComplexType( varType ) }, $v{mapName}, null, $v{shouldThrowAnError} )).expr;
}

static function hasMetaValue( meta, metaName : String )
return Lambda.find( meta, function(m) return m.name == metaName ) != null;

static function getMetaValue( meta : Null<Metadata>, metaName : String )
{
var meta : MetadataEntry = Lambda.find( meta, function(m) return m.name == metaName );
Expand Down
121 changes: 119 additions & 2 deletions test/hex/control/trigger/CommandTriggerTest.hx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ package hex.control.trigger;
import hex.collection.Locator;
import hex.control.trigger.MockCommandClassWithParameters;
import hex.control.trigger.MockCommandClassWithoutParameters;
import hex.control.trigger.mock.MockController;
import hex.control.trigger.mock.AnotherMockCommand;
import hex.control.trigger.mock.MockCommand;
import hex.control.trigger.mock.MockController;
import hex.control.trigger.mock.MockMacroCommand;
import hex.control.trigger.mock.MockMacroController;
import hex.control.trigger.mock.MockModule;
import hex.di.IDependencyInjector;
import hex.di.Injector;
import hex.log.ILogger;
import hex.di.error.MissingMappingException;
import hex.module.IContextModule;
import hex.module.IModule;
import hex.unittest.assertion.Assert;
import hex.unittest.runner.MethodRunner;
Expand Down Expand Up @@ -164,4 +165,120 @@ class CommandTriggerTest
Assert.equals( vos[ 8 ], acmd.pDate );
Assert.equals( vos[ 9 ], acmd.pEnum );
}

@Test( "test local var injection" )
public function testLocalVarInjection() : Void
{
//Settings
var injector = new Injector();
injector.mapToValue( IDependencyInjector, injector );
injector.mapToValue( IContextModule, new MockModule() );

this._controller = injector.instantiateUnmapped( MockController );
injector.mapToValue( String, 'test' );

//testing
var result = this._controller.testLocalVarInjection();
Assert.equals( 'test', result );
}

@Test( "test local var injection with name" )
public function testLocalVarInjectionWithName() : Void
{
//Settings
var injector = new Injector();
injector.mapToValue( IDependencyInjector, injector );
injector.mapToValue( IContextModule, new MockModule() );

this._controller = injector.instantiateUnmapped( MockController );
injector.mapToValue( String, 'test', 'test' );

//testing
var result = this._controller.testLocalVarInjectionWithName();
Assert.equals( 'test', result );
}

@Test( "test local var with missing optional injection" )
public function testLocalVarWithMissingOptionalInjection() : Void
{
//Settings
var injector = new Injector();
injector.mapToValue( IDependencyInjector, injector );
injector.mapToValue( IContextModule, new MockModule() );

this._controller = injector.instantiateUnmapped( MockController );

//testing
Assert.methodCallThrows( MissingMappingException, this._controller, this._controller.testLocalVarOptionalInjection, [] );

injector.mapToValue( String, 'test3', 'test3' );
var result = this._controller.testLocalVarOptionalInjection();
Assert.deepEquals( [ null, null, 'test3' ], result );
}

@Test( "test local var optional injection" )
public function testLocalVarOptionalInjection() : Void
{
//Settings
var injector = new Injector();
injector.mapToValue( IDependencyInjector, injector );
injector.mapToValue( IContextModule, new MockModule() );

this._controller = injector.instantiateUnmapped( MockController );
injector.mapToValue( String, 'test1', 'test1' );
injector.mapToValue( String, 'test2', 'test2' );

//testing
Assert.methodCallThrows( MissingMappingException, this._controller, this._controller.testLocalVarOptionalInjection, [] );

injector.mapToValue( String, 'test3', 'test3' );
var result = this._controller.testLocalVarOptionalInjection();
Assert.deepEquals( [ 'test1', 'test2', 'test3' ], result );
}

@Test( "test local var with parameterized optional injection" )
public function testLocalVarWithParameterizedOptionalInjection() : Void
{
//Settings
var injector = new Injector();
injector.mapToValue( IDependencyInjector, injector );
injector.mapToValue( IContextModule, new MockModule() );

this._controller = injector.instantiateUnmapped( MockController );
injector.mapToValue( String, 'test1', 'test1' );
injector.mapToValue( String, 'test2', 'test2' );

//testing
Assert.methodCallThrows( MissingMappingException, this._controller, this._controller.testLocalVarParamOptionalInjection, [] );

injector.mapToValue( String, 'test3', 'test3' );
Assert.methodCallThrows( MissingMappingException, this._controller, this._controller.testLocalVarParamOptionalInjection, [] );

injector.mapToValue( String, 'test4', 'test4' );
var result = this._controller.testLocalVarParamOptionalInjection();
Assert.deepEquals( [ 'test1', 'test2', 'test3', 'test4' ], result );
}

@Test( "test local var with replaced parameterized optional injection" )
public function testLocalVarWithReplacedParameterizedOptionalInjection() : Void
{
//Settings
var injector = new Injector();
injector.mapToValue( IDependencyInjector, injector );
injector.mapToValue( IContextModule, new MockModule() );

this._controller = injector.instantiateUnmapped( MockController );
injector.mapToValue( String, 'test1', 'test1' );
injector.mapToValue( String, 'test2', 'test2' );

//testing
Assert.methodCallThrows( MissingMappingException, this._controller, this._controller.testLocalVarReplacedParamOptionalInjection, [] );

injector.mapToValue( String, 'test3', 'test3' );
Assert.methodCallThrows( MissingMappingException, this._controller, this._controller.testLocalVarReplacedParamOptionalInjection, [] );

injector.mapToValue( String, 'test4', 'test4' );
var result = this._controller.testLocalVarReplacedParamOptionalInjection();
Assert.deepEquals( [ 'test1', 'test2', 'test3', 'test4' ], result );
}
}
80 changes: 80 additions & 0 deletions test/hex/control/trigger/mock/MockController.hx
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,84 @@ class MockController
{
return a + b;
}

public function testLocalVarInjection() : String
{
@Inject
var test : String;
return test;
}

public function testLocalVarInjectionWithName() : String
{
@Inject( 'test' )
var test : String;
return test;
}

public function testLocalVarOptionalInjection() : Array<String>
{
@Optional
@Inject( 'test1' )
var test1 : String;

@Inject( 'test2' )
@Optional
var test2 : String;

@Inject( 'test3' )
var test3 : String;

return [ test1, test2, test3 ];
}

public function testLocalVarParamOptionalInjection() : Array<String>
{
@Optional( true )
@Inject( 'test1' )
var test1 : String;

@Inject( 'test2' )
@Optional( true )
var test2 : String;

@Inject( 'test3' )
@Optional( false )
var test3 : String;

@Optional( false )
@Inject( 'test4' )
var test4 : String;

return [ test1, test2, test3, test4 ];
}

static var IS_TRUE = true;
static var IS_FALSE = false;

static var NAME_1 = 'test1';
static var NAME_2 = 'test2';
static var NAME_3 = 'test3';
static var NAME_4 = 'test4';

public function testLocalVarReplacedParamOptionalInjection() : Array<String>
{
@Optional( IS_TRUE )
@Inject( NAME_1 )
var test1 : String;

@Inject( NAME_2 )
@Optional( IS_TRUE )
var test2 : String;

@Inject( NAME_3 )
@Optional( IS_FALSE )
var test3 : String;

@Optional( IS_FALSE )
@Inject( NAME_4 )
var test4 : String;

return [ test1, test2, test3, test4 ];
}
}

0 comments on commit da13dee

Please sign in to comment.