Monday 2007/10/22
10:25 AM

Categories: Flash/Actionscript, Ruby/Rails, RubyAMF, Web Dev, Work

AS3 + SSR, RubyAMF, and RESTful Rails

Update 2008-05-02: This example was written using SSR v1 and RubyAMF 1.3.4, and does not currrently match what you get with the latest versions. I'll be updating it shortly to reflect the changes against both SSR and RubyAMF.

Update 2007-10-25: A quick AS2 example using the Rails controller detailed below can be found here.

The RubyAMF blog recently linked to a good tutorial on using Flex, RubyAMF, and RESTful Rails. At Domani we've been using RubyAMF for a few projects recently, but without Flex. So here's a quick tutorial on using Flash, SSR (Super Simple Remoting), and RubyAMF with RESTful Rails in a local development environment. This assumes the usage of OS X, Locomotive, and MAMP — for some notes on setting that up I wrote a quick post on setting that up here. As always, many thanks to Aaron and the crew behind RubyAMF for their work and quick responses to questions.

Set Up Your Database

Create a new database called people_development. I used the phpMyAdmin interface running on the MAMP instance to do this.

Next, create a new rails app in Locomotive (Applications > Create New). I called the app people. The first thing I usually do is bring up the info screen for the newly-created app and change the port from the default set by Locomotive. I've noticed that the listing in the main Locomotive window will still display the default port even after you change it; to avoid this hit tab from the port field while you're in the info screen and it should refresh the display in the main window.

Modify the database config file (config > database.yml) to add two things:

port: 8889
socket: /Applications/MAMP/tmp/mysql/mysql.sock

Also set the password to "root", which is the default for databases served by MAMP.

Select the Rails app in the main Locomotive listing, and hit Command+T to fire up a terminal session in the context of the app you created. From the terminal session run this command (one line):

ruby script/generate scaffold_resource person first_name:string last_name:string

Among other things, this will create a migration that we can run to create a table for our app in the database. From the terminal run:

rake db:migrate

At this point you should have people table in the people_development database.

Install RubyAMF

From your terminal session run (one line):

ruby script/plugin install http://rubyamf.googlecode.com/svn/trunk/rubyamf

After RubyAMF finishes the install, start the people app in Locomotive. Verify that the RubyAMF gateway is live by pointing your browser to http://localhost:3000/rubyamf/gateway.

Next, verify that the app is live by pointing your browser to http://localhost:3000/people. Create a few people to populate the database with some entries.

Add AMF format response

Add a line for the amf format response in the people controller file (app > controllers> people_controller.rb)

format.amf { render :amf => @people }

The index method should read:

RUBY:
  1. def index
  2.     @people = Person.find(:all)
  3.  
  4.     respond_to do |format|
  5.       format.html # index.rhtml
  6.       format.xml  { render :xml => @people.to_xml }
  7.       format.amf { render :amf => @people }
  8.     end
  9. end

Set Up Your Flash App

Create a new FLA called people_rest.fla. Drag a DataGrid component onto the stage so that the library contains the DataGrid symbol and the DataGrid classes can be. You can delete the DataGrid off the stage after this.

Download SSR from here. Unzip the folder, and move the org folder to the same location as your FLA.

Create a document class called PeopleRest.as, and link it to your FLA as the Document class via the Properties panel. PeopleRest.as will establish a connection to the Rails app via the RubyAMF gateway and make a call to the index method.

PeopleRest.as:

Actionscript:
  1. package {
  2.     import flash.net.Responder;
  3.     import flash.display.MovieClip;
  4.     import fl.controls.DataGrid;
  5.     import fl.data.DataProvider;
  6.     import flash.events.Event;
  7.  
  8.     import org.rubyamf.remoting.ssr.*;
  9.  
  10.     public class PeopleRest extends MovieClip
  11.     {
  12.         private var rs:RemotingService;
  13.         private var peopleGrid:DataGrid;
  14.  
  15.         public function PeopleRest()
  16.         {
  17.             init();
  18.         }
  19.  
  20.         private function init(): void
  21.         {
  22.             peopleGrid = new DataGrid();
  23.             peopleGrid.x = 50;
  24.             peopleGrid.y = 180;
  25.             peopleGrid.width = 400;
  26.             addChild(peopleGrid);
  27.  
  28.             rs = new RemotingService("http://localhost:3000/rubyamf/gateway", "PeopleController");
  29.             rs.addEventListener(FaultEvent.CONNECTION_ERROR, onConnectFault);
  30.             rs.addHeader('recordset_format',false,'fl9');
  31.             rs.index([], onList, onFault);
  32.         }
  33.  
  34.         private function onList(re:ResultEvent=null):void {
  35.             var people:Object = re.result;
  36.             var dp:DataProvider = new DataProvider(re.result);
  37.             peopleGrid.dataProvider = dp;
  38.         }
  39.  
  40.         private function onFault(fault:FaultEvent):void
  41.         {
  42.             trace("PeopleRest::onFault: " + fault.fault.faultString);
  43.         }
  44.  
  45.         private function onConnectFault(fe:FaultEvent):void
  46.         {
  47.             trace("PeopleRest::onConnectFault() " + fe);
  48.         }
  49.  
  50.     };
  51.  
  52. }

