Part 1: Using WebORB to access ActiveRecords from a Flex application. 5

Posted by Daniel Wanja Sun, 29 Oct 2006 20:21:00 GMT

On Friday I started for a customer an investigation in providing a Flex front-end for an Ruby on Rails backend using WebORB. In parallel I will push this investigation further for myself in order to find a nice mechanisms to support CRUD operations with relationship support using WebORB. Over the next couple of weeks I will write some of my findings on this blog. So this week-end I started to put in place an environment where I can unit test the interaction between Flex and Ruby on Rails using WebORB. In this first part I will show how to extend WebORB to perform a deep find, how to write a Flex unit test to test asynchronous remote calls, and how to use Ruby on Rails fixtures for the Flex unit tests.

This is an extract of the ‘final’ version of the Flex unit test (as of Part 1 of the article). The full version is at the end of the article.

public function testGetFirstCustomer():void {
    var activeRecordService:RemoteObject = getActiveRecordService(onGetFirstCustomerResult); 
    create_fixtures(["customers", "addresses", "orders", "items"], doGetCustomerFirstCustomer, activeRecordService);
}          
private function doGetCustomerFirstCustomer(activeRecordService:Object):void {
    var options:Object = {'include':['bill_to_address', {'orders':'items'}]};
    activeRecordService.get("Customer", 1, options);                       
}
private function onGetFirstCustomerResult(event:Event, token:Object=null):void 
{
    assertTrue(event.toString(), event is ResultEvent); // First param is message.
    var customer:Object = ResultEvent(event).result;
    assertEquals("Daniel", customer.name);
    assertEquals("Littleton", customer.bill_to_address.city);
    assertEquals(2, customer.orders.length);    // 2 order
    assertEquals(3, customer.orders[0].items.length); // the first has 3 items
    assertEquals("Remote Control", customer.orders[0].items[2].product); 
}          
          

Introduction

There are several ways a Flex front-end can connect to a Rails application: via a RESTFul services (xml over html), via SOAP, and now also using WebORB from Midnight Coders (see http://www.themidnightcoders.com/weborb/rubyonrails/index.htm). WebORB for Ruby on Rails is a server-side technology enabling connectivity between Flex and Flash Remoting clients and Ruby on Rails applications. WebORB and Flex provides an efficient way of encoding and decoding data using a binary protocol named AMF3 (Actionscript Message Format (I think)). This format is recognized by the Flash Player and provides an efficient way to communicate between a Flex application and a Ruby On Rails server.

This article will only investigate the WebORB usage, and we will not look into using SOAP or a RESTFul api. Part 1 of the article will show how to setup the Rails and the Flex applications to use WebORB, and how to retrieve data from the server. In subsequent parts of this article, I will comment my findings regarding updating data, security and performance issues and other aspect I discover during my investigation.

As you may not know, with Lee we wrote a similar server-side technology 18 months ago (flexonrails.com) which was based on AMF4R, but we where not satisfied with our findings at that time, nor with the qualitify of one of the library we where using, and the demand for integrating Flex and Ruby On Rails was non existent at that time, certainly due to the high price tag of Flex and the fact that not many developers were interested in both of these technologies as Flex was geared towards the enterprise and RoR was an open source framework. Since then Flex is free (not FlexBuilder, nor the DataServices), but everything we do in this article can be developed and deployed using free tools. Also Adobe is pushing quite hard to appeal to the open source community. I started this project using FlexBuilder for Mac, but I will also show how to compile the samples and tests using the flex command line compiler (mxmlc).

To download WebORB and to set it up I am following the instructions of http://www.themidnightcoders.com/weborb/rubyonrails/gettingstarted.htm. So for more details go see www.themidnightcoders.com

For this article I will create a “fictional” application. For my customer I am directly integrating with the existing application. In fact using this fictional application will allow me to test the WebORB in many different ways. Note that my customers current front-end application is really cool. It is written in plain-old Rails, aka Ajax, html, css, rjs. Nevertheless we wanted to investigate if we can achieve similar functionality with less code using Flex and to identify the cost and benefits of such a development. I will spend a couple of days over the next weeks for this investigations.

These are the steps I followed for this article

1. Create a Rails app and add the WebORB plugin
2. Write a simple unit test to access some data using a RemoteObject 
    a. create the Flex application using FlexBuilder.
         b. create the Customer ActiveRecord
         c. Write the FlexUnit test
    d. Write a small script to compile a Flex app from the command prompt.
         e. make the test pass.
3 . Use fixtures
4. Create a model (db+active record)
5. Write a test to retrieve the model
6. Provide a way to use fixtures from FlexUnit, so we can do some more testing.

1. Create a Rails app and add the WebORB plugin

* Go to the folder where you want create your rails application and issue the following command:
    rails rails

This create the rails application in a folder named “rails”. You can choose any name you want i.e. rails myApp. Note we use rails 1.1.6 for this article.

* Cd to the root of you rails application and install the plugin. The following command installs the plugin files in the vendor/plugins/weborb folder.

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

2. Write a simple unit test to access some data using a RemoteObject

We will put the flex applications in a folder named “flex” and put this folder next to the “rails” folder we create just before. Now lets download FlexUnit from http://weblogs.macromedia.com/as_libraries/zips/flexunit.zip. See Adobes wiki for more information (http://labs.adobe.com/wiki/index.php/ActionScript_3:resources:apis:libraries#FlexUnit)

2.a) Creating a project in FlexBuilder

