RL2 Modularity

234 views
Skip to first unread message

Alessandro Bianco

unread,
Mar 28, 2012, 11:47:09 AM3/28/12
to Robotlegs AS3
Hi robowalkers :)

I'm experimenting a bit with RL2 and I found something weird when
trying the modularity extensions.

If I create 2 Contexts and use two sibling DisplyObjects as context
views, those contexts are unable to talk between each other through
the shared EventDispatcher ...

I can see why this is happening from the code, but is it the intended?
Or it was supposed that EVERY context should communicate despite where
their contextView is attached?

thanks :)

ps: I think I'll nag the list a bit in the next days, cause I'm trying
to figure out a couple of things to make RL2 work easily with Starling
and Away3D so, yeah, sorry in advance ^_^

Shaun Smith

unread,
Mar 28, 2012, 12:03:46 PM3/28/12
to robo...@googlegroups.com
Hey Alessandro,

Indeed, each shared event dispatcher is only intended to be "shared" inside a given context. Having a truly "global" event dispatcher would probably not be such a great approach :)

You can use the ScopedEventDispatcherExtension to share additional event dispatchers between contexts. However, those sibling contexts will need a parent context with the ScopedEventDispatcherExtension installed if you want them to share dispatchers.

Let us know how it goes.

> --
> You received this message because you are subscribed to the Google
> Groups "Robotlegs" group.
> To post to this group, send email to robo...@googlegroups.com
> To unsubscribe from this group, send email to
> robotlegs+...@googlegroups.com
> for support visit http://knowledge.robotlegs.org

Alessandro Bianco

unread,
Mar 28, 2012, 12:17:17 PM3/28/12
to Robotlegs AS3
quick as always, awesome :)

so something like this:

_one = addChild(new Sprite());
_two = addChild(new Sprite());
new
Context().extend(MVCSBundle,ScopedEventDispatcherExtension).configure(UIConfig,
_one);
new
Context().extend(MVCSBundle,ScopedEventDispatcherExtension).configure(OtherConfig,
_two);

and then using:
[Inject(name="global")]
public var globalDispatcher:IEventDispatcher;

to talk between contexts, it's supposed not to work :(

that's kind of a show stopper, because it prevents starling-flavored
contexts to talk to each other AND with regular contexts, cause they
have nothing to share with the regular display list.

you think it would be possible (with some other extension) to fill the
gap somehow?

Shaun Smith

unread,
Mar 28, 2012, 12:47:05 PM3/28/12
to robo...@googlegroups.com
Good point. Ok, so the ScopedEventDispatcherExtension actually has nothing to do with the display list. It's the ModularityExtension that relies on the display list to connect the contexts together (simply by setting parent injectors).

The ScopedEventDispatcherExtension works by asking if the injector "satisfies" (directly or indirectly) the given dependency on a named event dispatcher. If that injector has been wired to a parent injector, then the mapping won't be made (as the mapping will already exist in the parent injector), and future requests for that mapping will be served by the parent injector:


So, we just need a way to wire the context injectors up to the same parent injector without using the ModularityExtension. Something like this:

const parentInjector:Injector = new Injector();
parentInjector.map(IEventDispatcher, 'global').toValue(new EventDispatcher());

_one = new Sprite();
_two = new Sprite();

_contextOne = new Context().extend(MVCSBundle, ScopedEventDispatcherExtension).configure(UIConfig, _one);
_contextTwo = new Context().extend(MVCSBundle, ScopedEventDispatcherExtension).configure(OtherConfig, _two);

_contextOne.injector.parentInjector = parentInjector;
_contextTow.injector.parentInjector = parentInjector;

addChild(_one);
addChild(_two);

Notice that I only add the display objects to stage *after* setting the parentInjectors. Without this, the contexts would probably initialize before setting the parentInjector and the ScopedEventDispatcherExtension would not work properly.

Now, the above is all a bit messy, and perhaps could be wrapped up in an extension. Anyhow, give it a shot and let me know how it goes.

Maybe the ModularityExtension needs to be renamed StageModularityExtension to indicate that it relies on the Display List?

Alessandro Bianco

