Flex Dynamic Scaffolding for Ruby on Rails.

No I am not announcing the next killer scaffolding framework but I had a couple of hours available today so I just explored some of the cool features or Ruby On Rails and Flex. The part I was most interested in (today) is dynamic user interface creation and not generating and application like several scaffolding frameworks are doing. So I was able to create a UI that adapts to a given model of a Rails application. Of course I didn’t go as far as I wished, but I thought I could share it with my readers as I usually get valuable feedback. Right now the application doesn’t even manage data. That will be the next step.

20070804_FlexScaffolding.jpg
In a first phase I have adapted RaildRoad Rails model class diagram generation tool to generate an xml definition that the UI would use to create it’s components. I generated an xml model for Sports a sample application provided with Streamlined and Typo a blog server.

The UI generation from the typo model is quite cpu intensive. There is not lazy instanciation of components, when you select the model, all the Tabs, lists and forms are created.

You can run the application and press view source from the context menu. Or you can see the source here. The generated xml can be seen here and here

In a second phase I created a Flex application that generates a UI from the given xml.

I was writing this blog entry while coding, so if you are more curious about how all this works, keep on reading.

Getting the schema of your ActiveRecords

We will generate an xml version of the schema that Flex can use to assemble a UI. So we need to find out info about each ActiveRecord of the application, it’s association with other ActiveRecords, and all it’s attributes. Later on would also be nice to identify the validation rules, so that we can build some validations on the fly without having to do a server round trip for some of the basic validations, (required, length, confirmation, regex). Supporting the various acts_as could also provide lots of functionality that doesn’t need to be coded over and over.

I will use the Streamlined Sports example database to experiment with. Later on we may have a look at Typo a blog server.

Let’s use the community to see how to parse the ActiveRecord. I am now checking out RailRoad (0.4.0) a class diagram generator for Rails. Railroad has the ModelsDiagram class that gather the information we need and then uses the DiagramGraph class to generate a dot format file that in turn is used to generate .svg or .png of the diagram. We are not interested in the dot generation, but will just ‘adapt’ the to_dot method to get the xml we need. So I simply reopened the class and created a new to_dot method as follows:

# http://chadfowler.com/2007/8/3/enumerable-injecting
module Enumerable
  def injecting(s)
    inject(s) do |k, i|
      yield(k, i); k
    end
  end
end

class DiagramGraph

  def to_dot
    return definition.to_xml(:root => 'active_records', :dasherize => false)
  end
  
  #Let organize the data in a way closer to the xml we want to generate.
  def definition
    active_records = {}
    @nodes.each do |node| 
      attributes = node[2].injecting({}) {|accumulator, value| k,v=value.split(" :"); accumulator[k] = v}
      class_name = node[1]
      active_records[class_name] = { :name => class_name, :attributes => attributes, :relations => [] }
    end
    @edges.each do |edge| 
      association_type = edge[0]
      from_class_name = edge[1]
      to_class_name = edge[2]
      active_records[from_class_name][:relations] << {association_type.to_sym => to_class_name}
    end
    active_records
  end  
  
end

The definition method generates a hash map with the information of the model. The to_xml is all that is needed to get an xml version of the data that the map contains.

definition.to_xml(:root => 'active_records', :dasherize => false)

Dynamically Generate a Flex UI

Let looks at the model.

Player has_many Sponsor
Coach has_many Sponsor
Team has_one Coach
           has_many Player
Sponsor has nobody!

The UI generation should be lot smarter and configurable. For now I have taken the approach to create one view for each model. This view shows all the records in a data grid and the detail of the selected record in a form view. All associations of the record are represented by a Tab Navigator. You will notice that this is not very practical for the Typo datamodel. We should have main ActiveRecords that are entry points to the application. Also the depth of the active record view is one association down but could be driven by definition i.e. Team => Coach => Sponsor, similar to the :include option of the find methods. We should be able to flatten a “to one” relation and have all the attributes of the association in the same view than the source ActiveRecord.

You can run the application and view the source.

The main application, FlexScaffolding, adds dynamically a ActiveRecordsView for each ActiveRecord in the model to the Tab navigator. That’s too many tabs for the Typo model…

    private function generateView():void {
          for each (var activeRecord:XML in definition.children()) {
               var arView:ActiveRecordsView = new ActiveRecordsView();
               arView.definition = activeRecord;
               mainView.addChild(arView);
          }        
    }       

The ActiveRecordsView.mxml does the main work of generating the UI. It’s not very big (66 lines), but is also limited for now. I let you check out the source code if you a curious. Next on the list is to map too more data types (ie a “text” ActiveRecord attribute should be mapped to a TextArea, date and datetime should be support). I need to add configuration options where the defaults can be overridden. I need to create a Rails controller that uses the same xml model to expose the data to the view. There shouldn’t be the need to hand code RESTFul controllers. Or maybe the UI should be build from the routes…

That’s all for now. Enjoy!
Daniel Wanja

Posted by Daniel Wanja Sun, 05 Aug 2007 04:31:57 GMT


RailsLogAnalyzer v0.2 for OSX - Faster, Better

