How to Use RobotLegs On Top of Gaia: Part 1 of 3 – Quickstart

How to Use RobotLegs On Top of Gaia: Part 1 of 3 – Quickstart

*** NOTE: This article is out of date, and superseded by Part 2. ***

The following is a quick start guide on how to get RobotLegs working on top of the Gaia Flash Framework written by Steven Sacks.  In later articles, I’ll show how to use per-Gaia-page Contexts, a visual example, as well as cover Enterprise scenarios.  For now this should get you up and running in 10 minutes.

What is RobotLegs?

RobotLegs is a MVCS framework for Flex & pure AS3 applications.  You can use with Flash as well.  I call it PureMVC done right.  She’s still not at version 1, but is really close (docs in progress).

What is Gaia?

The Gaia Flash Framework is the Ruby on Rails for building Flash websites.  You can code generate your entire Flash website in seconds.  It has modules, SEO, page navigation, and deep-linking all built-in, amongst other things.

Why?

Gaia is great for building websites, but it’s just a platform for you to build on, a bunch of helpful scaffolding; it doesn’t prescribe a way to build your applications.  Specifically, you have to write your own code for Business logic and Application/Domain logic.  You still have to write code to hit back-end services, parse the returning XML/JSON/AMF, and act on that data.

This is where an application framework like RobotLegs comes in.  You’re domain model goes in the Model classes, your Business logic goes in the Service classes, and your Domain/Application logic goes in your Commands & Mediators.  You setup your shiz in your Context class(es), like PureMVC’s ApplicationFacade

Gaia builds your site, and RobotLegs makes it work with dynamic data.

Requirements

Before we get to the “How”, you need to know 2 requirements first.  This may scare some people off.

  1. You need both mxmlc & Flash CS4 or CS3
  2. Your application must be AS3

RobotLegs requires compiling in mxmlc because of the custom metadata tags.  Flash CS3 & CS4 do not currently support the -keep-as3-metadata mxmlc compiler parameter, thus, you must compile your RobotLeg’s implementation code using mxmlc (via Flex Builder 2, 3, Flash Builder 4, FDT, FlashDevelop, etc.).

While you can compile Gaia to an SWC in Flash CS4, and then re-compile via a Library linkage in mxmlc, this defeats the purpose of using the Gaia workflow in Flash.  I understand this is deal killer for many automated build/continuous integrations of Enterprise applications, so I’ll cover optional build scenarios in a future article.

Flash CS4 is required because it allows you to link to external libraries, in this case, Flex created SWC’s.  You could utilize Flash CS3, and just add a class path to the RobotLegs classes since you’ll typically only be using the interfaces in your Flash/Gaia specific code.  Both Flash CS3/CS4 will be exporting AS3 code since RobotLegs is for AS3, so you can’t use Flash 8 and export to AS2.

I currently do not have access to Flash CS5 alpha/beta to determine if it’s integration with Flash Builder 4 demoed at MAX 2009 would help in this instance, nor do I know if it can -keep-as3-metadata.

Quickstart Preface

The Quickstart may appear intimidating at 15 steps.  If you know Flex Builder/FlashDevelop, Flash, Gaia, and RobotLegs, you’ll do just fine, it’s not that bad.  Additionally, you only need to do this stuff once.

The rest of your project, you’ll spend in your code editor.  You can also link the Main file in Flex Builder to get code hinting on it.  The only time you go to Flash is to do Control + Enter.

GIT-R-DONE!!!11oneone

Quickstart

1. Setup your Gaia site.

2. Open up your main.fla in Flash.

3. In Flash CS4, go to File > Publish Settings, click the Flash tab, click the Settings button next to AS version, click the Library Path tab, and link to the RobotLegs.swc.  In Flash CS3, just add a class path to the RobotLegs source code directory.

flash-lib

4. Save your main.fla.

5. Create an ActionScript 3 project in Flex/Flash Builder, FDT, or FlashDevelop.  Point it to the same directory your Gaia project lives.  I suggest changing bin-debug to bin since that’s what Gaia uses.  Although it’ll generate a SWF, it’s hereafter referred to as a “module” SWF since Gaia will load it in and use it’s pre-compiled classes.