unread,
Mar 28, 2012, 3:05:35 PM3/28/12
to Robotlegs AS3
looks like a solution, will try tomorrow :)

there's one thing i didn't quite understood:

you say that the ScopedEventDispatcherExtension has nothing to do with
the ModularityExtension, but it's that extension that listens for
contexts that are added and registers their parentInjector, right? so
shouldn't it also be responsible of registering "super" injectors if
necessary?


On Mar 28, 6:47 pm, Shaun Smith <dars...@gmail.com> wrote:
> Good point. Ok, so the ScopedEventDispatcherExtension actually has nothing to do with the display list. It's the ModularityExtension that relies on the display list to connect the contexts together (simply by setting parent injectors).
>
> The ScopedEventDispatcherExtension works by asking if the injector "satisfies" (directly or indirectly) the given dependency on a named event dispatcher. If that injector has been wired to a parent injector, then the mapping won't be made (as the mapping will already exist in the parent injector), and future requests for that mapping will be served by the parent injector:
>
> https://github.com/robotlegs/robotlegs-framework/blob/version2/src/ro...

Shaun Smith

unread,
Mar 28, 2012, 4:08:48 PM3/28/12
to robo...@googlegroups.com
Cool. The ModularityExtension is the one that is responsible for wiring context injectors together (by observing and dispatching bubbling events along the display list). The ScopedEventDispatcherExtension simply maps event dispatchers into a context if such mappings are not already satisfied by a parent injector.

So, the ScopedEventDispatcherExtension does not rely on the Modularity extension or on the display list. It only looks to see if it should map local event dispatchers into an injector, or wether it can let those dispatchers be provided by a parent injector. In a loose sense it sort-of relies on the Modularity extension as it requires that *something* has wired the injectors together. But that wiring can be done manually (as we did earlier) if there is no display list to bubble events along.

Alessandro Bianco

unread,
Mar 28, 2012, 4:43:35 PM3/28/12
to robo...@googlegroups.com

Then I don't see the reason why the ModularityExtension is included in the basic bundle and the ScopedEventDispatcherExtension isn't.. but probably I'm just missing something, and the late hour doesn't help :)

Alessandro Bianco

unread,
Mar 29, 2012, 2:41:21 AM3/29/12
to Robotlegs AS3
i actually was the late hour, it's pretty clear now in the morning :)

even without the ability to communicate cross-context, the injected
stuff is still shared between them

ok, all clear. i'll go try the manual parenting and try to find a way
to do in from an extension :)

