These things are never done, but here’s my first cut at a Cairngorm 3 / Parsley application (view source enabled):

Some code and design ideas were borrowed from insync, others from cafe townsend.

It’s a rough cut, but not terrible. It uses Parsley model injection, commands and an event interceptor (when you try to place a bet and you’re not logged in, it prompts you to log in), Flex 4 skinning and is localizable. I added some navigation features, but think I’d need to use something like SWFAddress for deep-linking. It does not have unit testing, but I wrote it to be testable (with business logic in commands).

On my wish list:
– Deep linking through the URL.
– Using the Task Library so that after the user is successfully saved, the bet result event is sent.
– Unit testing.
– A coin flipping animation (which raises additional questions about how to synchronize events and view states)…

One way this deviates from the Cairngorm 3 guidelines is that it does not use the Presentation Model pattern. I decided against this for two reasons. First, the client I was learning this for does not use this pattern. Second, it seemed like an unnecessary layer of decoupling. If you use skins and the Presentation Model, the decoupling starts to get a bit ridiculous: for every view element you’d have a Presentation Model, a SkinnableComponent and a Skin, with the business logic contained in commands. Finally, there’s some data (such as the currently logged in user) that seems to belong in an application model rather than a presentation model – why tie it to a particular view component?

Events are separated into two categories: view events (which I prefix with “UI”) that are handled by top components, and application events which are handled by commands. For example, when the user clicks on a number to place a guess, a UIGuessANumberPickEvent is dispatched. Following the principle that only top-level components should dispatch application events, the GuessANumberPage component handles this event by dispatching the application event UserGuessANumberEvent. This is then handled by the dynamic command UserGuessANumberCommand. It’s kind of a lot of code for something so simple, but it follows a pattern, and allows for eventually testing the business logic.

