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] 2
Print
Author Topic: Notification Chaining Utility  (Read 31497 times)
trilec
Sr. Member
****
Posts: 52



View Profile WWW Email
« on: April 28, 2008, 08:26:59 »

Hi All,
In an effort to simplify chaining of events within the pureMVC framework, I created a small utility based upon a mediator.
Its purpose is to accept a list of notifications in the order in which you would like them to be processed.
As the Chain Notifier is asynchronous it can be used for transitions or server-side communication, it merely responds to user defined notifications before moving to the next link in its chain keeping things simple and in one place.

In this example the Chain Notifier is within the execution body of a command.
but could be setup where ever you load your mediators.

ApplicationFacade
:
        override protected function initializeController( ) : void
        {
            super.initializeController();           
            registerCommand( STARTUP, StartUpCommand );
            registerCommand( STAGE_INITIALIZED, StageInitializedCommand );
        }

StageInitializedCommand
:
public class  StageInitializedCommand extends SimpleCommand
{
/**
* Register the Proxies.
*/
override public function execute( note:INotification ) :void
{
// Register the chainNotifier Mediator and establish chain linkages.
var chainNotifier:ChainNotifier = new ChainNotifier();
chainNotifier.AddNode( ApplicationFacade.SPLASH_SHOW, ApplicationFacade.SPLASH_INITIALIZED, note.getBody() ); //include note for startup
chainNotifier.AddNode( ApplicationFacade.PROGRESSBAR_SHOW, ApplicationFacade.PROGRESSBAR_INITIALIZED);
chainNotifier.AddNode( ApplicationFacade.LOGIN_SHOW, ApplicationFacade.LOGIN_INITIALIZED);
facade.registerMediator( chainNotifier ); //onRegister will start first notification
}
} ///class
In my example the note.Body was supplied so my splash screen would have a stage VO.
the SPLASH_INITIALIZED notification is what triggers the next link ie: PROGRESSBAR_SHOW and so on.
obviously the last one LOGIN_INITIALIZED does not have anthing to trigger and so is a little superfluous.. :-)

SplashCanvasMediator using chain notification
:
override public function handleNotification( note:INotification ):void
{
var stageVO:StageVO = note.getBody() as StageVO;
switch ( note.getName() )
{
case ApplicationFacade.STAGE_RESIZED:
canvas.setSize(stageVO.screenW, stageVO.screenH);
break;
case ApplicationFacade.SPLASH_SHOW:
//sendNotification will cause chain to next() using canvas as body for next chain link
canvas.init(stageVO.screenW, stageVO.screenH, styleProxy.style);
this.sendNotification( ApplicationFacade.SPLASH_INITIALIZED, canvas );
break;
}
}
as it integrates into the framework using notifications the ChainNotifier can be dropped in place with a minimal of effort and helps maintain a loosely coupled system.

Syntax Overview:
 