On Mar 28, 10:43 pm, Alessandro Bianco <i...@alessandrobianco.eu>
wrote:
> Then I don't see the reason why the ModularityExtension is included in the
> basic bundle and the ScopedEventDispatcherExtension isn't.. but probably
> I'm just missing something, and the late hour doesn't help :)
> Il giorno 28/mar/2012 18:47, "Shaun Smith" <dars...@gmail.com> ha scritto:
>
>
>
>
>
>
>
> > Good point. Ok, so the ScopedEventDispatcherExtension actually has nothing
> > to do with the display list. It's the ModularityExtension that relies on
> > the display list to connect the contexts together (simply by setting parent
> > injectors).
>
> > The ScopedEventDispatcherExtension works by asking if the injector
> > "satisfies" (directly or indirectly) the given dependency on a named event
> > dispatcher. If that injector has been wired to a parent injector, then the
> > mapping won't be made (as the mapping will already exist in the parent
> > injector), and future requests for that mapping will be served by the
> > parent injector:
>
> >https://github.com/robotlegs/robotlegs-framework/blob/version2/src/ro...
> > Context().extend(MVCSBundle,ScopedEventDispatcherExtension).configure(UICon fig,
> > _one);
> > new
>
> > Context().extend(MVCSBundle,ScopedEventDispatcherExtension).configure(Other Config,

Shaun Smith

unread,
Mar 29, 2012, 6:30:26 AM3/29/12
to robo...@googlegroups.com
Yup. But now that I'm thinking about it, perhaps "global" is a bad default name for the shared dispatchers - the dispatchers won't actually be global, but rather hierarchically shared. Maybe "shared" would be a better default.

Ondina

unread,
Mar 31, 2012, 2:26:15 PM3/31/12
to robo...@googlegroups.com

Hey Shaun and Alessandro,

I agree that “global” isn’t a good name, but “shared” isn’t much better either, because a dispatcher can be shared within one Context, between all Contexts and between some Contexts. So, “shared” doesn’t say anything about the scope of the dispatcher.

After playing around with words I made a list of synonyms and a little diagram.  It’s not easy to find good names for those dispatchers! So, you can either choose one from my list, or use it as an inspiration for further brain-storming.

1. within a Context (Shell or Module):

 intra, local, restricted, internal, endo(gen), endemic,  self, private, intrinsic

2. between all Contexts (Shell and Modules)

pandemic,  omni(present), overall, public, extended, extensive, widespread, pervasive, spread, disseminated, scattered, ubiquitous, universal, general, cosmopolite, umbrella, overarching

3. between some contexts (ModuleA-ModuleB, Shell-ModuleA..)

scoped, joint,  joined, targeted, F2F(Friend To Friend), limited

Then I narrowed it down to:

1. intraDispatcher, localDispatcher

2. panDispatcher, omniDispatcher

3. scopedDispatcher, jointDispatcher

or:

1. intraModularDispatcher

2. panModularDispatcher

3. scopedDispatcher

For example, in Module-A’s SomeMediator:

1. shared within Module-A’s Context

public var intraModularDispatcher:IEventDispatcher;

2. ScopedEventDispatcherExtension’s default dispatcher(“panModular”  instead of “global”)

//context.extend(MVCSBundle, ScopedEventDispatcherExtension);

[Inject(name="panModular")]

public var panModularDispatcher:IEventDispatcher;

3. between Module-A and Module-B:

// context.extend(MVCSBundle, new ScopedEventDispatcherExtension("scopedAB ");

[Inject(name="scopedAB")]

public var scopedABDispatcher:IEventDispatcher;

My two cents ;)

Ondina

Avi Kessner

unread,
Apr 1, 2012, 3:07:36 AM4/1/12
to robo...@googlegroups.com
I like Odina's direction.
However, I think pan/omni and scoped have the same problems that Shared or Global have.  How about some prefixes which define the relationship between the various dispatcher options, rather than a description of what they do?

i.e, SuperDispatcher, Dispatcher, and SubDispatcher?

Or perhaps just HyperDispatcher and Dispatcher (Is there really a significant difference between all three types of dispatchers?)
Two other possible  options  I found which might help inspire some ideas are CoDispatchers and  MegaDispatcher.

I was thinking in terms of small/medium/large to define what type of Dispatchers they are.
brought to you by the letters A, V, and I
and the number 47


--

Ondina

unread,
Apr 1, 2012, 4:28:26 AM4/1/12
to robo...@googlegroups.com

Hi Avi,

You’re right, pan and omni aren’t ideal either! There are 2 aspects to it: the hierarchy and the accessibility. It’s hard to find a name describing both aspects clearly. I like your idea of hierarchy, but “super” and “sub” wouldn’t be of much help in a big tree  either.

Just because 4 Modules are siblings, sharing the same parent, it doesn’t mean they automatically share the same dispatcher, but indeed it has to originate in the parent.

So, from Shaun’s example, Module-A and Module-B could share Bus1, where Module-C and Module-D would share Bus2.  So Bus1 and Bus2 would be both “SuperDispatcher”, right? But if Module-B would have a child Module-D, and a shared Bus3, what would it be, super or sub dispatcher?

Maybe something like rootDispatcher, parentDispatcher,  siblingDispatcher..?  Not easy, really :)

Ondina

Stray

unread,
Apr 1, 2012, 4:41:23 AM4/1/12
to robo...@googlegroups.com
Hi all,

nothing suggested so far has made me feel anything other than confused... so - yes, "Not easy, really" +1 !

So I think what you've uncovered is that there isn't a good-enough conventional naming system for specific-parts-of-a-tree.

But I think this has saved us from a mistake - because the developer can now choose names that represent purpose and not implementation-details.

There will be a reason why modules A and B share a bus that modules C and D don't have access to - that reason should be the source of the naming. 