* Create a FlexBuilder project (select Flex Data Services)
Root Folder: /Users/daniel/MyStuff/Projects/WebORBInvestigation/rails/config
Root Url: http://localhost.com:3000/weborb/

Root Folder points to your Rails application config folder
Root Url points to the weborb controller.

* In the project properties set
Output folder: /Users/daniel/MyStuff/Projects/WebORBInvestigation/rails/public/flex
Output folder URL: http://localhost:3000/flex

  • Add the flexunit.swc (in the /bin folder of the FlexUnit downloads). A .swc is the compiled version of the FlexUnit framework.

2.b) create the Customer ActiveRecord

    ./script/generate model Customer
This generate the following files:
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/customer.rb
      create  test/unit/customer_test.rb
      create  test/fixtures/customers.yml
      create  db/migrate
      create  db/migrate/001_create_customers.rb
Now edit db/migrate/001_create_customers.rb as follows:
class CreateCustomers < ActiveRecord::Migration
  def self.up
    create_table :customers do |t|
      t.column "name", :string
      t.column "address", :string
    end
  end

  def self.down
    drop_table :customers
  end
end
Now make sure you created an empty database and you point to it in the config/database.yml file. Then create the customers table by typing the following command:
    rake migrate
Now let’s create two customers:
./script/console
>> Customer.create(:name => "Daniel", :address => "Denver") 
>> Customer.create(:name => "Samuel", :address => "Geneva")
>> exit

2.c) Write a flex unit test

One of the issues with Flash and Flex unit testing of remote calls is the asynchronous nature of the remote calls. FlexUnit was based on JUnit and was mainly created for synchronous call testing. On one large project we dealt with this issue by splitting each test in two phases. The first phase performing calls and collecting data, we called it CallSequence, and the second phase to test the returned data. Since then, FlexUnit was extended with a nice feature allowing to test asynchronous calls. Look for the usage of the TestCase.addAsync method. It allows to notify that the current test method is waiting for a callback.

With this knowledge in mind let’s dive into writing the test. We want to retrieve the list of customers, ensure that their are two customers, and verify the name off one of the customers. This is what the test looks like:

WebORBReadOnlyTest.as
package {
     import flexunit.framework.TestCase;
    import mx.rpc.remoting.RemoteObject;
    import flash.events.Event;
    import mx.rpc.events.ResultEvent;
    import mx.rpc.events.FaultEvent;     

     public class WebORBReadOnlyTest extends TestCase {

          public function WebORBReadOnlyTest( methodName:String = null ) {
               super( methodName );
        }

        // This method triggers a remote call and defines the event handler for the asynchronous response.
          public function testGetCustomerList():void {
              var asyncCall:Function = addAsync(onCustomerListResult, 1000);
             var dataService:RemoteObject = new RemoteObject();   
            dataService.destination = "DataService"; // Default WebORB ActiveRecord accessor 
            dataService.addEventListener("result", asyncCall);
            dataService.addEventListener("fault", asyncCall);
             dataService.list("Customer");        
           }          
           // This is the even handler trigger by the asynchronous response, let assert that we received what we expected.
        private function onCustomerListResult(event:Event, token:Object=null):void 
        {
            assertTrue(event is ResultEvent); 
            var data:Object = ResultEvent(event).result;
            assertEquals(2, data.length);
            assertEquals("Daniel", data[0].name);
        }

      }
}

We also need a test runner