Compile the movie. You should see the person entries in your database in the data grid component on stage.

Adding Create/Update/Destroy Functionality

At this point you can add functionality to PeopleRest.as by adding input fields and buttons for triggering calls to the create, update, and destroy methods in the controller. Below are the edits to the Rails controller that are needed to support AMF responses:

Create:

RUBY:
  1. def create
  2.   if @is_amf
  3.     @person = Person.new();
  4.     @person.first_name = params[:first_name]
  5.     @person.last_name = params[:last_name]
  6.   else
  7.     @person = Person.new(params[:person])
  8.   end
  9.  
  10.   respond_to do |format|
  11.     if @person.save
  12.       flash[:notice] = 'Person was successfully created.'
  13.       format.html { redirect_to person_url(@person) }
  14.       format.xml  { head :created, :location => person_url(@person) }
  15.       format.amf { render :amf => @person }
  16.     else
  17.       format.html { render :action => "new" }
  18.       format.xml  { render :xml => @person.errors.to_xml }
  19.     end
  20.   end
  21. end

Update:

RUBY:
  1. def update
  2.   @person = Person.find(params[:id])
  3.  
  4.   if @is_amf
  5.     @person=Person.find(params[:id])
  6.     @person.first_name=params[:first_name]
  7.     @person.last_name=params[:last_name]
  8.   else
  9.     @event = Person.new(params[:person])
  10.   end 
  11.  
  12.   respond_to do |format|
  13.     if @person.update_attributes(params[:person])
  14.       flash[:notice] = 'Person was successfully updated.'
  15.       format.html { redirect_to person_url(@person) }
  16.       format.xml  { head :ok }
  17.       format.amf { render :amf => @person }
  18.     else
  19.       format.html { render :action => "edit" }
  20.       format.xml  { render :xml => @person.errors.to_xml }
  21.     end
  22.   end
  23. end

Destroy:

RUBY:
  1. def destroy
  2.   @person = Person.find(params[:id])
  3.   @person.destroy
  4.  
  5.   respond_to do |format|
  6.     format.html { redirect_to people_url }
  7.     format.xml  { head :ok }
  8.     format.amf { render :amf => 'deleted' }
  9.   end
  10. end

The final FLA, Actionscript3 document class, and rails controller file are here: people_rest.zip.


Responses


Aaron Smith

Tuesday 2007/10/23 4:16 AM

Thanks for the tutorial. Looks great!


thingsiam.com is shiny again :: sansumbrella

Tuesday 2008/03/11 12:58 AM

[...] at work using RubyAMF (for some details on how to do that yourself, check Mark Llobrera’s tutorial), but I still find working with the mediatemple rails install a bit tricky. Once I figure [...]


Bachir

Friday 2008/05/02 3:37 AM

great post!

I am not sure if the ssr package has changed since then, but in actionscript when passing for example:
rs.destroy( [ {id:personId} ], onResult, onFault );

in ruby the only way i could retrieve the id was like this:

def destroy
@person = Person.find(params[0][:id])

instead of
def destroy
@person = Person.find(params[:id])

since it’s an array of objects being passed rather than an object.

just found that out after have wasted some time.

do you agree, or is there something I am missing?


ds

Friday 2008/05/02 9:07 AM

Bachir:

I don’t think you’re missing anything – this was written against RubyAMF version 1.3.4 and SSR version 1. I believe that RubyAMF is up to 1.5, and SSR2 has been out for a while as well.

When I have some time next week I’ll try to update this simple example against the latest versions.


B.E.

Tuesday 2008/07/08 6:23 PM

This is neat! I’m playing with it in a Rails 1.2.3 app with old version of SSR and RubyAMF (the same that are in RubyAMF’s Flash9 examples)) and have it working in part. It gets the people from the db, but lists them as [object Object], and saves, but not with any values.
But I’ll try some more. Great stuff and I hope you update this with the code for SSR2 (Guttershark?) and latest RubyAMF some day. :)


Andreas

Monday 2008/07/21 10:00 AM

Hi,

do you still plan on writing a short example with the current RubyAMF & SSR? Would love to see it. Have been playing around for a while and cannot sort it out.

Thanks


ds

Monday 2008/07/21 10:08 AM

Andreas: Thanks for the reminder on updating this to the current RubyAMF/SSR releases. I’ll try to sort it out today.


pt

Thursday 2008/09/25 5:30 PM

Hi there, it is a awesome tutorial.

I am newbie. Sorry for the stupid question. However, when I compile this, I got this error message “1046: Type was not found or was not a compile-time constant: ResultEvent”.

Can anyone help me out with this issue?

Thanks everyone


Leave a Response