Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: [1]
Print
Author Topic: Module Groups  (Read 5695 times)
Jason MacDonald
Sr. Member
****
Posts: 243


View Profile Email
« on: December 08, 2008, 08:34:23 »

Hey Darshan,

I was wondering if you had any feedback on module groups. I find myself needing to send routing messages to a group of modules (group A) while having another group ignore the message (group B) where both group A and Group B have listeners for the same message type. I'm wondering what the best approach here might be.

To try and give an example, I have 2 views: View one is made up of 3 modules that need to respond to notifications from other modules within their group [view]; View 2 also has 3 modules that need to perform similar tasks as View 1.  View 1 & 2 are made up of the same modules, loaded with different instance names (module0 vs module1), that perform the same tasks but need to respond only to their internal groups notifications. While a solution for only two views might be to use some naming convention but the problem I face is I may have any number of views using the same modules just new instances, so using a name based approach becomes difficult.

My first thought was to add another parameter to routeNotification called group, where the loading view module could assign a group name to all child modules allowing a new discriminator in my listeners to check the group name against it's own. But obviously this would require changing Fabrication making future upgrades a pain. I can't just use the "type" field as I have many situations where that field is already providing a required piece of data for something.

Originally I thought this might have been what the firewall was for, but it seems (from my limited knowledge) that the firewall is based on stopping the message from ever going out, rather than filtering who the message should go out to.

Just wondering if I'm overlooking something already in place in Fabrication or if I need to bake up some homegrown solution?
« Last Edit: December 08, 2008, 08:36:59 by jasonmac » Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #1 on: December 09, 2008, 06:55:43 »

Hey Jason,

There isn't any easy way to do this within the framework currently. The firewall approach could work in a roundabout manner. Currently I am using the MultiRuleFirewall which is set to drop accidental system notifications. Instead you could use the PolicyFirewall with a custom policy function. The usage to install the firewall is,

:
var firewall:PolicyFirewall = new PolicyFirewall();
firewall.policyFunction = myPolicyFunction;

applicationRouter.install(firewall);
.