So, yes, Bus2 would suck, but UserServicesMessageBus would be good I think. In the end, all the developer needs to decide is "which bus do I need / am I listening to".

After all, the bus wiring may change over time, but the purpose of the messages is still going to apply (unless the class itself changes). And in one application this module may have a sibling-bus that carries these messages, and in another it may have only a local one. It shouldn't matter who else is listening and actually revealing those details in the naming is like the implementation details leaking into many places in your code that really don't need to know.

Choosing the names would also help with designing the granularity / wiring of the message buses you're using.

Just my ponderings...

Stray

Ondina

unread,
Apr 1, 2012, 5:25:30 AM4/1/12
to robo...@googlegroups.com

Hi Stray,

Good points!

>But I think this has saved us from a mistake - because the developer can now choose names >that represent purpose and not implementation-details.

>There will be a reason why modules A and B share a bus that modules C and D don't have >access to - that reason should be the source of the naming. 

So, if Module-D would be a Service, then Module-A, B and C would share UserServicesMessageBus with Module-D in order to communicate with it. If Module-E were a logger, then we could have a LoggerBus shared by A,B,C,D and E.

The purposes are clear.

The purpose of what I called a localDispatcher or intraDispatcher is also clear.

And, indeed, it’s up to you as a developer how you name the dispatchers.

But what is the purpose of a „global“ dispatcher, or however we would name it?

Maybe there shouldn’t be a default dispatcher after all…

Ondina

Alessandro Bianco

unread,
Apr 1, 2012, 6:23:26 AM4/1/12
to Robotlegs AS3
why not use something like the classic function namespaces

private - just for the current module
protected - current module and descendants
public - globally accessible (event between siblings and others)

and then leaving to the developer freedom to name other dispatchers as
he like, like it is right now

Ondina

unread,
Apr 1, 2012, 6:40:03 AM4/1/12
to robo...@googlegroups.com

I had public and private on my lists too, so I like your idea!

So the default dispatcher would be ‘publicDispatcher’ or ‘publicBus’,

‘privateDispatcher’/’privateBus’ for the current module,

and maybe instead of ‘protected’  something like what Stray has suggested, denoting the purpose?

Ondina

Ondina

unread,
Apr 1, 2012, 6:46:26 AM4/1/12
to robo...@googlegroups.com

Or

1. centralBus

2. localBus

3. UserServicesMessageBus, OtherPurposeBus etc

Avi Kessner

unread,
Apr 1, 2012, 7:19:08 AM4/1/12
to robo...@googlegroups.com
Problem with Public,private,protected is that you are not talking about the relationship between classes, but a subset of classes, and it will become very confusing to developers who are not aware of these use cases, which caused the name in the first place.

I thought the problem with global is that it's not truly global, doesn't "center" have the same problem?

brought to you by the letters A, V, and I
and the number 47


--

Ondina

unread,
Apr 1, 2012, 7:53:40 AM4/1/12
to robo...@googlegroups.com

 >I thought the problem with global is that it's not truly global, doesn't "center" have the same problem?

True, it does…

But let us think how we would describe the default dispatcher in a sentence. It is provided by a parent context with the ScopedEventDispatcherExtension installed and it is inherited by its children? What else?  What is its purpose?

Would ‘default’ or ‘defaultBus’ or ‘defaultDispatcher’ be better?  

[Inject(name="defaultBus")]

public var whateverNameYouChoose:IEventDispatcher;

Actually, not much better, right?

Ondina

unread,
Apr 1, 2012, 8:38:39 AM4/1/12
to robo...@googlegroups.com

Last ones for today:

[Inject (name="preset")]

Or

IYWYCHIBD = If You Wanted You Can Have It By Default ;-)

[Inject (name="IYWYCHIBD ")]

Cheers

Shaun Smith

unread,
Apr 6, 2012, 11:20:05 AM4/6/12
to robo...@googlegroups.com
Hah! Yeh, this thread kinda makes me feel that any default name is going to be a poor choice. Not there weren't some great suggestions, just that the default behaviour requires understanding how both the modularity and scoped dispatcher extensions work, and no single word will capture that. Perhaps forcing users to supply their own, use-case specific names would be best.

Joni Bekenstein

