tschneidereit / SwiftSuspenders

NOTE: Find the offical repo at http://github.com/robotlegs/swiftsuspenders
https://github.com/robotlegs/swiftsuspenders
MIT License
280 stars 89 forks source link

Child Injector Mapping not stored? #54

Open chrisathook opened 12 years ago

chrisathook commented 12 years ago

I ran into an interesting problem today while trying to create a child injector and I thought it might be an issue with 1.6. I was mapping an injection by value and event though though injector.hasMapping () returned true i was still giving me a mapping not found error.

i found that if i added a getInstance call it fixed it issue. It did not create a new instance, the dependency was mapped already it just was not being made avaliable.

// Does not work.

var childInjector:IInjector = injector.createChild (injector.applicationDomain);

var vo:InfoGraphicsDataModel = new InfoGraphicsDataModel (item,childInjector);

childInjector.mapValue (IInfoGrahicsDataModel, vo);

childInjector.injectInto (vo.rawContent);

// does work

var childInjector:IInjector = injector.createChild (injector.applicationDomain);

var vo:InfoGraphicsDataModel = new InfoGraphicsDataModel (item,childInjector);

childInjector.mapValue (IInfoGrahicsDataModel, vo); childInjector.getInstance (IInfoGrahicsDataModel); childInjector.injectInto (vo.rawContent);

tschneidereit commented 12 years ago

That does look like a bug indeed. I'll have a look.

ZackPierce commented 12 years ago

In this example, what class is the InfoGraphicsDataModel.rawContent property? Is that class mapped to anything in the injector?

Also, Does the class for the InfoGraphicsDataModel.rawContent property contain any injection points ( [Inject] tags) of its own? If so, what are they? I'm particularly interested in possible circular class injection pathways (i.e. if the rawContent class also had an [Inject] tag on a property of type InfoGraphicsDataModel.)

chrisathook commented 12 years ago

No, it is just a loaded swf. It's document root as dependencies marked with

[inject]

That need to be fulfilled that’s why I am injecting into it. I am creating a child injector because I am loading several different swfs that need different values injected. So in the for loop I create a child injector for each one, map the 1 dependency that is unique per loop then inject it into the loaded swf.

-----Original Message----- From: ZackPierce [mailto:reply@reply.github.com] Sent: Sunday, June 19, 2011 5:57 PM To: chris@byhook.com Subject: Re: [SwiftSuspenders] Child Injector Mapping not stored? (#54)

In this example, what class is the InfoGraphicsDataModel.rawContent property? Is that class mapped to anything in the injector?

Reply to this email directly or view it on GitHub: https://github.com/tschneidereit/SwiftSuspenders/issues/54#issuecomment-1399470

ZackPierce commented 12 years ago

Thanks for the clarification.

ZackPierce commented 12 years ago

Alright, I think I'm still not comprehending the full class layout here. In particular, at what point in the object lifecycle is the rawContent property being set. Let's try to winnow it down to the simplest set of classes and calls that will break.

Also, please pardon my pedantry, but I want to point out that injectInto fullfills all the injection requirements of the object passed. I'm worried that you may be using injectInto as if it were meant to accomplish property-assignment:

// *not* what happens after injectInto(vo.rawContent)
vo.rawContent = someObjectFromTheInjector;

when in fact, the SwiftSuspenders code is trying to do:

 // roughly what happens when injectInto(vo.rawContent) is called
vo.rawContent.propertyA = somethingElseFromTheInjector;
vo.rawContent.propertyB = yetAnotherOtherThing;

With that clear, how does this sound for a class hierarchy and corresponding test?
Frame ~= InfoGraphicsDataModel Door ~= the class of InfoGraphicsDataModel.rawContent Knob ~= some sub-property of InfoGraphicsDataModel.rawContent that needs to be injected

class Frame {
    public var door:Door = new Door()
}

class Door {
    [Inject]
    public var knob:Knob;
}

class Knob {
}

// In the appropriate unit test class
[Test]
public function childInjectorCorrectlyHandlesSubPropertyInstantiation():void {
    var injector:Injector = new Injector();
    injector.mapClass(Knob, Knob);

    var childInjector:Injector = injector.createChildInjector(injector.getApplicationDomain());
    var frame:Frame = new Frame();

    childInjector.mapValue(Frame, frame);
    childInjector.injectInto(frame.door);
    Assert.assertNotNull(frame.door.knob);
}

Since that above unit test passes in the current SwiftSuspenders codebase, either I have missed some aspect of the class relationships described, or there is some other misunderstanding (or bug) going on. If it's the former, please suggest modifications to the above minimal test and class-set.

chrisathook commented 12 years ago

Zack,

I am using injectInto to fulfill the injection dependencies in the instance of rawContent passed. The swf loaded and referenced as rawContent is not mapped in the injector, it just has dependencies that need to be fulfilled. I did this so I could allow the developers of all the modules access to parts of the framework I am building automatically.

So rawContent is being set after it is loaded by the loader. So when I encounter the bug rawContent is already loaded and the constructor would have been called since its a loaded swf.

I am looking at your example below, I think your basic class structure is ok, but what I am doing with the injector is different.

I based this idea on the following lines from the documentation.

"The child injectors forward all injection requests they don’t have a mapping for to their parent injector. This enables sharing additional rules between the injectors"

"If a mapping from a parent (or other ancestor) injector is used, that doesn’t mean that the child injector isn’t used for subsequent injections anymore. I.e., you can have “holes” in your child injector’s mappings that get filled by an ancestor injector and still define other mappings in your child injector that you want to have applied later on in the object tree that is constructed through DI."

chrisathook commented 12 years ago

If I create the childInjector using the application domain of the loaded swf instead of the main application domain the error disappears and I don't have to make that second request to get the dependency to map correctly.

Replaced

var childInjector : IInjector = injector.createChild( injector.applicationDomain )

with

var childInjector : IInjector = injector.createChild( (DisplayObject (vo.rawContent).loaderInfo.applicationDomain ));

it must have something to do with the injecting into loaded swfs.