6. Create your own main class, differently named (ie not “Main.as”), and put next to Gaia’s Main.as.  This will be where your RobotLegs code lives.

project-dir

7. Link to the RobotLegs.swc as a Library.  If you are in Flex/Flash Builder, you may wish to link to the RobotLegsLib Library project instead.  If so, I put this in Gaia’s lib folder next to the FLA’s that Gaia puts there.  The image below shows linking to the Library Project.

lib-link

8. Create a “MainContext” ActionScript class where ever you’d like in your package structure.  Might I recommend something other than Gaia’s pages directory, like “com.company.project.robotlegs.contexts”.  In this class, you register your modules, in this case, your Gaia pages that actually need Mediators.  Here’s mine:

package com.jxl.gaiarobotlegs.robotlegs.contexts
{
        import com.jxl.gaiarobotlegs.pages.IAboutPage;
        import com.jxl.gaiarobotlegs.robotlegs.mediators.AboutMediator;

        import flash.display.DisplayObjectContainer;

        import org.robotlegs.mvcs.Context;

        public class MainContext extends Context
        {
                public function MainContext(contextView:DisplayObjectContainer)
                {
                        super(contextView);
                }

                public override function startup():void
                {
                        mediatorMap.mapModule('com.jxl.gaiarobotlegs.pages::AboutPage', IAboutPage, AboutMediator);
                        super.startup();
                }
        }
}

Notice the mapModule method goes “Your Gaia Page class as a String”, “The interface the Gaia Page class and the Mediator share”, and “The RobotLegs Mediator class”.  NOTE: In older builds of RobotLegs, they are using the fully qualified class name which is ::AboutPage, not .AboutPage (more info).  I have a hacked version locally which accepts a normal package path of “pages.AboutPage” vs. “pages::AboutPage”.  Yes, I’ve asked the RobotLegs authors to fix this.

9. Create 1 more class and 1 corresponding interface: a Mediator class for whatever Gaia page you’ll be mediating, and an interface of the same name with the I prefix.  Example: If you’re creating an “About Us” page for your site, you’ll probably have an about page node in your site.xml, and thus a corresponding FLA.  Create an “IAboutUs” interface, and an “AboutUsMediator” class that implements the interface.  Your Gaia “AboutUsPage” class will also implement the “IAboutUs” interface.  This is how RobotLegs will communicate to your Gaia code via the Bridge Pattern (more info on why).

Here’s the interface:

package com.jxl.gaiarobotlegs.pages
{
        public interface IAboutPage
        {
                function setAboutText(value:String):void;
        }
}

Here’s the Mediator:

package com.jxl.gaiarobotlegs.robotlegs.mediators
{
        import com.jxl.gaiarobotlegs.pages.IAboutPage;

        import flash.events.TimerEvent;
        import flash.utils.Timer;

        import org.robotlegs.mvcs.Mediator;

        public class AboutMediator extends Mediator
        {
                [Inject]
                public var aboutPage:IAboutPage;

                private var timer:Timer;

                public function AboutMediator()
                {
                        super();
                }

                public override function onRegister():void
                {
                        timer = new Timer(3 * 1000);
                        timer.addEventListener(TimerEvent.TIMER, onTick, false, 0, true);
                        timer.start();
                }

                private function onTick(event:TimerEvent):void
                {
                        timer.stop();
                        timer.removeEventListener(TimerEvent.TIMER, onTick);
                        timer = null;

                        aboutPage.setAboutText("Blah blah blah,nthis is from RobotLeg's 'AboutMediator'");
                }
        }
}

Thing to note in the above is the Dependency Injection via the [Inject] tag does IAboutPage vs. AboutPage; this ensures mxmlc doesn’t attempt to compile Gaia code into your module SWF.

10. Any events your Gaia About Us page will emit, put in the IAboutUs interface.  Any data your Gaia About Us page needs to have set on itself, implement a setter or a method in the IAboutUs interface.  This’ll ensure your About Us page class in Gaia and your AboutUsMediator won’t compile until you implement those methods, nor will your AboutUsMediator RobotLegs class.  Yes, I know events in interfaces aren’t enforced, but that doesn’t mean you shouldn’t do it.

Here’s the Gaia AboutPage class:

package com.jxl.gaiarobotlegs.pages
{
        import com.gaiaframework.api.*;
        import com.gaiaframework.debug.*;
        import com.gaiaframework.events.*;
        import com.gaiaframework.templates.AbstractPage;

        import flash.display.*;
        import flash.events.*;
        import flash.text.TextField;

        import gs.*;

        public class AboutPage extends AbstractPage implements IAboutPage
        {
                public var copyTextField:TextField;

                public function AboutPage()
                {
                        super();
                        alpha = 0;
                        //new Scaffold(this);
                }

                // called by RobotLegs's AboutPageMediator
                public function setAboutText(value:String):void
                {
                        copyTextField.text = value;
                }

                override public function transitionIn():void
                {
                        super.transitionIn();
                        TweenLite.to(this, 0.3, {alpha:1, onComplete:transitionInComplete});
                }

                override public function transitionOut():void
                {
                        super.transitionOut();
                        TweenLite.to(this, 0.3, {alpha:0, onComplete:transitionOutComplete});
                }
        }
}

Notice the implementation of IAboutPage.  Since Gaia FLA’s by default have “../src” set in their class path, it’ll share the same class path with your ActionScript project.  The only class it’s importing form that code, however, is the interface, which is a few 300 bytes or so once compiled into the SWF.  If you’re clever, you could use External Libraries in CS4, but that’s out of scope for this article.

11. Open up your main.as file in your editor of choice.  First, create a mainContext class reference, like:

private var mainContext:IContext;

12. Override init and do no call super.init.  Instead, write code to load in your RobotLegs SWF that your ActionScript project will create in bin.  You can use a Loader, your own wrapper class, extend Main to abstract away these details in a base class… whatever you want.  I used a Loader for this example.  Ensure you load the classes into the same ApplicationDomain so Gaia can share and use these classes, as well as any loaded SWF’s that need them.

var loaderContext:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
moduleLoader.load(new URLRequest('GaiaRobotLegs.swf'), loaderContext);

13. In your Event.COMPLETE function, snatch your MainContext class from the loaded SWF, instantiate it, and pass the stage in, and call super.init to let Gaia know you’re done, like so:

private function onComplete(event:Event):void
{
        const mainContextClassName:String = "com.jxl.gaiarobotlegs.robotlegs.contexts.MainContext";
        try
        {
                var mainContextClass:Class = moduleLoader.contentLoaderInfo.applicationDomain.getDefinition(mainContextClassName) as Class;
                mainContext = new mainContextClass(stage) as IContext;
        }
        catch(err:Error)
        {
                trace("Failed to find class: " + err);
        }
        super.init();
}

You use the stage so any DisplayObject added to the DisplayList will be checked to see if it has an associated Mediator.  Yes, I know OOP Purists will groan at this; don’t worry, I’ll offer a more pure solution later.  Remember, Gaia is typically used in Agencies with 2 week deadlines vs. 4 months; this is more than good enough for that crowd.

14. In your main ActionScript project class, define a dependency variable; this is strictly to ensure your RobotLegs code is compiled into the “module SWF”.  Assuming you have Build Automatically on in Flex Builder, it should make your SWF in the bin folder for you once you save.  Here’s mine:

package
{

        import com.jxl.gaiarobotlegs.robotlegs.contexts.MainContext;

        import flash.display.Sprite;

        public class GaiaRobotLegs extends Sprite
        {
                private var mainContext:MainContext;

                public function GaiaRobotLegs()
                {
                        super();
                }
        }
}

15. Test your Gaia project in Flash.

Conclusions

As you can see, all you need to do now is code all your code in Flex Builder (or editor of choice), and anytime you need to compile your site, you just go back to main.fla in Flash and hit Control + Enter.

You’ll also notice I create one “global” context here.  Reiterating, this is good enough for most design agencies as not every Gaia page will need a Mediator, and most Gaia pages aren’t that complex View’s anyway.

For the purists, in a future article I’ll explain how you can modify the Gaia template files, and configure your Context classes via the Gaia site.xml externally.  This ensures that the details of a Gaia page registering it’s own Context, or simply just creating a single Context + Mediator for itself, are self-contained and as strongly-typed as possible.

For those building Enterprise sized sites with automated build processes, I’ll later go into how you can integrate a Gaia site into your build process.