unread,
May 9, 2012, 3:03:41 PM5/9/12
to robo...@googlegroups.com
Hey, I'm doing a little demo with RL2 and Modularity (pure Actionscript).

Regardless of the naming issues that were discussed in this thread, I'm wondering if I'm on the right path:

I currently have a shared event dispatcher between my modules which is mapped in the injector with the name "global". So far so good, I should be able to send and receive events between different modules.

Now in my Shell module (which is the display list parent of all other modules) I want to map some commands to an EventCommandMap which would listen on the global event dispatcher. For this, I created a ScopedEventCommandMapExtension that creates and maps in the injector named IEventCommandMap objects that use an IEventDispatcher retrieved from the injector using the same injection name.

So... An IEventCommandMap with name "global" requires that an IEventDispatcher with name "global" be mapped in the injector.

What do you think of this approach?

Anyways, I must be doing something wrong since this setup isn't working for me. ScopedEventDispatcherExtension (and ScopedEventCommandMapExtension also) registers a handler for context.lifecycle.whenInitializing. But this handler gets called AFTER my ContextConfig is executed (it's a plain class with [PostConstruct]). So, by the time ContextConfig is called, I need to have the ScopedEventCommandMapExtension installed, but it isn't, so the Injector complains it can't find the required injections.

What could be wrong? This is how I'm creating my context:
_context = new Context()
                    .extend(MVCSBundle, ScopedEventDispatcherExtension, ScopedEventCommandMapExtension)
                    .configure(ContextConfig, this);

Thanks!

Shaun Smith

unread,
May 10, 2012, 8:00:57 AM5/10/12
to robo...@googlegroups.com
Hi Joni,

Sounds like a good approach. I would make sure that your ScopedEventCommandMap extension uses the same mechanism as the ScopedEventDispatcherExtension for offering the user the ability to override the default injection name via the constructor (with the default set to "global" for now).

As for the timing stuff: I need to have a think about that. It feels as if perhaps user configs should actually be instantiated "after" initialization and not during (as they currently are). This would solve your issue - but it has some consequences that will need to be explored.

Joni Bekenstein

unread,
May 10, 2012, 9:13:02 AM5/10/12
to robo...@googlegroups.com
Yes, the ScopedEventCommandMap has the same features as the ScopedEventDispatcher. 

About the timing stuff, I could only make it work if my config added a handler to context.lifecycle.whenInitializing. But at least in my scenario, I thought this was the logical flow (install extensions, THEN run configurations). If I may ask, what's the consequence you are talking about?

Joni Bekenstein

unread,
May 10, 2012, 9:16:24 AM5/10/12
to Joni Bekenstein, robo...@googlegroups.com
I meant to say: context.lifecycle.afterInitializing

Shaun Smith

unread,
May 10, 2012, 9:40:49 AM5/10/12
to robo...@googlegroups.com, Joni Bekenstein
Yeh, that's the work-around I'd recommend for now.

Regarding consequences: it depends on whether or not extensions depend on configs. They shouldn't really, but there is at least once instance where that happens at the moment. It's also a conceptual problem: is configuration not part of initialization? As soon as a context reports that it is initialized I would expect that all configuration has completed, and the context is configured and ready for action.

So, either the config manager is the first thing to run "after" initialization. Or, the config manager is the last thing to run during initialization ("when"). I'm leaning towards the second option.

Shaun Smith

unread,
May 14, 2012, 10:49:58 AM5/14/12
to robo...@googlegroups.com, Joni Bekenstein
Hi Joni,

I've changed things so that configs are run at the end of the "initialize" phase, and have opened a ticket to discuss any issues surrounding that:


Could you grab the latest source and ensure that your work-around is no longer needed. Thanks,

Joni Bekenstein

unread,
May 14, 2012, 10:55:30 AM5/14/12
to Shaun Smith, robo...@googlegroups.com
Sure! I'll give it a go tomorrow and let you know. Thanks!!

Joni Bekenstein

unread,
May 15, 2012, 10:00:38 AM5/15/12
to Joni Bekenstein, Shaun Smith, robo...@googlegroups.com
Works like a charm!
Reply all
Reply to author
Forward
0 new messages