WebORBTestApp.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
    layout="absolute"
                xmlns:flexunit="flexunit.flexui.*"
                creationComplete="runTests()">
    <mx:Script>
        <![CDATA[
            import flexunit.framework.TestSuite;

            private function runTests():void
            {
                 var ts:TestSuite = new TestSuite();                 
                 ts.addTestSuite( WebORBReadOnlyTest );                 
                testRunner.test = ts;
                testRunner.startTest();
             }
        ]]>
    </mx:Script>

    <!-- flexunit provides a very handy default test runner GUI -->
    <flexunit:TestRunnerBase id="testRunner" width="100%" height="100%" />

</mx:Application>

2.d) Compile the unit test

The following script compiles the application from the command line (on OSX). That is, not using FlexBuilder. You may need to adapt for your needs. On windows it may be easier and you can maybe just refer to the mxmlc.exe. Not that I had to patch the flex-config file due to an compilation issue due to some missing fonts. You can try without the patch first. Let me know if that worked.

build.sh
FLEX_HOME="/Applications/Adobe Flex Builder 2 Beta/Flex SDK 2/"
cp flex-config-patched.xml "$FLEX_HOME/frameworks/"
java -Xmx384m -Dsun.io.useCanonCaches=false -jar "$FLEX_HOME/lib/mxmlc.jar" -load-config "$FLEX_HOME/frameworks/flex-config-patched.xml"  -library-path+=../flexunit/bin/flexunit.swc WebORBTestApp.mxml
cp WebORBTestApp.swf ../../rails/public/flex 

2.e) Run the unit test

Make sure the server is running and launch the flex test application.

Note that I had some red bars before I got the test to pass, but I wanted to spare these details.

Ok, this is quite some work just do test the dataService.list(“Customer”) call. However now I have an environment which I can use to go crazy on testing various aspects of the Flex and Ruby On Rails integration using WebORB.

3 . Extend WebORB to allow to write a FlexUnit test using fixtures

Without fixtures this test wouldn’t be very useful. We need a way to reset the fixtures between each test method invocation.

3.a) First let’s add a mechanism to reset the fixtures using WebORB

create the app/services/ActiveRecordService.rb file (note this is the naming convention for the WebORB services, I would rather use active_record_service.rb but I haven’t tried if that works.)

require 'weborb/context'
require 'active_record/fixtures'
class ActiveRecordService 
  def create_fixtures(table_names)
    Fixtures.create_fixtures("#{RAILS_ROOT}/test/fixtures/", table_names, {})
  end 
end    
ActiveRecordService is the placeholder where we will extend the standard WebORB DataService offering, by adding some utility methods to reset the fixtures and by providing an extended get method as described further in this document.

Now configure WebORB to recognize this new service. Add the following code to the config/WEB-INF/flex/remoting-config.xml file

    <destination id="ActiveRecordService">
        <properties>
            <source>ActiveRecordService</source>
        </properties>
    </destination>

A server restart is required when changing ActiveRecordService. When invoking create_fixtures this reloads the fixture based on the provided table names, however it overwrite the tables content of the database mode (development, test or production) for which you server is currently running in. I use the development mode during this exercise, so my weborb_development tables will be loaded with the fixtures.

3.b) Now let’s invoke the create_fixtures from our flex unit test.

Warning, I haven’t found a nice way (yet) to transparently invoke the create_fixtures from the setup of the test. So for now each test method we will have three methods 1) that notifies that the test method is asynchronous and invokes the create_fixtures methods, 2) the callback of the create fixtures that will perform the ‘real’ remote call we want to perform, and 3) the callback of the ‘real’ remote call to perform the assertions. That’s when you wish that Flex supports directly synchronous calls. However this can be wrapped way nicer than I just did. I will certainly refactor this once I need to write more tests.

Edit the rails/test/fixtures/customers.yml as follows:

first:
  id: 1
  name: Daniel
  address: Littleton
another:
  id: 2
  name: Samuel
  address: Bernex

The test now looks as follows:

package {
     import flash.events.Event;

     import flexunit.framework.TestCase;

     import mx.rpc.events.FaultEvent;
     import mx.rpc.events.ResultEvent;
     import mx.rpc.remoting.RemoteObject;
     import mx.rpc.AsyncToken;     
    import mx.collections.ItemResponder;
    import mx.controls.Alert;

     public class WebORBReadOnlyTest extends TestCase {

          public function WebORBReadOnlyTest( methodName:String = null ) {
               super( methodName );
        }

        // The mechanism to invoke the server side create_fixtures method is still a little clunky here.
        // This should be part of the setUp method, which doesn't support asynchronous setUp for now.
        // So writing a test with fixture now requires three method (that's the really clunky part!)
        //     1. the testXXXXX method that must call the getActiveRecordService and the create_fixture method.
        //     2. the method that will actual call the server (here. doGetCustomerList)
        //     3. the result handler for the abort call that will perform the assert.
          public function testGetCustomerList():void {
             // getActiveRecordService - Signal to TestCase that this is an asynchronous test
             // and pass end point of the test method (in this case the onCustomerListResult method)
             var activeRecordService:RemoteObject = getActiveRecordService(onCustomerListResult); 
              create_fixtures(["customers"], doGetCustomerList, activeRecordService);
           }          
           private function doGetCustomerList(activeRecordService:Object):void {
             activeRecordService.list("Customer");                       
           }
           // This is the even handler trigger by the asynchronous response, let assert that we received what we expected.
        private function onCustomerListResult(event:Event, token:Object=null):void 
        {
            assertTrue(event.toString(), event is ResultEvent); // First param is message.
            var data:Object = ResultEvent(event).result;
            assertEquals(2, data.length);
            assertEquals("Daniel", data[0].name);
            assertEquals("Littleton", data[0].address);
        }

        private function getActiveRecordService(resultHandler:Function, timeOut:Number=2000):RemoteObject 
        {
              var asyncCall:Function = addAsync(resultHandler, timeOut);
             var activeRecordService:RemoteObject = new RemoteObject();   
            activeRecordService.destination = "ActiveRecordService";
            activeRecordService.addEventListener("result", asyncCall);
            activeRecordService.addEventListener("fault", asyncCall);
            return activeRecordService;            
        }

        private function create_fixtures(table_names:Array, callback:Function, callbackData:Object):void 
        {
             var activeRecordService:RemoteObject = new RemoteObject();   
            activeRecordService.destination = "ActiveRecordService";
             var call:AsyncToken = activeRecordService.create_fixtures(table_names);    
            call.addResponder(new ItemResponder(onCreateFixturesResult, onCreateFixturesFault));
            call.callback = callback;
            call.callbackData = callbackData;
        }
        private function onCreateFixturesResult(event:ResultEvent, token:Object=null):void 
        {
            event.token.callback(event.token.callbackData);
        }
        private function onCreateFixturesFault(event:Event, token:Object=null):void
        {
            fail("Failed to create fixtures. "+event.toString());
        }            

      }
}

4. Implementing a deep find

With the default WebORB’s DataService implementation you cannot extract a Customer and his relationships (we will add them shortly) in one call. However this is where the AMF protocol really shines as nested structure can be serialized over http in one call. This is similar to what XML could provide but allows strongly typed objects to be mapped between Flex and Ruby On Rails. The current Customer model is not very representative for an application, so let’s extend our model as follows:

I know you may want to argue why is the ship_to/bill_to on the customer and not on the order. Hey, it’s just for testing purpose for now, just to have some richer relationships between objects :-)

4.1 Extending the Model

the following command to generate the new models
 ./script/generate model Address 
 ./script/generate model Order --skip-migration  
 ./script/generate model  Item --skip-migration  
This creates the active records, the fixtures and the db/migrate/002_create_addresses.rb database migration file.

Edit 002_create_addresses.rb as follows and run ‘rake migrate’

class CreateAddresses < ActiveRecord::Migration
  def self.up
    create_table :addresses do |t|
      t.column "street", :string
      t.column "zip", :string
      t.column "city", :string
    end
    create_table :orders do |t|
      t.column "customer_id", :integer
      t.column "created_at", :datetime
    end
    create_table :items do |t|
      t.column "order_id", :integer
      t.column "quantity", :integer
      t.column "product", :string
    end        
    remove_column :customers, :address
    add_column :customers, "bill_to_address_id", :integer
    add_column :customers, "ship_to_address_id", :integer
  end

  def self.down
    drop_table :addresses
    drop_table :orders
    drop_table :items
    add_column :customers, :address, :string
  end
end
And define the ActiveRecord relationships as follows:
class Customer < ActiveRecord::Base
  has_many :orders
  belongs_to :bill_to_address, :class_name => "Address", :foreign_key => "bill_to_address_id"
  belongs_to :ship_to_address, :class_name => "Address", :foreign_key => "ship_to_address_id"  
end
class Address < ActiveRecord::Base
  belongs_to :customer
end
class Order < ActiveRecord::Base
  belongs_to :customer
  has_many :items