In the policy function you could alter the message to go to a fully qualified module address. Returning null drops the message. Something like, change to A/* to A/A0 if the message payload contained xyz or was within a certain container sprite etc.

This is all far from ideal though. I think your solution of using module groups is much better and in general would be a good feature to have within the framework. The changes are probably minor but spread across a lot of classes. I will explore the things required to implement this a little bit further and get back soon.

peace,
darshan
Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #2 on: December 10, 2008, 07:29:52 »

Hey Jason,

I looked at the grouping implementation today. I see no need to add another group argument to routeNotification. Current destination types are *, A/*, A/A0. So we can support group names directly with the condition that they should not have a / in their name. The current routeNotification's to argument would suffice. I need to modify the internals of routed notification to understand moduleGroups to avoid extra suffixes and such. The public api to work with groups would be something like,

:
// set the moduleGroup on module from outside
fabrication.moduleGroup = "myGroup";

// set the default route to point to this group
fabrication.defaultRoute = "myGroup";


You set the moduleGroup at the same place that you set the router on the fabrication depending on your platform and what you use to load the module. At the time of routing if to=null and a defaultRoute is set then the notification goes to the group name so any existing routeNotification would get sandboxed within that group.

This looks like a very useful addition. I will try to finish up the implementation by tomorrow.

peace,
darshan


Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #3 on: December 10, 2008, 08:14:15 »

Hey Jason,

Can you post some of the routes you are using in your application? I want to make sure I have got everything covered.

peace,
darshan
Logged
Jason MacDonald
Sr. Member
****
Posts: 243


View Profile Email
« Reply #4 on: December 10, 2008, 10:57:54 »

One problem I see with always having the group as the default route is that currently all my modules have their default route set to my Data Module (this handles all communication with my back-end). It worked best this way because all of my modules communicate with the DataModule so I can have direct 1-to-1 data communication without having to pass around the name of the data module instance to each and every loaded module. In the future I plan to have multiple data module sources, my module loader would assign the appropriate data module instance as the default route for each loaded module depending on it's configuration. It sounds confusing but it makes sense when you understand the app as a whole.

Most of my problems derive from the fact that my entire app is assembled at run-time based on a very large XML file - I've basically recreated Flex inside Flash and my XML file is like one big MXML file that lays out components and styles them. Because of this, I cannot hard code any kind of instance names because future modules may need to respond to generic output notifications from other modules.

To give an example, if View1 loads Module1, Module2 and Module3, I need to sandbox SOME of the communications to view1 descendants, but not all. View1 and it's modules still need to notify the Shell (parent of the views) of major state changes like changing the view from View1 to View2. But if VIew1 contains a ThumbnailList module and a VideoPlayer module, the VideoPlayer knows it needs to react to notifications of ShowVideo (sent from the thumbnail list but maybe other modules too, it's a generic notification). So if View1 is showing a video, but view2 (hidden) also contains the video player, both react to the ShowVideo notification. Right now I have to do some finagling to check the players visibility and decide whether to react or not to the ShowVideo routed notification. This is just one example.

All of this works right now, I just can't cleanly sandbox communications within a view.

Most of my routes right now are to:* since using instance names is not easy with everything being dynamic. Unless I know for sure there's only one instance of a module (like my Styles Module) and that's it's always being loaded regardless of the XML. When communicating with my Data module I leave the to field blank for my default route.

I use the type field a lot to tell receiving modules that I want responses sent to only the original sender. I do this by putting applicationAddress.getInputName() in the type field so I can check the type field on the other end to see who I should send response back to. This is why I can't use type for a module group identifier.

In this case I need more ways to filter the message on the receiving end rather than on the senders end because the sender does not know what modules and views have been loaded. The group idea was just my way to allow modules to know which cases of a particular notification it should respond to. Most of the time I want all my modules within a view to respond to notes, ones they are interested in anyway, sent by other modules within their view. But there may be future cases where they shouldn't :(

« Last Edit: December 10, 2008, 11:13:19 by jasonmac » Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #5 on: December 10, 2008, 11:14:57 »

Hey Jason,

I am not completely sure I have covered all the use cases here. I will try to list them out, let me know if I missed any.

1. All modules have defaultRoute set to dataModuleAddress. This is set from outside the module by the loader. Hence routeNotification without to go to defaultRoute i.e.:- dataModule
:
// default route set to dataModule's application address
module.defaultRouteAddress = dataModule.applicationAddress;
routeNotification("toDataModule");

2. To send system-wide messages to the shell or any other module use the to argument with *. This goes to everyone listening to this notification.
:
routeNotification("toEveryone", "foo", "bar", "*");

3. To send messages to modules loaded within View1/View2 use module groups. When loading modules within view1 set moduleGroup to "view1". And use this moduleGroup as to when routing the notification.
:
// set the moduleGroup on the loaded module
module.moduleGroup = "view1"; // or something that indicates the view1 context.

// later route the notification within the view1 group context
routeNotification("toGroup", "foo", "bar", fabrication.moduleGroup);

4. This case is something you won't be using because of the dynamic nature of your application but makes sense to have in the framework. Suppose I need to send messages to all instance of Module1 that are loaded within View1. In this case, I can't use Module1/* because that would also go to all instances of Module1 that may be present in View2. So I am proposing another route, Module1/#. This translates to all instances of Module1 that are within the View1 group. The View1 group would be set on moduleGroup property.
:
module.moduleGroup = "view1";

// to send the notification to all instances of Module1 in view1
routeNotification("toModule1InView1", "foo", "bar", "Module1/#");

Does this cover all your use cases? Or am I missing something?

Regarding the use of the type parameter to send information about the message. You don't need to do this. The notification object that you ultimately get in the destination module is of type RouterNotification. This is subclass of Notification and contains the message that carried the notification. You can do note.getMessage().getFrom() to inspect the source of the message.

The use case regarding the view2 video player that should not respond to the notification until it is visible etc. This sounds like a good thing to implement with Interceptors. Interceptors are something I found in another open source framework called Parsley[1]. Parsley is a Spring like IOC container with MVC support for Flex.

Parsley application events are like PureMVC notifications. Within Parsley, Interceptors allow you to trap application events based on some criteria. I am going to be implementing Interceptors in Fabrication soon but for Notifications. The idea is to alter/drop/multiply notifications enroute before they reach commands and mediators.

In your case you could create a simple interceptor that drops messages if the module is not currently visible, or hasn't loaded enough data etc. This keeps your normal application flow from being cluttered with special conditions.

peace,
darshan

[1] : http://www.spicefactory.org/parsley/docs/current/manual/mvc.php#config_interceptors

Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #6 on: December 11, 2008, 06:14:18 »

I have committed the moduleGroup implementation to svn. The module group example i posted earlier should work now. Module group names must not have a / or # character in them. Also the A/# route and A/#groupName route is also working. Let me know if you find any bugs.

peace,
darshan
Logged
Jason MacDonald
Sr. Member
****
Posts: 243


View Profile Email
« Reply #7 on: December 11, 2008, 07:01:30 »

Great work! I appreciate all your effort.

Regarding the use of the type parameter to send information about the message. You don't need to do this. The notification object that you ultimately get in the destination module is of type RouterNotification. This is subclass of Notification and contains the message that carried the notification. You can do note.getMessage().getFrom() to inspect the source of the message.

Yup, I did see that I can get that from the message, but my problem was/is that the receiving module stills needs to know whether or not it should isolate the response to the original sender. By having it check the type field for a valid module address the responder knows to send it back to only that person. If I only used the note.getMessage().getFrom() it would always send the response to the sender and not be able to be selective about whether or not it should send to "from" or "*" or "defaultRoute".

Your list of other use cases pretty much sums up my need, and I like how case (3) puts the onus on Fabrication to check the ModuleGroup and send the message to the appropriate listeners rather than letting the receiving mediator decipher its involvement.

I'll have to take a peek at Parsley, it sounds like a useful concept [Interceptors] to have in my app as well.

Thanks again for all the help Darshan!
Logged
Pages: [1]
Print