Version 0.2 of the RailsLogAnalyzer is still a development version but a great improvement over my first prototype. This version has been rewritten from the ground up and doesn't use a database to store intermediate log file aggreation. RailsLogAnalyzerActionView.gif

Analyzing your log file data. Once the log file is loaded you will see a breakdown of your requests by year, month, and day. Click on the year, month, or day to see the controllers invocations during that period. Click on the controller in the chart to see the method invocations during the selected period. The method are further broken down based on their http methods (get, post, delete, ...). Note: loading a 10Mb production log file with 30000 requests takes about 10 seconds on my MacBook Pro. loading a 250Mb production log file with 530000 requests takes about 2 minutes. loading a 4.5Gb production log file with 11 million request takes about 45 minutes. The data is loaded in memory and must be reloaded once the application is closed. Download it here RailsLogAnalyzer_0.2.dmg (487KB) and let me know your findings at daniel@onrails.org

Posted by Daniel Wanja Wed, 15 Nov 2006 22:05:00 GMT


RailsLogAnalyzer – help wanted

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…

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


Rails Log Analyzer - Rails and Flex with JSON

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 < ApplicationControllerclass 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.

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


.rjs and link_to_function

A neat little trick is that .rjs templates can be used to generate local javascript functions and be invoked without doing a server roundtrip.

playground_controller.rb

class PlaygroundController < ApplicationController
  def rjs
     response.headers['content-type'] = 'text/html'
  end
end

rjs.rhtml

<html>
<head>
  <%= javascript_include_tag :defaults %>  
</head>
<body>
<h1 id='header'>Javascript function test</<html>
<head>
<%= javascript_include_tag :defaults %>
</head>
<body>
<h1 id
=
header>Javascript function test</h1>

<%= link_to_function(‘Add’, ‘add_item()’ ) -%> |
<%= link_to_function(‘Clear’, ‘clear_list()’) >
<ul id=‘list’
/>
<script type=text/javascript>
<= render :partial => functions, :type => rjs >
</script>
<script type=‘text
/
javascript>
<
= # Note: following javascript is run when the page is loaded.
update_page do |page|
3.times { page.call
add_item }
end
%>
</script>
</body>
</html>

The rjs method in the PlaygroundController set’s the content-type as we perform a render of an rjs from within a .rhtml and this seems to change the content-type, so we need to reset it.

_function.rjs

page << 'function add_item() {'
  page.insert_html :bottom, 'list', content_tag('li', 'item', :id => 'list_item' )
  page.visual_effect :highlight, 'list', :duration => 3
page << '}'page << function add_item() {
page.insert_html :bottom, list, content_tag(li, item, :id => list_item )
page.visual_effect :highlight, list, :duration => 3
page << }

page << function clear_list() {
page.replace_html :list, ""
page.visual_effect :highlight, list, :duration => 3
page << }

In the partial function.rjs we insert the function declaration before writing to the page object. This allows us to invoke the additem _ and clear_list _ methods using the link_to_function _ from in the .rhtml file. Note also in the .rhtml file we invoke directly the update_page method to insert three calls to add_item().

The generated html files looks like this

<html>
<head>
  <script src="/javascripts/prototype.js" type="text/javascript"></script>
<script src="/javascripts/effects.js" type="text/javascript"></script>
<script src="/javascripts/dragdrop.js" type="text/javascript"></script>
<script src="/javascripts/controls.js" type="text/javascript"></script>  
</head>
<body>
<h1 id='header'>Javascript function test</h1>

<a href="#" onclick="add_item(); return false;">Add</a> | 
<a href="#" onclick="clear_list(); return false;">Clear</a>
    <ul id='list' />
<script type='text/javascript'>
  function add_item() {
new Insertion.Bottom("list", "<li id=\"list_item\">item</li>");
new Effect.Highlight('list',{duration:3});
}
function clear_list() {
Element.update("list", "");
new Effect.Highlight('list',{duration:3});
}
</script>
  <script type='text/javascript'>
  add_item();
        add_item();
        add_item();
  </script>  
</body>
</html>

Posted by Daniel Wanja Thu, 12 Jan 2006 05:57:00 GMT


Xml Builder

I created the new Playground category on this blog to expose various aspects regarding Ruby On Rails that I am exploring. It may raise more questions than provide answers, but in any case don’t hesitate to jump in and add your 2 cents.

The xml_builder method here after uses the render_to_string method to create some xml structure. The xmlstring could as well have been in a separate .rxml file and and a simple render statement instead of render_as_string could have saved one line of code. But hey, that’s what the playground is for!

class PlaygroundController < ApplicationController
  
  def xml_builder
    xml_string = <<-XML_END
        xml.test do
          xml.language(name)
          xml.description("Rocks!")
        end
    XML_END
    result = render_to_string(:inline => xml_string, :locals => { :name => 'Ruby'}, :type => :rxml) 
    render_text result
  end
  
end

The output:

<test>
  <language>Ruby</language>
  <description>Rocks!</description>
</test>

Posted by Daniel Wanja Tue, 10 Jan 2006 21:10:00 GMT