Vixiom Axioms

August 29, 2006

Flash through the years

Filed under: Cool Shise, Flash Alastair @ 1:15 pm

History of Flash

Adobe have created a great interactive overview of Flash for it’s tenth anniversary. The piece itself is a showcase for what the Flash player can now do, audio, video, and advanced interaction (make sure to give the world a spin with your mouse).

They do break two of the Vixiom design rules

  1. Don’t use overly long transitions between content (cool the first time, annoying thereafter)
  2. Kern all type including numbers!!! (All the years in the 90s, 1996 etc., have a gap you could drive a truck through after the ‘1′, my typography professor would have failed them on the spot)
Digg! submit Flash through the years to stumbleupon.com submit Flash through the years to del.icio.us submit Flash through the years to reddit.com Like this post? subscribe to the feed.

August 26, 2006

Flash Remoting for Rails Tutorial 2 - Data Swings Both Ways

Filed under: ActionScript, Flash, Ruby on Rails Alastair @ 3:02 pm

In the first tutorial I showed how easy it is to pass data from Ruby on Rails to Flash via Flash Remoting, this tutorial will show that sending data objects back to Rails is just as simple. Backpack was one of the first apps to get people excited by Rails, I’ll be ripping off their To-do list feature and making it Flash rather than Ajax. I’m using Flash professional DataGrids for this, so unfortunately if you only have the regular flavor of Flash you’re out of luck (I’m guessing if you do webdev with Flash you use the pro version anyways). The entire source code for the tutorial can be downloaded here. The Flash source files are in the ‘fla’ directory.

As before start a new Rails app and call it ‘todoapp’

> rails todoapp

Change in the directory

> cd todoapp

Install WebORB for Ruy on Rails

> ruby script/plugin install http://themidnightcoders.net:8089/svn/weborb

There was briefly a bug with WebORB and ActionScript v2.0 components and sending parameters with your remoting call, however it’s been fixed (very quickly, thanks!). If you’ve previously installed WebORB in another Rails app you can update WebORB by adding –force at the end of the install command (warning: it will overwrite any config files you edited).

> ruby script/plugin install http://themidnightcoders.net:8089/svn/weborb –force

Create a MySQL database called ‘todoapp_development’ with a table called ‘todos’ (I’ve populated it with my wish list for Adobe) there’s a ‘todo.sql file’ in the ‘db’ direcotry of the source code.