Thanks

Just a quick thanks to Steven for helping answer my Gaia architecture questions, and to the RobotLegs crew for helping me work out the details of how it could be done.

16 Responses

  1. How exactly did you put the events in the IAboutUs interface? Are you talking about [Event] metadata tags? Event constants (usually static so don’t go in interfaces)?

  2. thanks for the great post; this could be very useful – tuned in….

  3. I couldn’t stand the manual switching between apps on my last Flash project and so created a workflow that was a bit more automated. Basically tied an Ant task to Mike Chambers’ flashcommand Python script, which results in the switch to Flash and compile step being tied to a keyboard shortcut. More details at http://j.mp/2elgSk if you’re interested.

  4. Thanks B! Good info, I remember that post from Mike from a looooong time ago; still relevant today!

  5. [...] is some new resources about using RobotLegs on top of Gaia This was written by admin. Posted on Thursday, September 3, [...]

  6. Glenn

    Last posted: Robert Penner
    “Event constants (usually static so don’t go in interfaces)? ”
    I also don’t exactly understand how you put Event notifications within the interface itself. Do you mean you provide public getters in the interface which actually return the private/protected static constants of the class itself? I’ve tried it sometimes in Gaia projects to avoid needing to import the full class definition into the .swf and this also allows cross-platform accessibility since the interface signatures are already compiled in the parent domain.

  7. Glidias

    Regarding DI and “global” context, I’m more concerned on the possible effect on performance there could there be when registering on the global stage context. The main concern isn’t about purists’/non-purist standpoints from Gaia developers or not, but the fact that registering a context view on the stage can literally kill an application that has tons of interactive movement and child removal/adding activity going on since Flash/Gaia apps can often involve integrating other aspects like Pv3D and other rich content, particles, etc..

    If i’m not wrong, the ContextView will listen for all Event.ADDED_TO_STAGE events in the capture phase for all children and sub-children, which means any newly added item in the display list will get processed whether nested or not. This is done through the Reflector utility (which will get the qualified class name of those items and check it with the mediator map to see if it should register a mediator).

    Either way, having this could waste unnecessary CPU cycles, getting the framework to do more unnecessary work for you (ie. finding class names remotely of any unknown item added to the stage) at the cost of performance. When talking about raw performance such as adding a series of child particles having their own display container, this could be detrimental since the context view would also do redundant checking of those items everytime a child is dynamically added or populated. (For non-graphic-intensive applications though, the effect on performance could be lesser since developers often populate their list displays with dynamic content prior to actually adding them to stage to avoid stage invalidation. However, this is normally done by component Flex users (or other users of components of similar nature). This however won’t work for Flash developers that often at times, already have their list-display items and UI stuff already staged on the Gaia page document already). To populate an on-stage list with 100 items for example, would call the reflector utility method 100 times in the onAddedToStage handler in the ContextView’s MediatorMap.

    I think it may be better to listen to a specific app-specific event bubble which triggers the reflection (done by only the required view-based classes themselves, which can also pre-define and encapsulate certain reflection parameters in the event itself, or even provide a payload of multiple mediators to register), rather than hogging Event.ADDED_TO_STAGE and forcing the reflector to “find out” what class it is. It’s like a shot in the dark. But I guess, the idea of payloading an event which is sent to the framework to request an injection defeats the very purpose of IOC and getting IOC to do the controlling for you. Or perhaps, I’m not aware of a way in which it may be possible to “register one’s own” context. But I know it can be done by setting useCapture to false, which only limits registering the context to a single displayObject only. (Since ADDED_TO_STAGE isn’t bubbling), which seems pointless.

    Also, if the view components need to dispatch their own custom events to notify the ContextView, there’s a requirement on the view component’s part to run that event dispatch on added to stage, which means more unnecessary boiler-plte code on the view component’s part.

    On a sidenote, maybe the Gaia Page could function as an IMediator himself, while the components and view modules could be housed as Gaia assets within that page. Certain views (such as pages) often change due to client requests and to create an app-specific set of methods just to change something as trivial as a page title, for example, isn’t good if the API has to constantly change to support fluctuating graphic design changes. If you need to, have an entire suite of components and generic/minimalistic cross-platform interface signatures such as IText, ILabler, IList, etc. to handle such operations on the view component (even though this would mean the mediator or page would have to make “assumptions” that the view compenets has these interfaces. Normally, developers would run exception checks to see if the casted interface referencess are available before executing them), or find a convenient way to inject a series of method/setter executions on an unknown view-based class based off an array of casted interfaces, and skip the execution if the class no longer implemenent the interface (and returns null).

    Generally, I think it may be more convenient to automatically map Gaia nodes to Gaia page variables or method executions. But again, to hardcode this request within the Gaia page itself could mean faster off-hand performance rather than having the framework “shoot” in the dark for you.

    On the other hand, to create a template for such Gaia pages or mediator-specific view components to dispatch a specific notification context events when being added or transitioned in to notify RobotLegs, would be fine and boiler plate is still kept to the minimal. You still get the framework to do the processing, but only when necessary to reduce the overhead.

    Generally, regarding DI and such, I’ve been considering how inverting control could mean more unnecessary processing overhead on the framework’s part, particularly if one is more interested in raw performance. What do you think?

  8. [...] above mentioned frameworks is that most, if not all, can be used in conjunction with each other (like so). If you don’t like the way one part does it’s job, you can more than likey swap out [...]

  9. Almost have it. but i t get …

    Error
    Failed to find class: Error: Mediator Class does not implement IMediator – [class IAboutPage]

    I am using CS3 for Gaia, and creating an ActionScript Project for the robot legs via eclipse/flexbuilder plugin.

    mapModule is out of date? is it mapView now?

    when i debug GaiaRobotLegs, it gives me a null value for mainContext. So it seems the class is not being included?

  10. @cole 1.0′s API has changed.

    So, yes, use mapView; he works both with classes and modules now. Also, his API order has changed too; it’s:

    mediatorMap.mapView(‘com.jxl.gaiarobotlegs.pages::AboutPage’, AboutMediator, IAboutPage);

    Notice the Interface is 3rd, not 2nd.

  11. [...] from Flash IDE (CS3 or CS4) and found two approaches: the first one came’s form Jesse Warden combining mxmlc & Flash CS4 or CS3 with GAIA Framework. the second one (that i think is really much easier) i found it at Helmut Granda post using XML [...]

  12. Great article, Jesse. You’re probably crazy busy, but I (and undoubtedly many others) would be thrilled to read part 2 and 3. Do you have any plans for these still?

  13. @Mads Thanks chief. Yeah, give me 2 more weeks. Now that Robotlegs is stable, and I’ve gotten a lot of community feedback both here, on Twitter, and during speaking engagements, I know what I need to say. I just have to wrangle some code examples under control first. I also have 2 conferences coming up which take a lot of time.

  14. Rob

    Kind of funny that someone copy/pasta’d this post and put it on their russian blog.

    http://pro.tekaev.com/2009/11/how-to-use-robotlegs-on-top-of-gaia.html

  15. cheers dude! big up for the excellent article!!! enjoy your guiness and i really hope you’re going to write more about this subject…

    guess most gaia-developers didn’t see the potential but i have to emphasize: IT REALLY ISN’T THAT COMPLEX ;) just set it up once and enjoy the possibilities robotlegs has to offer!

    rock on!

    btw: it would be dope if you could publish your preso from 360flex *g*

  16. oh; one question regarding step 8 -> why should we use:
    mediatorMap.mapView(”com.jxl.gaiarobotlegs.pages::AboutPage’, AboutMediator, IAboutPage);
    instead of:
    mediatorMap.mapView(getQualifiedClassName(AboutPage), AboutMediator, IAboutPage);

    is there a reason why you use the first (less readable) approach? ;)

    tia!

  17. @freemind By importing AboutPage into Robotlegs, you’re importing all the other classes that go with it. This includes TweenLite, and all the other View related GUI graphics that it uses. Since it’s not linked to a FLA when you compile in mxmlc, you may even get compile errors.

    I get the getQualifiedClassName angle, but once you import AboutPage, you bring with it all the other classes it references.

  18. hah! that makes sense; one detail i didn’t have in mind. thought it had something to do with the older version of gaia you were using for the tut… thx for your feedback mate!