end
class Item < ActiveRecord::Base
  belongs_to :order
end
And the define some more fixtures:
customers.yml
first:
  id: 1
  name: Daniel
another:
  id: 2
  name: Samuel
addresses.yml
first:
  id: 1
  street: First Street.
  zip: 80246
  city: Littleton  
another:
  id: 2
  street: Other Avenue.
  zip: 1200
  city: Bernex  
orders.yml
hardware_order:
  id: 1
  customer_id: 1
  bill_to_address_id: 1
book_order:
  id: 2
  customer_id: 1
  bill_to_address_id: 1
items.yml
media_center:
  id: 1
  order_id: 1
  product: iTv
  quantity: 1
screen:
  id: 2
  order_id: 1
  product: 200" LCD Screen
  quantity: 1
remote:
  id: 3
  order_id: 1
  product: Remote Control
  quantity: 1
book1:
  id: 4
  order_id: 2
  product: Agile Web Development With Rails, 2nd Edition
  quantity: 1
book2:
  id: 5
  order_id: 2
  product: Mastering CSS
  quantity: 1

4.2 Add get to ActiveRecordService

The rails ActiveRecord.find method allows for the :include option to specify what relationship should be retrieved as part of the find. This will allow to perform 1 call and retrieve the customer, his address and all of his orders and order items.

Let’s extend ActiveRecordService as follows (we show only the new methods):

class ActiveRecordService 

  # Allow for a deep fetch (find_by_id with :include options)    
  def get(model_name, id, options={})
    model_class = Object.const_get model_name
    find_options = {:include => options['include']} # Here we limit options for now.
    result = model_class.send(:find_by_id, id, find_options)
    puts result.inspect
    result
  end  

  def find_all(model_name, options={})
    model_class = Object.const_get model_name
    find_options = {:include => options['include']} # Here we limit options for now.
    result = model_class.send(:all, find_options)
    puts result.inspect
    result
  end  
end

4.3 Call the new get method from our flex unit test

          public function testGetFirstCustomer():void {
             var activeRecordService:RemoteObject = getActiveRecordService(onGetFirstCustomerResult); 
              create_fixtures(["customers", "addresses", "orders", "items"], doGetCustomerFirstCustomer, activeRecordService);
           }          
           private function doGetCustomerFirstCustomer(activeRecordService:Object):void {
            var options:Object = {'include':['bill_to_address', {'orders':'items'}]};
             activeRecordService.get("Customer", 1, options);                       
           }
        private function onGetFirstCustomerResult(event:Event, token:Object=null):void 
        {
            assertTrue(event.toString(), event is ResultEvent); // First param is message.
            var customer:Object = ResultEvent(event).result;
            assertEquals("Daniel", customer.name);
            assertEquals("Littleton", customer.bill_to_address.city);
            assertEquals(2, customer.orders.length);    // 2 order
            assertEquals(3, customer.orders[0].items.length); // the first has 3 items
            assertEquals("Remote Control", customer.orders[0].items[2].product); 
        }  

Now that is getting more interesting. From flex in the doGetCustomerFirstCustomer method the following call activeRecordService.get(“Customer”, 1, options); returns the customer his addresses, his orders and all order items. It would be even nicer if we could call Customer.find(1, options).

RailsLogAnalyzer – help wanted 2

Posted by Daniel Wanja Thu, 03 Aug 2006 22:37:00 GMT

I need some Mac users to test and give feed back on a very early version of the RailsLogAnalyzer. This app is an OSX app and not deployed on a server, so I would like to find out the obvious bug that may occur on different Macs. I tried it on my own MacBook Pro, G5 server, and on an old PowerBook G4. But then again the people most likely to use it are Rails developers that may have differences in their environment.

So if you like to take risks :-), understand the basic structure of a Rails app, have a production.log you want to analyze, and are not afraid of some scary bugs then read on…

The RailsLogAnalyzer is an OSX application that allows to visualize the production.log of a Rails application. The RailsLogAnalyzer is itself a Rails application, with a Flash user interface, and wrapped as an OSX app using Platypus.

To use the application you ‘just’ need to drop it in your application folder (or where ever you want) and double click it. See the ‘user guide’ here after.

I wrote this small utility to help me see how users where using different Rails applications I was working on by visualizing the production log data. This utility is just doing what I needed and is pretty basic. It’s definitively not ready for prime time, however your input will help drive it’s development.