Main ChainNotifier Constructor
 the class is conceptually a asynchronous queuing system referred to as a chain
 transition between nodes (links) is controlled through the use of notifications inthe pureMVC framework
 
 You can choose to override the default generated action trigger notifications names if needed, but should not be needed
 Base action names are appended to the Mediator name to insure unique action names for any givin ChainNotifier
 in this example , ChainNotifier("MYCHAINNAME"); ... would yeld MYCHAINNAME_ACTION_START, MYCHAINNAME_ACTION_STOP .. etc
 
 public function ChainNotifier( qname:String,
          qstart:String="_ACTION_START",
          qstop:String="_ACTION_STOP",
          qcontinue:String="_ACTION_CONTINUE",
          qend:String="_ACTION_END",
          qnext:String="_ACTION_NEXT",
          qprev:String="_ACTION_PREV",
          qclear:String="_ACTION_CLEAR",
          qsetactive:String="_ACTION_SETACTIVE" ) :void
 
 eg: var chainNotifier:ChainNotifier = new ChainNotifier("MYCHAINNAME");
 
 @param qname - name of chain, If using defaults this name will be added to Action notification names
 @param qstart - Action start at the beginning of the queue and send first initial notification
 @param qstop - Action pause at current queue position, receiving of notifications except Actions, will be paused
 @param qcontinue - Action continue from pause position,activation of notifications will be enabled
 @param qend - Action skip to end node and send notification
 @param qnext - tAction node Move to nextAction Node and send notification , default behavior
 @param qprev - Action node Move back and repeat send notification
 @param qclear - Action will remove all nodes in array and free memory (no resart from this unless addNode/addAction is used
 @param qsetactive - experimental,not yet implemented with notifications however, can be triggered as method on the class
                     Active/Inactive will skip Inactive Nodes

* Notes: note.Body() and noteType() are sent to next link from prev notifyListener
*      Start will override all commands (pause, continue)
*       All Actions can be activated through the use of notifications and notification names overridden at constructor time
*      onRegister will start first notification, if you addNodes after this you will need to Start()

Function addNode
 This function provides the main method for sequentially adding notifications
 when its notifyListener notification is recived it moves to the next in the chain.
 This nex link then emits its notifySender notifications and waits for ITS notifyListener

 If you wish to use commands,  simply register your commands to take the chain links notifySender.
 when the command is called, at some point, either in the command or what the command is activating will
 need to send notification for the chain to be moved to next next point.

 eg. addNode( ApplicationFacade.SPLASH_SHOW, ApplicationFacade.SPLASH_INITIALIZED, note.getBody() ,note.getType());
     optional note.getBody() ,note.getType() can be set for the first Node to pass through, to the next node in the chain
     otherwise body and type are used from the received notifyListener and passed through to the next node in the chain
 
 @param notifySender  - the notification you wish to send when this link in the chain is triggered
 @param notifyListener - the notification this link will listen for to move to next link in chain
 @param body - an optional  notification body object to pass ( default is previous links notification body)
 @param type -  and optional notification type string to pass ( default is previous links notification type)
 @param active - to set a node inactive or active (testing whether this is useful) ( maybe placed before node function in a different release )

Function addAction
 This function provides a method to add Function callbacks triggered by notifications
 The Class uses this internaly  for adding the default Actions
 Although this is a little outside the box of its intended use it does provide a powerful
 triggering mechanism from within PureMVC the framework

 eg. addAction( "MY_ACTION_NOTIFICATION", myFunction);
     myFunction(node:ChainNotifierNode=null):void //function prototype for any addAction

 @param notifyListener - the notification this Action will listen for to call nFunction
 @param nFunction -  the function to call when triggered by notifyListener notifications

Flow Overview:
The constructor sets up Action notifications that can be used to control internal ChainNotifier behavior (if needed)
AddNode creates a node within the ChainNotifier supplying its name and start/next notification scheme.
OnRegister generates the first Action (if addNode includes a note body this is also sent with the notification)
dynamic listNotificationInterests register interest in the current index and Actions.
Upon receiving handleNotification either an Action is issued or the chain is moved to the next item and notification sent

In its basic operation it should not be necessary to use Actions , only if you want to get fancy or controll chain other next()
installation
Simply copy the code into your view area and replace package naming
The code was designed using multi-core but can be replaced easily to standard (remove multicore name)

Comments or suggestions are welcome, if developers feel this is a worthy tool to be included I'll work with Cliff to create repository and package accordingly (as a utility)

Zip File
Latest Version
http://www.trilec.com/opensource/ChainNotifier_1.2.zip

Previous
http://www.trilec.com/opensource/ChainNotifier_1.1.zip
http://www.trilec.com/opensource/ChainNotifier_1.0.zip


EDIT1: Removed Superfluous Naming and Added SetActive(), a few optimizations and added passing of getType()
EDIT2: AddNode documentation ,
EDIT3: Action Wording change in Docs and Code, Cliffs notes addressed
EDIT4: Version update, better wording of examples

 Enjoy
Curtis [Trilec] 
« Last Edit: April 29, 2008, 04:43:52 by trilec » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #1 on: April 28, 2008, 11:11:57 »

Curtis,

This looks really cool, and is actually just in time for me to be having a look at it, because of where I am in a project at the moment.

Expect some feedback on this shortly,
-=Cliff>
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #2 on: April 28, 2008, 03:05:15 »

Hi Curtis,

I notice there is a mix of camelCase and StudlyCaps on both properties and methods (i.e. ListenersArray and chainAssocArray). The generally accepted best practice for AS3 (and the standard for PureMVC) is to use camelCase throughout. This isn't an arbitrary "6 of one / half dozen of the other" choice in AS3. Problem is that since you can access static properties of a class by Classname.method, it becomes difficult to visually distinguish a call on a locally named member using StudlyCaps from a reference to a static member of another class. This can often lead to problems that are hard to troubleshoot. The following thread is an example where simply switching member naming to camelCase fixed the issue completely: http://forums.puremvc.org/index.php?topic=373.0

Also, I think, perhaps referring to the Start, Stop, etc functions as commands might be a little confusing since they aren't associated with actual Command classes, and this utility has a lot to do with Notification. Perhaps Actions might be a better term to use, i.e Start action, Stop action, Pause action.

Also, any concrete reason for not just extending Mediator? Since you're implementing IMediator in order to register it as a Mediator, there's not any real baggage I could see that is being avoided. You could ditch getMediatorName, getViewComponent, setViewComponent.

-=Cliff>


Logged
trilec
Sr. Member
****
Posts: 52



View Profile WWW Email
« Reply #3 on: April 28, 2008, 03:54:06 »

Version Update:

*Cliff Suggestions added
*Naming Changed
*Keywords Changes "ACTION"
*Subclass of mediator
*Docs Changed
*camelCase Adjustments

-- See top post for latest link

T
« Last Edit: April 29, 2008, 04:33:45 by trilec » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #4 on: April 28, 2008, 04:09:24 »

I'm working on a large application framework running on MultiCore, and have decided that in addition to interface based communications (as the Modularity demo shows), that event-based communication can be an even more loosely-coupled and strongly-typed way of communicating across Core boundaries. Thus instead of making an interface call on the shell, the module would send an event instead.

This is very nice, but it's async. A module sends an Event thats listened for by the shell, which might be a request to create another module and pass back the reference. It's the getting back the reference part that's async. With an interface, you can do stuff synchronously, getting an immediate return and continue on with your code.

Many people balk at the async nature of service communications when they first start writing client apps. Async communications between modules feels a little too limiting. But the problem is that the actors in this app are more complex, and limiting or controlling access to all functionality is a priority. So, giving a third party module access to the object with the sensitive methods is scary. Sure I could just split the sensitive methods out to another interface that isn't exposed to the module, or even into another actor, but removing the coupling entirely seems way safer.

So having a utility like this ChainNotifier is useful, because when the async response comes back, we pick up at the next step in the process automatically. This causes us to define our process as a series of discrete steps, and gives us a handy way to define how those steps chain together.

The methods for going forward or back a step or to the start or end, make me wonder about the use cases you have in mind.

Anyway all very interesting. Intersects a little with the StartupManager functionality, only StartupManager is completely focused on resource loading.

-=Cliff>
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #5 on: April 28, 2008, 04:17:03 »

BTW, that camelCase goes for functions as well as vars and constants. So it would be addNode() and start() instead of AddNode() and Start().

-=Cliff>
Logged
trilec
Sr. Member
****
Posts: 52



View Profile WWW Email
« Reply #6 on: April 28, 2008, 04:33:52 »

Hi Cliff,
The concept behind "Actions" was put in after I realized the potential need for more in-depth means of control flow.
An example of this may be a repeating chain of events and within this repetition certain notifications may wish to be skipped, stopped, paused or moved to the end if certain logic conditions are met etc.
at first was wondering about basing nodes on a tree structure where branches could be active or inactive and processing of events could take interesting paths depending on state logic.

But certainly in its infancy simplicity is best, you press a button and can guarantee a string of events will fire in an asynchronous manner (useful if you're also trying to maintain transitional states)

I like your idea of inter-core communication seems interesting. I am just finishing up an interesting way to manage styles without using any of the flash style manages as I'm trying to do dynamic styles at runtime and this could be a useful method of notifying widgets of the style update.

T
« Last Edit: April 29, 2008, 04:32:26 by trilec » Logged
theFlashWizard
Jr. Member
**
Posts: 13


View Profile Email
« Reply #7 on: April 29, 2008, 01:07:19 »

With my Notification Queue I also considered using a mediator, because it can send and receive notifications.
But, shouldn't a mediator be a mediator for a view component and not a chain/queue?
The chain/queue also is mainly about data loading and having to wait for that. Therefor I fount the Proxy better fitting for the job. The only downside is that I have to use a command to let proxy goto the next step.
How do you guys think about this?
Logged
trilec
Sr. Member
****
Posts: 52



View Profile WWW Email
« Reply #8 on: April 29, 2008, 03:07:32 »

Yes I did debate this for a while as well, but it in the end I wanted what ever was simpler.
I feel the role it is providing is still one of mediation, talking to the system through notifications, but instead off discussing this with a view it discusses it with itself...lol (may be a stretch here and could lead to madness).
Also I was looking towards allowing action notifications  (which would manipulate the chain) and felt could accomplish this cleaner by using the mediator.

T
« Last Edit: April 29, 2008, 04:23:13 by trilec » Logged
trilec
Sr. Member
****
Posts: 52



View Profile WWW Email
« Reply #9 on: April 30, 2008, 10:27:12 »

Version Update:
*Ability to add your own actions, better documentation and toString function for debugging
*Mediator name now must be supplied, and will generate unique Action notifications names based on the Actions Mediator name.
ChainNotifier("MYCHAINNAME");
would yeld ...
MYCHAINNAME_ACTION_START
MYCHAINNAME_ACTION_STOP
.. etc

This helps the programmer by not having to deal with creating unique Action notifications in a multi-Chain environment. (but can still be overridden)

-- See top post for latest link

T
« Last Edit: April 30, 2008, 10:31:12 by trilec » Logged
binarycrafts
Newbie
*
Posts: 6



View Profile WWW Email
« Reply #10 on: May 16, 2008, 06:03:37 »

This notifications chaining looks really handy indeed.
I am on the way to implementing it in my AIR client for a REST service.
Right now I am making a chain for getting the general data ( list of projects for my ProjectsProxy, list of PostTypes for my PostTypes Proxy, ... ).
I am putting these app level logic chains in the app mediator. In the logic there are certain notifications that will trigger one of the chains. For instance the above one is triggered when my AuthProxy emits a "LoginSuccess" notification.
I was browsing trough the class code but I want to doublecheck with anyone who used this succesfully and has a bit of more indepth knowlege.
Would it be ok to put the chains as properties of my app mediator. Init them in the constructor and register them when needed ( when a certain notification is received in the handleNotification of my app mediator )?
Is there a way to register it but start it later. Or maybe creating the chains when needed using var chain = new ChainNotifier() makes more sense and is the intended way of usage?

cosmin
Logged
trilec
Sr. Member
****
Posts: 52



View Profile WWW Email
« Reply #11 on: May 29, 2008, 07:20:42 »

The utility in its true form is a mediator.
when you create a ChainNotifier it is added to the system in order to receve events.
when an event is recived it sends out the next event.

if the ChainNotifier is in another mediator, issues maybe if you delete the org  mediator
you will also need to remove the created  ChainNotifier .
these is support for onRemove() so it will clean up after itself when removed

iF you are also using the new version, unique action names will be based on the supplied name
so no conflict should occure.

var chainNotifier:ChainNotifier = new ChainNotifier("MYCHAINNAME");

Trilec

Logged
binarycrafts
Newbie
*
Posts: 6



View Profile WWW Email
« Reply #12 on: June 19, 2008, 03:38:22 »

addNode adds only the first one.
I verify this by tracing the listNotificationInterests().

Hmm, actually the class handles the listeners differently. All are there but I think it's supposed to activate them one by one.
Still it doesn't advance to the 2nd node notifyListener...

And should it work for singlecore if  I just remove the multicore from the path at:
import org.puremvc.as3.multicore.interfaces.*;
?

« Last Edit: June 19, 2008, 05:20:20 by binarycrafts » Logged
binarycrafts
Newbie
*
Posts: 6



View Profile WWW Email
« Reply #13 on: July 18, 2008, 06:14:06 »

Not working for me.
Right now I think that it actually does not listen to the 2nd node's notifyListener at all.
This makes sense because at register time the chain's listNotificationInterests() returns only the 1st node's notifyListener.
Upon each nextAction the view should actually refresh the observers and create a new observer that links the chain and the notification of the current action.
Does this happen?
Can the framework handle dynamic listNotificationInterests ?
« Last Edit: July 18, 2008, 06:20:02 by binarycrafts » Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #14 on: July 18, 2008, 09:17:02 »

Can the framework handle dynamic listNotificationInterests ?

There's no problem with having listNotificationIntersts return something dynamic rather than a static list. However it is only called when a Mediator is registered and will therefore only be useful if you remove the Mediator and then reregister it.


-=Cliff>
Logged
Pages: [1] 2
Print