CREATE TABLE `todos` (
  `id` int(11) NOT NULL auto_increment,
  `title` varchar(50) NOT NULL default ,
  `done` tinyint(4) NOT NULL default ‘0′,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

insert into `todos` values(‘1′,‘Release Flex Builder for OS X’,‘0′),
 (‘2′,‘Release Creative Suite Universal’,‘0′),
 (‘3′,‘Release Apollo’,‘0′);

Create a ‘Todo’ model

> ruby script/generate model Todo

Create a remoting service inside of the app/services directory called ‘TodoService.rb’

require ‘weborb/context’
require ‘rbconfig’

class TodoService

  # getTodos for remoting
  def getTodos
    getTodoLists
  end

  # toggleTodo for for remoting
  def toggleTodo(id)
    todoToToggle = Todo.find(id)
    # set to true(1) if false(0) or vice versa (1 - 0 = 1, 1 - 1 = 0)
    todoToToggle.done = 1 - todoToToggle.done
    # if save send new data back to Flash
    if todoToToggle.save
      getTodoLists
    end
  end

  # toggleTodo for for remoting
  def addTodo(title)
    todoToAdd = Todo.new
    todoToAdd.title = title
    # if save send new data back to Flash
    if todoToAdd.save
      getTodoLists
    end
  end

  # get the todo lists (used throughout)
  def getTodoLists
    # putting lists in a hash as we have two sets of data
    todos = Hash.new
    todos[“not_done”] = Todo.find(:all, :conditions => [“done LIKE ?”, false])
    todos[“done”] = Todo.find(:all, :conditions => [“done LIKE ?”, true])
    return todos
  end

end

There’s a bit more going on here than the last tutorial which only had one method, this time there are three (four counting ‘getTodoLists’ which is used throughout the service). ‘getTodos’ calls ‘getTodoLists’ which fetches all the items in the ‘todos’ table. Since we want to split the todos into those that are done and not we make a Hash object to hold the filtered items. todos["not_done"] gets all that have their done column marked false and todos["done"] gets those that are true. You could return all the data and filter it in Flash but this way is easier.

‘toggleTodo(id)’ if a method that will be called from Flash via Remoting is has an argument ‘id’ that is used to find the todo item that you want to change from not done to done or vice versa. If the todo item has been succesfully toggled and saved we call ‘getTodoLists’ which returns the updated data back to Flash.

addTodo(title)‘ adds a new todo item with it’s ‘title’ coming in from Flash via Remoting, and returns the updated data again back to Flash using ‘getTodoLists‘.

With that setup and using the source for this tutorial you can now start the local lighttpd/WebBrick server and test the todo.fla in the Flash IDE. Click on the todo item’s title to send it to the other column.


Click on a todo…


…and it moves to the done list


The database is updated

 

Now for an overview of what’s happening on the Flash side. As in the first tutorial I’m using base Remoting.as and View.as classes that I extended with other classes. Below is the extened Remoting class ‘RemotingTodo.as’. New from the first tutorial are params/args (args? I never know the difference I started out as a designer) being sent to Rails eventObj.data.id’ in the line
‘var pc:PendingCall = this.svc.toggleTodo(eventObj.data.id);’
of ‘toggleTodo‘ where eventObj.data is a Todo item object staright from Rails, so appending ‘.id’ works just as it would in Rails (I cover event dispatching in the first tutorial if this is as confusing as it reads, Flex has event dispatching built in which makes things easier)

/**
*   @class RemotingTodo
*   @extends Remoting
*   @author Alastair Dawson
*   @copyright 2006 Vixiom Communications, LLC
*/

import mx.remoting.*;
import mx.rpc.*;

class com.vixiom.remoting.RemotingTodo extends com.vixiom.remoting.Remoting
{
    // todos holder array
    private var todos:Object;

    //////////////////////////////////////////////////////////////////////
    //
    // Constructor (gatewayURL, servicePath, userid, password)
    //
    //////////////////////////////////////////////////////////////////////

    public function RemotingTodo (gURL, sp, u, p)
    {
        super(gURL, sp);
        // this.svc.connection.setCredentials(u, p);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Get todos (handler:onGetTodos)
    //
    //////////////////////////////////////////////////////////////////////

    public function getTodos()
    {
        trace(“// remoting - getting todos”)
        // create a pending call out to rails
        var pc:PendingCall = this.svc.getTodos();
        // create a responder to handle the return from rails
        pc.responder = new RelayResponder(this, “onGetTodos”, “handleRemotingError”);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Toggle todo (handler:onGetTodos)
    //
    //////////////////////////////////////////////////////////////////////

    public function toggleTodo(eventObj:Object)
    {
        trace(“// remoting - toggle todo: “ + eventObj.data.title)
        // create a pending call out to rails
        var pc:PendingCall = this.svc.toggleTodo(eventObj.data.id);
        // create a responder to handle the return from rails
        pc.responder = new RelayResponder(this, “onGetTodos”, “handleRemotingError”);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Add todo (handler:onGetTodos)
    //
    //////////////////////////////////////////////////////////////////////

    public function addTodo(eventObj:Object)
    {
        trace(“// remoting - add todo: “ + eventObj.data)
        // create a pending call out to rails
        var pc:PendingCall = this.svc.addTodo(eventObj.data);
        // create a responder to handle the return from rails
        pc.responder = new RelayResponder(this, “onGetTodos”, “handleRemotingError”);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onGetTodos handler
    //
    //////////////////////////////////////////////////////////////////////

    public function onGetTodos (re:ResultEvent)
    {
        if (re != undefined)
        {
            trace(“// onGetTodos broadcaster - Word!”)

            // put result in recordset
            todos = re.result;

            // dispatch event to the view
            dispatch(todos, “onGetTodos”);
        }
    }
}

In the todo.fla file we create instances of the RemotingTodo and ViewTodo classes and connect broadcasters to their listeners. The previous tutorial only had one event being broadcast between the classes (Remoting telling the View when it had fetched the data) this time there are two broadcasters for the View to tell Remoting to "toggleTodo" and "addTodo". The todo lists are Flash DataGrid components and they get event listeners that broadcast when they ‘change’ i.e. someone clicks on a row.

/**
*   @author Alastair Dawson
*   @copyright 2006 Vixiom Communications, LLC
*/

// import remoting, view, and debug
import mx.remoting.debug.NetDebug;
import mx.utils.Delegate;
import com.vixiom.remoting.RemotingTodo;
import com.vixiom.view.ViewTodo;

// ini debug
NetDebug.initialize ();

// style for grids (apple green is fugly)
_global.style.setStyle (“themeColor”, “haloOrange”);

iniApp();

// setup and start
function iniApp()
{
    // create remoting & view objects
    var RTodo:RemotingTodo  = new RemotingTodo (“http://localhost:3000/weborb”, “TodoService”); // weborb gateway, ruby class name
    var VTodo:ViewTodo      = new ViewTodo (_root);

    // set up listeners
    RTodo.addEventListener (“onGetTodos”, Delegate.create (VTodo, VTodo.onGetTodos));
    VTodo.addEventListener (“toggleTodo”, Delegate.create (RTodo, RTodo.toggleTodo));
    VTodo.addEventListener (“addTodo”, Delegate.create (RTodo, RTodo.addTodo));

    todo_dg.addEventListener(“change”, Delegate.create (VTodo, VTodo.onTodoChange));
    done_dg.addEventListener(“change”, Delegate.create (VTodo, VTodo.onDoneChange));

    add_btn.onRelease = Delegate.create(VTodo, VTodo.addTodo);

    // start the app, get the todos
    RTodo.getTodos();
}

Below is the extended View class, it’s all ActionScript related so I won’t go into it but there’s a ‘todos’ object to hold our data from Rails (after it’s passed from the Remoting class) there’s validation in there to make sure the add to do input field isn’t blank (again Flex has validation built in).

/**
*   @class ViewTodo
*   @extends View
*   @author Alastair Dawson
*   @copyright 2006 Vixiom Communications, LLC
*/
import mx.controls.DataGrid
import mx.controls.TextInput
import mx.controls.Button

class com.vixiom.view.ViewTodo extends com.vixiom.view.View
{
    // todos holder object
    private var todos:Object;

    // objects on stage
    private var todo_dg     :DataGrid;
    private var done_dg     :DataGrid;
    private var title_txt   :TextInput;

    //////////////////////////////////////////////////////////////////////
    //
    // Constructor (target)
    //
    //////////////////////////////////////////////////////////////////////

    public function ViewTodo (t:MovieClip)
    {
        super(t);

        // references for objects on the stage
        todo_dg     = target.todo_dg;
        done_dg     = target.done_dg;
        title_txt   = target.title_txt;
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onGetTodos listener
    //
    //////////////////////////////////////////////////////////////////////

    public function onGetTodos(eventObj:Object)
    {
        trace (“// onGetTodos listener - Word heard!”);

        // todos object
        todos = eventObj.data;

        // update lists
        updateLists();
    }

    //////////////////////////////////////////////////////////////////////
    //
    // todo & done dataGrid listeners
    //
    //////////////////////////////////////////////////////////////////////

    public function onTodoChange(eventObj:Object)
    {
        // set the selected todo
        var selectedTodo = todos.not_done[eventObj.target.selectedIndex];

        trace(“// view - on change: “ + selectedTodo.title);

        // tell remoting to change select item to done
        dispatch(selectedTodo, “toggleTodo”);
    }

    public function onDoneChange(eventObj:Object)
    {
        // set the selected todo
        var selectedTodo = todos.done[eventObj.target.selectedIndex];

        trace(“// view - on change: “ + selectedTodo.title);

        // tell remoting to move the select item to todo
        dispatch(selectedTodo, “toggleTodo”);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // update the todo and done lists
    //
    //////////////////////////////////////////////////////////////////////

    private function updateLists()
    {
        // populate data grids with remoting objects
        todo_dg.dataProvider = todos.not_done;
        done_dg.dataProvider = todos.done;

        /* when using dataProviders with dataGrids they show all your columns
            in the database, remove the columns we don’t want to see */
        todo_dg.removeColumnAt(todo_dg.getColumnIndex(“id”));
        todo_dg.removeColumnAt(todo_dg.getColumnIndex(“done”));
        done_dg.removeColumnAt(done_dg.getColumnIndex(“id”));
        done_dg.removeColumnAt(done_dg.getColumnIndex(“done”));
    }

    //////////////////////////////////////////////////////////////////////
    //
    // add button
    //
    //////////////////////////////////////////////////////////////////////

    public function addTodo()
    {
        if (notEmpty(title_txt.text)) {
            // not empty dispatch to remoting (RTodo)
            dispatch(title_txt.text, “addTodo”);
            // reset style (might have been an error previously)
            title_txt.setStyle (“backgroundColor”, 0xFFFFFF);
            title_txt.setStyle (“themeColor”, “haloOrange”);
            title_txt.setStyle (“color”, 0×000000);
        } else {
            title_txt.text = “Todo can’t be blank”;
            // style the text box to alert the user to the error
            title_txt.setStyle (“backgroundColor”, 0xFFDFDC);
            title_txt.setStyle (“themeColor”, 0xCC0000);
            title_txt.setStyle (“color”, 0×990000);
        }
    }

    //////////////////////////////////////////////////////////////////////
    //
    // validate field
    //
    //////////////////////////////////////////////////////////////////////

    private function notEmpty (s:String):Boolean
    {
        if (s == “” || s == “Todo can’t be blank”) {
            return false
        } else {
            return true
        }
    }

}

That’s it! you now know how to pass data back and forth between Rails and Flash with Flash Remoting, now go build some cool shise!

PS For ActionScripters looking to learn Rails Agile Web Development is a great book to get started with, there’s also the ‘pickaxe‘ book online for learning Ruby. For Railers wnating to know more about ActionScript Essential ActionScript 2.0 is the best programming, never mind ActionScript, book I’ve ever read.

Digg! submit Flash Remoting for Rails Tutorial 2 - Data Swings Both Ways to stumbleupon.com submit Flash Remoting for Rails Tutorial 2 - Data Swings Both Ways to del.icio.us submit Flash Remoting for Rails Tutorial 2 - Data Swings Both Ways to reddit.com Like this post? subscribe to the feed.

August 25, 2006

Build Flex on OS X with TextMate

Filed under: Flex, Ruby on Rails Alastair @ 1:00 am

Since Adobe decided to ignore us Mac stiffs - not like I’ve ever done anything for ‘em, I’ve only been buying Photoshop since version 3 - I’ve made my own quasi Flex work-flow with “the missing editor for OS X” TextMate.

BTW Photoshop 3 introduced layers which was their greatest innovation until Flex 2.0 :P

Okay enough about my love/hate relationship with Adobe, here’s how I built the ‘Vixiom Flex Builder 3000′. First make sure you have Xcode installed (I can’t remember if this is 100% necessary before I was using TextMate I used Xcode to build from Josh Buhler’s instructions), if you use ruby Xcode is probably already installed, if not install it from the CD that came with your Mac. Put the Flex SDK in /Developer/SDKs/Flex.

TextMate is completely customizable through the bundle editor, you open it by going to Bundles>Bundle Editor>Show Bundles

To make a new bundle you click the + sign at the bottom left of the editor and select ‘New Bundle’ call it ‘Flex’.

With your Flex bundle selected click the + again and choose ‘New Command’ call it ‘Build Project’, I change the ‘Save:’ drop down to ‘Current File’ so it save your work when you build, also change Output to ‘Discard’ otherwise it will overwrite your file with output (what you would see in the terminal). At the bottom I made ‘Activation’ Apple-B (Command-B) which becomes the short cut to build the mxml file.

Now for some applescripting! I’m somewhat ashamed to say this is my first applescript, I’ve been told it can do all kinds of wonderful things but I’ve just never had the time to get into it before. In the ‘Command(s):’ section of the bundle editor enter the following script.

osascript <<EOF
    tell application “Terminal”
    activate
        do script “/Developer/SDKs/Flex/bin/mxmlc -output $TM_PROJECT_DIRECTORY/public/swf/$TM_FILENAME.swf -file-specs $TM_FILEPATH -services $TM_PROJECT_DIRECTORY/config/WEB-INF/flex/services-config.xml -context-root http://localhost:3000/weborb”
        delay 1
        repeat while the front window is busy
            delay 0.5
        end repeat
    end tell
    tell application “Firefox”
    activate
    end tell
EOF

It translates like this:

  1. open the Terminal;
  2. run script "/Developer/SDKs/Flex/bin/mxmlc -output $TM_PROJECT_DIRECTORY/public/swf/$TM_FILENAME.swf -file-specs $TM_FILEPATH -services $TM_PROJECT_DIRECTORY/config/WEB-INF/flex/services-config.xml -context-root http://localhost:3000/weborb" the last two options are um… optional, they’re used for comiling with WebORB for Rails which of course you’re using to load data into your flex app! There’s some Textmate dynamic variables in there $TM_PROJECT_DIRECTORY which is dynamic for your TextMate project’s directory, and $TM_FILENAME which is the name of your file because of this your output swf will be called ‘filename.mxml.swf’.
  3. pause while mxml compiles (repeat while the front window is busy)
  4. bring Firefox to the front, I had code in there before that opened a specific location in Firefox but it was a hassle to change, the first time I run it I browse to the URL I’m testing then refresh the page when Firefox pops to the front after a compile.

It’s still a bit rough around the edges, I want Firefox to auto-refresh when it pops to the front, also a new Terminal window is created every time I build. I had the window closing but then it closes even if you have an error and you miss the debug info.

One thing I’d love to know is if the compile times I’m getting with the command line compiler are normal, I’m averaging about 10-20 seconds per build which is alright unless you’re debugging then it’s an interminable amount of time. I have Flex Builder on my MacBook Pro with Parallels but haven’t set up Rails yet on there, plus all my files, and production apps are on the G5 so switching back and forth would be a headache (and who wants to work in Windows all day, ack!). However compile times with Flex Builder are almost instant, I think it’s because the command line has to load the config xml files each time while Flex Builder has them built in and cached.

If only I had Flex Builder for OS X *sigh*.

Digg! submit Build Flex on OS X with TextMate to stumbleupon.com submit Build Flex on OS X with TextMate to del.icio.us submit Build Flex on OS X with TextMate to reddit.com Like this post? subscribe to the feed.

August 23, 2006

Flash Remoting for Rails Tutorial

Filed under: ActionScript, Flash, Ruby on Rails Alastair @ 7:22 pm

Ever since I’ve been using Rails I’ve missed one important tool from my webdev toolbox, Flash Remoting. I’ve been getting by with XML but it’s just not the same and it changes the way you approach things ("If all you have is a hammer everything looks like a nail"). Remoting can pass objects, arrays, strings etc. straight to Flash as native data types which avoids XML sit-ups (as the Rails folk would say). Remoting uses AMF (Active Message Format) which comes across as bytes so it’s faster than XML. Yesterday my prayers where answered as the Midnight Coders released WebORB for Ruby on Rails.

Their examples use Flex 2.0 which is the future of of Rich Internet Apps, however requiring Flash player 9.0 in all situations isn’t too user friendly (like on a hybrid HTML page) and since Remoting has been available since Flash player 6 (pre Ajax craziness) you should try to publish for only the features you need. That said I’m a design Nazi, and can’t get enough of anti-alias for readability, so I publish for Flash 8 as much as possible (Adobe claims 86.0% user penetration for player 8).

If you’ve never used Flash Remoting before you need to install the Remoting Components.

We’ll be hooking a Flash MP3 player up to a Rails back-end (which in real life would have some kind of CMS for adding MP3s). For this tutorial I ported a MP3 player I made for Buchanan a local Orange County band. The original uses XML as the data is static. I’ve replaced the Buchanan playlist with a “Midnight” themed one in honor of the hard work Midnight Coders put in to deliver Remoting on Rails for free!

On to the tutorial… a zip file of all the source code is here. All the flash files are in mp3app/fla.

Create a new rails app called ‘mp3app’

> rails mp3app
> cd mp3app

Install Weborb for Rails

> ruby script/plugin install http://themidnightcoders.net:8089/svn/weborb

Create a MySQL development database ‘mp3app_development’ with a table to hold your tracks

CREATE TABLE `tracks` (
  `id` int(11) NOT NULL auto_increment,
  `title` varchar(50) NOT NULL default ,
  `artist` varchar(50) NOT NULL default ,
  `album_art` varchar(50) NOT NULL default ,
  `filename` varchar(50) NOT NULL default ,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

insert into `tracks` values(‘1′,‘After Midnight’,‘Eric Clapton’,‘clapton.jpg’,‘AfterMidnight.mp3′),
 (‘2′,‘Midnight Train to Georgia’,‘Gladys Knight’,‘gladys.jpg’,‘MidnightTrainToGeorgia.mp3′),
 (‘3′,‘Midnight In A Perfect World’,‘DJ Shadow’,’shadow.jpg’,‘MidnightInAPerfectWorld.mp3′),
 (‘4′,‘Two Minutes to Midnight’,‘Iron Maiden’,‘maiden.jpg’,‘TwoMinutesToMidnight.mp3′);

Create a ‘Track’ model

> ruby script/generate model Track

Create a remoting service inside of the app/services directory called ‘TrackService.rb’

require ‘weborb/context’
require ‘rbconfig’

class TrackService
   def getTracks
     tracks = Track.find(:all)
   end
end

If you’ve ever used AMFPHP or one of the .NET flavors of remoting you probably said "That’s it!?!". This is by far the shortest remoting service I’ve ever written. You should now be able to start the local lighttpd/WebBrick server and test the mp3Player.fla file locally. I’ve left all my traces in so you can see what’s going on.

Next on to Flash. There’s a few frameworks out there for Flash apps (ARP, Cairngorn) but for relatively simple things like our MP3 Player they’re overkill. I have my own lightweight framework (if it even qualifies as a framework, it’s only two files) that is a kind of MVC where the Model is a Remoting class that hooks up to the back-end (PHP, .NET, or now Rails) a Controller that is actually the .fla file, and a View class. The base Remoting and View classes have event dispatching for communicating with each other through the controller and that’s about it, any other functionality is extended with other classes.

The base Remoting class looks like this

import mx.remoting.Service;
import mx.services.Log;
import mx.remoting.PendingCall;;
import mx.rpc.RelayResponder;
import mx.rpc.FaultEvent;
import mx.rpc.ResultEvent;
import mx.remoting.debug.NetDebug;
import mx.utils.Delegate;

class com.vixiom.remoting.Remoting
{
    private var gatewayURL:String;
    private var servicePath:String;

    private var svc:Service;

    function dispatchEvent() {};
    function addEventListener() {};
    function removeEventListener() {};

    /**
    * Constructor
    *
    *   @param      gURL    gatewayURL
    *   @param      sp      service path
    *   @param      u       username
    *   @param      p       password
    *
    */
    public function Remoting(gURL, sp, u, p)
    {
        gatewayURL = gURL;
        servicePath = sp;

        // initialize as a broadcaster
        mx.events.EventDispatcher.initialize(this);

        // create a new service
        svc = new Service (gatewayURL, null, servicePath, null, null);

        // credentials
        if (u != undefined && p != undefined) {
            svc.connection.setCredentials(u, p);
        }

    }

    /**
    * Global fault event
    */
    function handleRemotingError(fault:FaultEvent):Void
    {
        mx.remoting.debug.NetDebug.trace({level:“None”, message:“Error: “ + fault.fault.faultstring });
    }

    /**
    * Event dispatcher
    *
    *   @param      d       data
    *   @param      et      eventType
    *
    */
    function dispatch(d, et)
    {
        // broadcast message
        var eventObj:Object={target:this,type:et}
        eventObj.data = d;
        dispatchEvent(eventObj);
    }

}

This is the base View class

import mx.utils.Delegate;

class com.vixiom.view.View
{
    private var target:MovieClip;

    function dispatchEvent() {};
    function addEventListener() {};
    function removeEventListener() {};

    /**
    * Constructor
    *
    *   @param  t   target (target timeline: _root || a mc)
    */
    public function View (t:MovieClip)
    {
        target = t;

        // initialize as a broadcaster
        mx.events.EventDispatcher.initialize(this);
    }

    /**
    * Event dispatcher
    *
    *   @param      d       data
    *   @param      et      eventType
    *
    */
    function dispatch(d, et)
    {
        // broadcast message
        var eventObj:Object={target:this,type:et}
        eventObj.data = d;
        dispatchEvent(eventObj);
    }
}

For the MP3 Player I extended the Remoting class as shown below. You don’t have to keep your classes in packages (com.vixiom.remoting…) but it helps to keep them organized. ‘var pc:PendingCall = this.svc.getTracks();‘ is the line that makes the remoting call to Rails (it corresponds to ‘getTracks‘ in our TrackService.rb class). My class has a ‘tracks’ object and it receives the tracks object straight from ruby ‘tracks = re.result;‘. I’ve commented out the line ‘this.svc.connection.setCredentials(u, p);‘ as that’s for creating secured remoting calls. The last line ‘dispatch(tracks, "onGetTracks");‘ uses even dispatching to pass data to the view.

import mx.remoting.*;
import mx.rpc.*;

class com.vixiom.remoting.RemotingMp3 extends com.vixiom.remoting.Remoting
{
    // tracks holder object
    private var tracks:Object;

    //////////////////////////////////////////////////////////////////////
    //
    // Constructor (gatewayURL, servicePath, userid, password)
    //
    //////////////////////////////////////////////////////////////////////

    public function RemotingMp3 (gURL, sp, u, p)
    {
        super(gURL, sp);
        // this.svc.connection.setCredentials(u, p);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // Get tracks with handler (onGetTracks)
    //
    //////////////////////////////////////////////////////////////////////

    public function getTracks()
    {
        trace(“// getting tracks”)
        // create a pending call out to rails
        var pc:PendingCall = this.svc.getTracks();
        // create a responder to handle the return from rails
        pc.responder = new RelayResponder(this, “onGetTracks”, “handleRemotingError”);
    }

    public function onGetTracks (re:ResultEvent)
    {
        if (re != undefined)
        {
            trace(“// onGetTracks broadcaster - Word!”)
            // put result in recordset
            tracks = re.result;
            // trace for testing
            for (var i = 0; i < tracks.length; i++) {
                trace(tracks[i].title);
            }
            // dispatch event to the view
            dispatch(tracks, “onGetTracks”);
        }
    }
}

This is a little bit out of order but I’m going to show the ‘controller’ code next. The thing to notice is ‘Rmp3′ is an instance of my Extended Remoting class and it has two parameters the weborb gateway URL ‘http://localhost:3000/weborb‘ and the ruby class that’s in the app/services folder ‘TrackService‘. I haven’t shown the Extended View class yet bu the controller code is also creating a ‘Vmp3′ instance of it with the _root of the Flash file as it’s parameter (it uses that as a target). The next four lines are delegating button functions that are in the Extended View, the last line ‘Rmp3.getTracks();‘ calls the remoting method and is the entry point for the app (as it’s pretty useless sans data).

// import remoting, view, and debug
import mx.remoting.debug.NetDebug;
import mx.utils.Delegate;
import com.vixiom.remoting.RemotingMp3;
import com.vixiom.view.ViewMp3;

// ini debug
NetDebug.initialize ();

iniApp();

// setup and start
function iniApp()
{
    // create remoting & view objects
    var Rmp3:RemotingMp3    = new RemotingMp3 (“http://localhost:3000/weborb”, “TrackService”); // weborb gateway, ruby class name
    var Vmp3:ViewMp3        = new ViewMp3 (_root);

    // set up listeners
    Rmp3.addEventListener (“onGetTracks”, Delegate.create (Vmp3, Vmp3.onGetTracks));

    pause_btn.onRelease = Delegate.create(Vmp3, Vmp3.pauseTrack);
    play_btn.onRelease  = Delegate.create(Vmp3, Vmp3.playTrack);
    prev_btn.onRelease  = Delegate.create(Vmp3, Vmp3.previousTrack);
    next_btn.onRelease  = Delegate.create(Vmp3, Vmp3.nextTrack);

    // start the app, get the tracks
    Rmp3.getTracks();
}

Here’s the Extended View. It’s pretty complicated and unrelated to Rails or Remoting so I’ll just wave my hands and say "Ta da!", hopefully my comments explain what’s going on. Our tracks object from the remoting side hitches a ride on an event object in the listener ‘onGetTracks’ (tracks = eventObj.data;)

class com.vixiom.view.ViewMp3 extends com.vixiom.view.View
{
    // tracks holder object
    private var tracks              :Object;
    private var currentTrack        :MovieClip;
    private var albumArt            :MovieClip;

    // tracking vars (no pun intented)
    private var currentTrackNumber  :Number;
    private var totalTracks         :Number;
    private var pausePos            :Number;

    // song position interval
    private var songPosInterval     :Number

    //////////////////////////////////////////////////////////////////////
    //
    // Constructor (target)
    //
    //////////////////////////////////////////////////////////////////////

    public function ViewMp3 (t:MovieClip)
    {
        super(t);
    }

    //////////////////////////////////////////////////////////////////////
    //
    // onGetTracks listener
    //
    //////////////////////////////////////////////////////////////////////

    public function onGetTracks(eventObj:Object)
    {
        trace (“// onGetTracks listener - Word heard!”)
        tracks = eventObj.data;

        // keep track of total tracks :P
        totalTracks = tracks.length;

        // setup the mp3 player
        iniPlayer();
    }

    //////////////////////////////////////////////////////////////////////
    //
    // setup the player
    //
    //////////////////////////////////////////////////////////////////////

    public function iniPlayer ()
    {
        // local reference
        var vObj:ViewMp3 = this;