Riding the Rails, the Rich Internet Way

Here is the sample that I showed at OSCON, a bit updated on the plane flight home, of how ActionStep could integrate simply with Rails.

You can find notes from the OSCON session here.

First we start with requiring actionstep in our application.rb file:

require "actionstep"

Simple enough, now we build a basic controller:

class WindowController < ApplicationController
  def window
    @title = "Welcome to OSCON"
    render
  end
end

It just sets the title instance variable and then you build a asv (ActionStepView) formatted file which describes your ActionStep UI:

NSWindow :main do
  attributes :title => @title,
             :contentRect => {:x => 100, :y => 50, :width => 400, :height => 400}
  execute { application.load(:view1) }
end

A couple of things to note about this. First, the @title is reference just like when using Builder to build XML with Ruby, but here you specify component types (NSWindow) and provide them with an (optional) name (:main). You set the attributes, and then embed this cool execute block. That block is deceptively powerful. In that block you have a root object, with ‘application’ being the application control object, and then you just do dot (.) method invocation and you can pass parameters, and chain the call (root.foo.bar) and that code is actually executed dynamically in ActionStep when it loads the component definition. That example loads an additional component definition (:view1) into the NSWindow component. That in turn calls the view1_controller.rb file:

class View1Controller < ApplicationController
  def hello
    puts "YOU SELECTED PERSON: #{@params["selected_person_id"]}"
  end
  
  def view1
    @names = {"Richard"=>2, "James"=>4, "Dave"=>3}
    @buttons = ["H1", "H2", "H3", "Sy"]
    render
  end
end

Which defines a method (hello) and a rendering method (view1) which sets one variable to be a Ruby hash, and another to be a Ruby array then loads the view1.asv file:

NSView :test do
  attributes :rect => {:x => 0, :y => 0, :width => 200, :height => 400}, 
             :backgroundColor => 0xdddd00
 
  publishes :selected_person_id => execute { names.selectedItem.label }
 
  NSButton :hello do
    attributes :title => "Test", 
               :rect => {:x => 10, :y => 10, :width => 20, :height => 20}
  end
 
  ASList :names do 
    attributes :items => @names, 
               :rect => {:x => 10, :y => 50, :width => 100, :height => 200}
  end
end

Still with me? Cool! This file has lots of nifty things. You have an NSView with has two other components in it (NSButton, NSList). You set the attributes of those controls and name them (:hello, :names). When the template system parses the asv files encounters a component of a given name, it reflects on the controller instance to determine if that name has a corresponding method, which in the case of the NSButton named :hello there is a hello method on View1Controller. That in turn sets the target/action on the button to call that method (via HTTP) when the user clicks on the button. The other piece is the publishes hash. What that does is indicate a named variable that will be published when an action is called (the user clicks on the :hello NSButton). When that occurs, the execute block is called dynamically in ActionStep the root object here being the list (names) and invokes the selectedItem.label methods, returning the currently selected user from the list. Looking back at the View1Controller ‘hello’ method, its referenced just like with any other HTTP param:

  def hello
    puts "YOU SELECTED PERSON: #{@params["selected_person_id"]}"
  end

This is just a basic example, but I have all the pieces build to make this work. I am now working on what is returned from the hello method (like an ‘execute’ block) which would provide for a work flow with round tripping of data and actions. Once that is done I will publish this out a gem for folks to play with.