Requirements:

  • You need the Flash Player 9. For intel macs the player is in beta and can be downloaded here http://fpdownload.macromedia.com/get/flashplayer/current/fp9_beta/install_flash_player_9_ub_beta.dmg. More info here http://www.adobe.com/products/flashplayer/public_beta/. For PowerPC macs, just starting the application will open a browser window that will guide you through the flash player upgrade if you don’t have version 9 installed.
  • Port 3000 needs to be free. So don’t run any other Rails app when you launch the application.
  • Don’t click too fast. See the user guide here after for more information.

What the application does

  • Show graph of unique session and total request per day.
  • Show pie chart of controller usage (at end of the period observed) .
  • Show the top ten actions invoked (can be filter by http method and controller)
  • Show the graphical geoip information ((at end of the period observed). [Very buggy]
  • Allow time navigation (30 days/7 days increment)

What it doesn’t do so well

  • Visual feedback when processing. You gotta check the launch window output to see if it’s still busy.
  • Disable buttons when processing. You can there overload the server if you are not patient.
  • Report errors. In corner cases you have to open the log file that are inside the application pacakage. Use the ‘Show Package Contents’ on the application then goto to /Contents/Resources/RailsLogAnalyzerPackage/log folder.
  • Time aggregation. It would be nice to see data aggregated by week/month.
  • Local File Browsing to upload the log file. Currently you need to enter the full path to the file, there is not UI to select a file.
  • Log parsing. Certainly can be greatly improved. Haven’t tried to parse development files. Wouldn’t support custom log files either. Doesn’t support well multi processes logging to the same production.log (which is most cases).

Licenses

Well, I still need to put a complete list of what I use and the implications there off. Mostly Rails which is MIT and the GeoLiteCity.dat from maxmind.com/ that is GPL . Note the GeoLiteCity.dat file is 24mb big, thus at least partly the large size of the application. Also I am using the Flex Charting Trial components, that’s visible when the application is running. My trial is running out in 17days as of this writing. I would like to buy the version of Flex Builder with Charting, but Adobe hasn’t answered yet if buying the license for Windows would grant me usage of the version for OSX, when it comes out.

Performances

The application doesn’t provide the best visual feed back yet. It also takes about 1 minute on my MacBook Pro to load a 20Mb production log file and 13 minutes for a 264Mb production log.

Side notes

  • The database is a sqlite3 and is created in your /Users/daniel/Library/Application\ Support/RailsLogAnalyzer
  • Note the application must be ‘read-write’. Use ‘Get Info’ on the application and ensure in the ‘Ownership & Permissions’ that the application is ‘Read & Write’. This is due to the fact that rails needs to write to the tmp folder and log folder that are embedded in the application it self.

Please provide feedback to daniel@onrails.org. Thanks in advance, Daniel.

Download RailsLogAnalyzer0.1.dmg (18.6MB)
User Guide

Rails Log Analyzer - Rails and Flex with JSON 7

Posted by Daniel Wanja Mon, 15 May 2006 21:15:00 GMT

I started to write a small Rails Log Analyzer that provides some insight on how a given application is used. I’ve just spent three hours so far, so not too much to show, but I have found the integration of Flex with Rails for read-only purpose of the different time series pretty straight forward.

In two words…

RAILS: data.to_json

FLEX: JSON.decode(String(srv.lastResult));

On the Rails side

The controller simply transforms the Hash return by the model into a json textual representation.

the controller
class DataController < ApplicationController

  def overview
    render :text => Hit.overview_data.to_json
  end

end

This is an extract of the method that returns a Hash that contains the time series in an Array.

the model
def Hit.overview_data
    result = {}
    result[:header] = {:period => {:start => Hit.minimum(:time).to_s(:db), :end => Hit.maximum(:time).to_s(:db)}}
    result[:sessions_series] =
           {:by_day => Hit.data_serie(Hit.count(:session, :group => :day, :conditions => 'controller <> "HeartbeatController"'), "sessions by day")    }
    result
  end

On the Flex side

the view

   import com.macromedia.serialization.json.*;

   private function resultHandler(event:ResultEvent) : void
        {
            status = "Loaded. Parsing data...";
            var result:Object = JSON.decode(String(srv.lastResult));
            header = result.header;
            ts = getSerie(result.sessions_series.by_day.data);

  }

    <mx:HTTPService id="srv" url="http://10.37.129.2:3000/data/overview" result="resultHandler(event)" />

The service is invoked by the following actionscript call

        srv.send()

JSON doesn’t support Date objects out of the box, but it’s a nice way to exchange complex data such a Hash and Map between Rails and Flex.