There are two ways you can dispatch events so that the Parsley IOC can route them to commands. One way looks like this:

    [Event(name="guessFlipACoinWon", type="net.flexpla.sharkys.application.events.UserGuessFlipACoinEvent")]
    [Event(name="guessFlipACoinLost", type="net.flexpla.sharkys.application.events.UserGuessFlipACoinEvent")]
    [ManagedEvents("guessFlipACoinWon,guessFlipACoinLost,saveUser")]
    public class UserGuessFlipACoinCommand extends EventDispatcher
    {
        public function dispatchSave():void
       {
            dispatchEvent(new SaveUserEvent(SaveUserEvent.SAVE, updatedUser.userVO));

The other way is this:

    public class UserGuessFlipACoinCommand
    {
        [MessageDispatcher]
        public var dispatcher:Function;

        public function dispatchSave():void
       {
            dispatcher(new SaveUserEvent(SaveUserEvent.SAVE, updatedUser.userVO));

I strongly prefer the second way. It’s more readable and vastly less brittle, since metadata has no error-checking.

There’s an architectural problem with this code that seems pretty endemic to the pattern itself: the LoginModel is essentially a global that can be injected anywhere, and the currentUser setter is public. This is not nearly as bad as making the currentUser a global variable, since at least there’s only one function that can modify the currentUser, but it’s not ideal. What I’d really like to do is have the setter only accessible to LoginCommand. One possibility would be something like this:

    public class LoginModel extends EventDispatcher
    {
        private var _currentUser:User;

        // read-only bindable getter for the model's state
        [Bindable(event='currentUserChanged')]
        public function get currentUser:User():String
        {
            return _currentUser:User;
        }

        public function setCurrentUser(value:user, command:LoginCommand):void
        {
            if (_currentUser:User!= value)
            {
                _currentUser:User= value;
                dispatchEvent(new Event("currentUserChanged"));
            }
        }

The idea is that the LoginCommand could set the current user by passing ‘this’ as the second parameter. Of course, anyone could call it like so: setCurrentUser(whateverUser, new LoginCommand()), so it’s not bulletproof. Also, it might complicate testing somewhat. Still, it would make for slightly more robust code.

Feedback welcome! Thanks.

Advertisements

Jens Halm at spicefactory helped clear up some confusion on my part. If you declare a command usingĀ <DynamicCommand> in your context file, you should not also use metatags like [CommandResult] in your command class. I had done this, and was finding that result() was being called twice.

Bugs in my parsley…

February 4, 2011

The dependency injection in Parsley is nice when it works, but what do you do when your objects aren’t configured properly and you get an error like this?

Error: Error in message receiver
at org.spicefactory.parsley.core.messaging.impl::DefaultMessageProcessor/unhandledError()[I:\Spicefactory\Parsley\parsley-core\org\spicefactory\parsley\core\messaging\impl\DefaultMessageProcessor.as:134]
at org.spicefactory.parsley.core.messaging.impl::DefaultMessageProcessor/handleError()[I:\Spicefactory\Parsley\parsley-core\org\spicefactory\parsley\core\messaging\impl\DefaultMessageProcessor.as:128]
at org.spicefactory.parsley.core.messaging.impl::DefaultMessageProcessor/proceed()[I:\Spicefactory\Parsley\parsley-core\org\spicefactory\parsley\core\messaging\impl\DefaultMessageProcessor.as:107]
...etc...

There’s the brute force approach: just comment stuff out until it stops breaking, then you know where the issue is. This is crude, time-consuming and problematic…what if you forget to uncomment the code out? This happened to a client of mine recently.

You can step it up by configuring logging for Parsley. I added the following to my application’s <fx:Declarations> block:

        <mx:TraceTarget id="logTarget"
            includeTime="true"
            includeCategory="true"
            includeLevel="true">
            <mx:filters>
                <fx:Array>
                    <fx:String>org.spicefactory.*</fx:String>
                </fx:Array>
            </mx:filters>
            <mx:level>0</mx:level>
        </mx:TraceTarget>

With logging on, I learned a little more about my error, namely:
Caused by: Error: Unsatisfied dependency: Context does not contain an object of type mx.rpc.remoting::RemoteObject

Better, but where exactly in my code is this occurring?

Time to step it up again and enable source code level debugging. First, you need the source code. I selected “Import…” then “Checkout Projects from SVN” and checked out http://opensource.powerflasher.com/spicefactory/svn/parsley/trunk/main. This gave me an error at the end, but it didn’t really matter for my purposes.

Once I got the source code, I edited my project’s settings, went to Run/Debug Settings and edited my main application’s launch configuration to add the Parsley folder to the Source path (with ‘Search subfolders’ checked).

So, next I just searched for where “Unsatisfied dependency: Context does not contain an object” messages were thrown and set breakpoints wherever they occur. Long story short, I stopped inside ObjectTypeReference.resolve() and worked my way up the call stack to find that the problem was with my “SaveUserCommand” class that had this erroneous declaration:

        [Inject]
        public var service:RemoteObject;

Well, that was a little annoying, but a good “learning moment.” Moving on…

Initially, I found Cairngorm 3 a little hard to wrap my brain around. It’s not a framework per se, but a set of optional libraries, guidelines and suggestions for other frameworks to use. It almost seems possible to accidentally code a “Cairngorm 3” program, at least by Adobe’s definition. All that said, many of the optional Cairngorm 3 libraries provided by Adobe require the Parsley framework, so Adobe seems to be strongly nudging in that direction. I’m going to take the hint and build my first Cairngorm 3 application with Parsley, using those optional libraries when I can.

My goal will be to build a complete (if profoundly stupid) Cairngorm 3 application from design to completion, following all Cairngorm 3 guidelines, and including the following features:

  • Deep-linking. This application will have two pages, and each can be navigated to through a URL.
  • Localizable string resources. Because it’s the right thing to do.
  • Use of SkinnableComponent and SparkSkin for visual components.
  • Unit testing.

So what will it do? Well, a recruiter recently tried to get me to move to Austin to help build “video gaming” devices. I won’t lie – my soul is definitely for sale, but the asking price is higher than what they were offering. Still, why not build a video gaming website just for fun? Maybe not Texas Hold ‘Em, but more something like…