Tuesday, January 06, 2009

New Swiz 0.0.5 for the New Year!

Update: Just wanted to clear up a few typos. You need to call setStrict(true) before calling loadBeans([Beans]). The code snippet has been fixed. Also, I misspoke, Sönke's strict flag only supports fully qualified class names or static constants in the format SOME_EVENT (all caps with underscores). I have also made that change.

Happy New Year all! You may have noticed I've been on a bit of a blogging holiday, but after a productive for Christmas/new years, I've released a new build of Swiz over at our googlecode site! I was hoping to make a release earlier this week, but decided to take a little extra time to restructure the repository so I could include dependencies and project files to make it easier to build Swiz from source. So what's in this shiny new build? We've got two major features, and a bunch of bug fixes, which I'll list out at the end of the post. Most importantly though is run-time checking of event types for DynamicMediators, which we are supporting through two different approaches. It really only takes one or two times scratching your head over some non functioning code, only to find a misspelled event type, to see the need for run-time checking of event types. The first approach has been added by a new addition to the Swiz team Sönke Rohde. Sönke's been very active on the google groups list, as well as blogging some great articles on Swiz and talking with me quite a bit off list about Swiz enhancements. I've very happy to have him now contributing! But let's get on to this run time event checking...

If you've read the documentation for DynamicMediator, you know that Swiz looks for the Mediate annotation to essentially create a wrapper for a function to be added as an event listener for any event type. The syntax looks like the following:

[Mediate(event="user:SaveUserEvent", properties="user")]
public function saveUser(user : User) : void { ... }

Swiz will not only look for Mediate annotations in objects in your BeanLoaders, but also in any view it sees added to the stage. It then creates a DynamicMediator for the event type supplied, which passes the event properties listed into the function you have annotated. DynamicMediators can significantly reduce boilerplate code, but do have one major drawback. You can only supply string values in Flex medatada tags. It would be great to be able to write:

[Mediate(event=com.foo.SaveUserEvent.SAVE_EVENT, properties="user")]

and have the Flex compiler understand that you are referring to a property of a class, but unfortunately, it won't. However, Sönke has added a 'strict' flag, which will force Swiz to evaluate the string value of the event type as a fully qualified class name or static constant in that class, and throw an error if the class cannot be found. This will occur as soon as Swiz tried to create the DynamicMediator, effectively giving you runtime checking of your event types. In order to use this in your project, you would change your initialization of Swiz to something like the following, in an event handler for preinitialize:

private function onInitialize() : void {
    Swiz.setStrict(true).loadBeans( [ Beans ] );
}

You may have noticed the method chaining style I used, which is another new change. Static configuration methods on Swiz return the Swiz instance, allowing you to chain method calls. Now if we change the mediate annotation above to:

[Mediate(event="com.foo.SaveUserEvent.SAVE_EVENT", properties="user")]
public function saveUser(user : User) : void { ... }

Swiz will first check to make sure that a class named com.foo.SaveUserEvent exists, and has a string property called SAVE_EVENT when it attempts to create the DymanicMediator. Early on, Sönke had told me that he commonly uses a standard in his development of always using fully qualified class names as his event types, so an Event class com.foo.SaveUserEvent would use a call to super("com.foo.SaveUserEvent") in it's constructor. In that case, you could use an annotation such as the following:

[Mediate(event="com.foo.SaveUserEvent", properties="user")]
public function saveUser(user : User) : void { ... }

and Swiz would just make sure the class existed. However, this would enforce that you MUST have a class for every event type, excluding DynamicEvents. By adding adding the ability to resolve a static constant, you can place your event types in any class. Let's say you had a com.foo.EventConsants class, you could use:

[Mediate(event="com.foo.EventConsants.SAVE_USER_EVENT", properties="user")]

for your mediator, and then dispatch a DynamicEvent like this:

var e : DynamicEvent = new DynamicEvent(EventConsants.SAVE_USER_EVENT);
e.user = currentUser;
Swiz.dispatchEvent( e );

I really like the flexibility of this approach. However, while Sönke was working on his strict checking approach, I was working on a similar solution to the same problem. While attending a Spring User Group meeting, I saw that they were experiencing similar issues with their move to annotation driven configuration, and was very intrigued by their 'expression language'. So I have come up with a simple implementation of my own, which I think will continue show it's use in Swiz elsewhere as time goes on.

Let's take a look at how you might use the new expression language instead of the strict approach. In my own work, I have gotten into the practice of storing event types in my controllers, for me it's a way of keeping them part of a particular domain. So in the previous example, if I have a UserController with a SAVE_USER_EVENT property, I would reference it directly in my SaveUserEvent type, calling super(UserController.SAVE_USER_EVENT) in the constructor. Since I have added my UserController to BeanLoader, with an id of "userController", I can now use an expression in my Mediator to reference the SAVE_USER_EVENT property, like so:

[Mediate(event="${userController.SAVE_USER_EVENT}", properties="user")]

When Swiz encounters the ant-style syntax I chose for expressions, it evaluates it in the format [beanId].[property]. If a bean with the supplied ID does not exist, you will immediately get a runtime ExpressionError. What's nice about this format is, you can use public static constants or instance variables for your event types,, and you can even nest them! In the earlier example using Sönke's strict flag, I mentioned using a class called EventConstants for event types. Depending on your development style, you could either provide this bean to Swiz and reference it directly in you expression, like I did with the userController, or maybe your controllers could have an instance of it, in an 'eventTypes' property. Then you can use an expression like:

[Mediate(event="${userController.eventTypes.SAVE_USER_EVENT}", properties="user")]

I think that both these styles will be a great improvement for developers using Swiz, providing a flexible model to suit your development standards. Unless I have strong community outcry to adopt one format over the other, I see no reason not to support both. Well, this post has already gotten a bit long in the tooth, so maybe I'll leave this as part one, and let you know that part two will cover, Swiz's new Prototype bean! So go download the new Swiz 0.0.5 library and happy coding!

Changes / Fixes for Swiz 0.0.5:

- DynamicChannelSet now contains an 'endPointName' property that can be set to 'flex2gateway' for ColdFusion
- Multiple calls to autowire for an object or view will only create mediators once
- handleAutowireEvent ignores flash.* and mx.* classes
- DynamicMediators allow event properties to be null
- Primitive objects can be added to BeanLoaders
- Autowire now supports by type (with [Autowire] instead of [Autowire(bean="foo")])
- DynamicResponder supports passing an array of values to be passed back into the result handler
* for example to have the original saved object passed in to a save result function
- Refactored all bean factory specific functionality out of Swiz and into BeanFactory
- Added Strict flag and MediatorUtils for event type checking
- Added ExpressionUtils, also or event type checking
- Added Prototype object, for non-singleton bean definitions

Labels: , ,

3 Comments:

Blogger All Smile said...

Chris,

Thanks for your hard work with this.

I'll give version 0.5 a test run in a week or two and publish a real world application soon.

Thanks
-Hem

10:14 AM  
Blogger Dan Nelson said...

Thanks for this great little framework. The development team that I work with love using it! We will have to download this new release right away.
Thanks again!

-Dan

12:28 AM  
Blogger ThunderHead said...

GREAT Job - u know, would be nice to Mediate where the function name _is_ the event type, in such a way that if you dispatch( "foo"), the [Mediate] public function foo() : void {...} will be automagically "registered" - in addition, would be nice to enable swiz to stop listening to any added children, say after an application creation complete event. WDYT ?

9:47 AM  

Post a Comment

<< Home