Subcontexts in a large, modular application
So here's the setup: I'm in the process of building a rather large application which must be completely dynamic. RobotLegs has been a huge help in keeping the app organized and testable, but I'm having trouble determining what the best practice is for using sub-contexts in an application, keeping in mind that those sub-contexts may share some mappings with the original super-context.
Example: The main context of the application uses mapSingletonOf to control, say, the content model to one instance. When I create a sub-context within this app, I could use mapSingletonOf again to fill any dependencies within that context, but they would not be filled with the same instance of the content model. Or think of this in terms of a queue-loader service. There should ideally be one queue loader instance to manage loading items within the app, but every context would receive its own instance, thereby making the queue loader useless.
I've been fighting with the best way to tackle this issue. I can solve it by passing in the injector, reflector, and so on into a custom sub-context class and assigning them before the default SwiftSuspendersInjector et al are created, but this technique seems far too clunky, and actually also demolishes the testability I've grown so fond of with this framework.
I know in the best practices documentation you've mentioned that it is possible to build modular applications with multiple contexts, but the document is clearly concerned with a simpler sort of application. Hopefully I'm missing an easier, cleaner way to make data or services available safely in an application with multiple contexts.
Has anyone else run into this problem? How did you end up solving it?
- singleton-src.zip 12 KB
Comments are currently closed for this discussion. You can start a new one.
Keyboard shortcuts
Generic
? | Show this help |
---|---|
ESC | Blurs the current field |
Comment Form
r | Focus the comment reply box |
---|---|
^ + ↩ | Submit the comment |
You can use Command ⌘
instead of Control ^
on Mac
Support Staff 1 Posted by Till Schneidere... on 10 Dec, 2009 07:06 PM
Hey Jeremy,
if you're willing to use a beta version of SwiftSuspenders, you might
be interested in the newly added support for child injectors. These
can contain their own mappings, but in addition, they automatically
delegate all unknown injection requests to their parent injector,
enabling you to share mappings for all contexts while still
configuring your contexts with their individual mappings.
As this feature of SwiftSuspenders is pretty new, there's no currently
no real documentation, but for some usage examples, you can see the
unit tests here:
http://github.com/tschneidereit/SwiftSuspenders/blob/childinjectors/test/org/swiftsuspenders/ChildInjectorTests.as
To use the new version of SwiftSuspenders, you have to clone the
"childinjectors" branch from github and compile from source. I'll try
to put up a beta release of the swc in the coming days, so using that
should become much simpler.
cheers,
till
2 Posted by levi.strope on 10 Dec, 2009 07:53 PM
Hello, what kinda timeframe can we expect this kind of feature to roll out of beta?
I would love to experiment with this feature and provide feedback, but I would need proper documentation that I can distribute to my team and also reference myself when they ask questions.
Thanks and regards,
Levi
Support Staff 3 Posted by Till Schneidere... on 10 Dec, 2009 09:59 PM
hey levi,
Honestly, I,m mostly waiting for some feedback before rolling it out
for real. but obviously, I should write some documentation for that to
happen.
I do that tomorrow and think it should be up pretty quickly. the
feature is quite complex conceptually, but it has a really minimal
interface.
4 Posted by Jeremy Ruppel on 10 Dec, 2009 10:37 PM
Thanks for the quick response! Looks like this will be very powerful indeed, exactly the idea I was looking for. I'm having a tough time conceptualizing how it will be integrated smoothly into RobotLegs though, especially since the Injector.createChildInjector will not implement IInjector or be adapted. In fact, I'm still trying to come up with an acceptable implementation of this within RobotLegs' Contexts that doesn't couple the two libraries together any more than they need to be. Have you had any luck talking this great new feature over with Shaun?
Thanks again,
Jeremy
Support Staff 5 Posted by Till Schneidere... on 10 Dec, 2009 10:49 PM
I'm not at my desk anymore so I can't give you an example right now,
but child injectors do implement IInjector and using them works
without any changes to robotlegs.
will write documentation and an example tomorrow.
Support Staff 6 Posted by Till Schneidere... on 11 Dec, 2009 04:32 PM
I've added some documentation and example for using child injectors to
the SwiftSuspenders documentation:
http://github.com/tschneidereit/SwiftSuspenders/blob/master/README.textile
Also, I just added a build of the first 1.5 beta to the github downloads:
http://github.com/tschneidereit/SwiftSuspenders/downloads
If you download this build and drop the contained swc into your
projects' libs folder, you should be able to use the child injectors
feature with the current Robotlegs release.
Looking back at what I wrote yesterday, I have to admit that what I
wrote about the child injectors implementing IInjector was simply
false, sorry about that! Alcohol and playing Poker just don't go well
with technical discussions ;)
Still, the actual behavior is no problem in practice: Just inject the
injector itself typed as Injector, not IInjector into your
configuration command and use it directly:
//in your context:
injector.mapValue(Injector, injector);
//in your startup command:
[Inject] public var injector : Injector;
Now to your use-case:
for each subcontext, supply the main (or parent, if you're going to
have multiple levels of nesting) injector in the constructor. Then
create the child injector for the current context before calling
super, and you're done:
class ChildContext extends Context
{
public function ChildContext(contextView : DisplayObjectContainer,
parentInjector : Injector)
{
injector = parentInjector.createChildInjector();
super();
}
}
Now, all mappings in the parent injector are still available in the
current context, but you can add additional mappings that are specific
to this context and don't affect the parent or any other contexts.
I'll create an example of this over the next few days, but I hope this
helps you get started right now.
cheers,
till
Support Staff 7 Posted by Ondina D.F. on 11 Dec, 2009 05:00 PM
Great!!!
Actually the documentation is here (under childinjectors):
http://github.com/tschneidereit/SwiftSuspenders/tree/childinjectors
8 Posted by Jeremy Ruppel on 11 Dec, 2009 05:33 PM
Thanks again for the quick response, Till! Still, the same problem exists where the ContextBase's definition of injector is limited to IInjector, so the line...
injector = parentInjector.createChildInjector( );
...would still run into conflicts. I suspect that the changes necessary to make this feature available within sub-contexts will have to happen mostly in the RobotLegs side of things, namely:
I think for the time being I may write a quick adapter to help with assigning the child injector to the sub-context, but what are your thoughts on the above?
Thanks again for the help on this issue, really appreciate it.
Jeremy
Support Staff 9 Posted by Till Schneidere... on 11 Dec, 2009 05:49 PM
@Ondina: Thanks for correcting that mistake!
@Jeremy: Serves me right for writing examples in emails instead of
testable projects - sorry for that!
You're completely right about the need to implement IInjector. I think
I have a workable approach in mind, I'll work on that over the
weekend. As a quick outline, exposing Injector#setParentInjector as a
public method should enable using the injector created by the Context,
without changing anything in Robotlegs.
cheers,
till
Support Staff 10 Posted by Shaun Smith on 11 Dec, 2009 06:12 PM
@Jeremy: We have to be careful with what we put into RL's IInjector interface - we need to support as many DI solutions as possible. The idea is that IInjector represents what RL needs access to in order to do it's job, but the app developer is free to work directly with the underlying DI solution (in this case SwiftSuspenders).
Support Staff 11 Posted by Shaun Smith on 11 Dec, 2009 06:16 PM
@Levi: it's a catch-22! Till needs someone to test out the Child Injector stuff before he can roll it out (and before we roll that into Robotlegs).
@Till: Sorry that I haven't had a chance to play with that stuff yet - and it's looking like I won't have any time to do so for quite a while :'<
Support Staff 12 Posted by Till Schneidere... on 11 Dec, 2009 06:46 PM
@Shaun: I'm more and more of the opinion that Robotlegs should only
support the most basic functionality of whatever DI solution is used.
Everything else should be used by adding a mapping to the concrete
injector implementation in the context and a matching injection in a
startup command. Adding IInjector#mapRule was a regrettable mistake on
my part.
No worries about not being able to play with child injectors - I get
that you have a day job ;)
@everyone: Shaun is completely right: I need feedback from real
world-usage before I can release a new version of SwiftSuspenders that
I can call "stable" with any confidence. I hope that releasing a
proper build of the beta version makes this easy enough.
13 Posted by levi.strope on 11 Dec, 2009 07:20 PM
I understand the catch 22.
I do have a need for this in our project, I'll see if I can't start implementing it.
Till Schneidereit closed this discussion on 14 Jan, 2011 09:29 AM.