Posts filed under 'Howto'

Howto: Associations with ext_scaffold (aka ExtJS with Ruby on Rails)

Jonathan Barket kindly compiled a nice howto on leveraging belongs_to associations in ext_scaffold generated user interfaces.

Check it out!

Update: The above link points to a local copy of Jonathan’s article that was pulled from the Google Cache after he restructured his blog sadly dropping the original content.

Related Posts

Add comment February 17th, 2009

Advanced Ext JS Form Controls with Ruby on Rails: ComboBox

The Ext Scaffold Generator Plugin for Ruby on Rails provides a quick start for single-model forms. When proceeding from the generated code to an actual application, one question keeps popping up in no time:

“How can I have a ComboBox in my form populated with choices from some other model to assign a association?”

Say, you have a Post model for blog posts and a Category model, similiar to the one we used in the Treeview example. Each Post belongs to a Category:

class Post < ActiveRecord::Base
  belongs_to :category
  # has attributes: id, title, body, category_id
end
class Category < ActiveRecord::Base
  # has attributes: id, name
end

If we were using just Rails without Ext JS, we would probably have the select form helper build a HTML select field ending up with this sort of code in our view:

<%= select :post, :category_id,
       Category.find(:all).collect {|c| [ c.name, c.id ] },
       { :include_blank => true } %>

Guess what? You can use the exact same code with Ext JS. One of the handy features of Ext.form.ComboBox is the ability to transform an existing HTML select tag into one of Ext’s nice looking controls.

To turn above select field into a Ext.form.ComboBox we can use this form item configuration:

{ fieldLabel:    'Category',
  xtype:         'combo',
  triggerAction: 'all',
  typeAhead:      true,
  forceSelection: true,
  transform:     'post_category_id',
  lazyRender:     true
}

A complete _form_items.html.erb partial (as used with the Ext Scaffold Generator) would then look like this:

<%= select :post, :category_id,
       Category.find(:all).collect {|c| [ c.name, c.id ] },
       { :include_blank => true } %>
<% javascript_tag do -%>
  var <%= "#{form_items}" %> = [
    <%= ext_field(:field_label => 'Title', :name => 'post[title]', :xtype => :text_field) %>,
    <%= ext_field(:field_label => 'Body',  :name => 'post[body]',  :xtype => :text_area) %>,
    { fieldLabel:    'Category',
      xtype:         'combo',
      triggerAction: 'all',
      typeAhead:      true,
      forceSelection: true,
      transform:     'post_category_id',
      lazyRender:     true
    }
  ];
<% end -%>

Recommend Martin Rehfeld on Working With RailsIf this mini-tutorial was useful to you, please consider recommending me on Working with Rails. Thank you!

Related Posts

6 comments February 2nd, 2008

Howto use the Ext JS Treeview (Ext.tree) with Ruby on Rails

ExtJS Tree Sample ScrenshotIn this little tutorial I am going to show you, how to connect a Ext.tree component (from the remarkable Ext JS Javascript framework) to a Ruby on Rails backend.

For starters, we need to download and extract the Ext framework to public/ext in our Rails project folder. We are going to use Rails 2.1.x for this Tutorial. That’s not a particular requirement - but you would have to adapt certain Rails 2 concepts should you be using an older version.

To model a tree in Rails, we are going to use the acts_as_nested_set plugin - install it using script/plugin install acts_as_nested_set now.

To demonstrate the Ext.tree I will be using a Category model, having a root category with recursive sub-categories. We can use the Rails resource generator to initialize all neccessary files for us:

script/generate resource Category parent_id:integer lft:integer rgt:integer text:string

As you can see, each of our categories is just carrying a text attribute to store its name and the required attributes for the nested set (parent_id, lft, rgt).

To use the Category model, we just have to add the acts_as_nested_set decorator to the Category class, so it looks like this:

class Category < ActiveRecord::Base
  # For Rails 2.1: override default of include_root_in_json
  # (the Ext.tree.TreeLoader cannot use the additional nesting)
  Category.include_root_in_json = false if Category.respond_to?(:include_root_in_json)

  acts_as_nested_set
end

We can now create some sample data using script/console (you did run rake db:migrate already, didn’t you? ;-) ):

r = Category.create(:text => 'Frameworks')
r.add_child(c1 = Category.create(:text => 'Ruby on Rails'))
c1.add_child(Category.create(:text => 'Model'))
c1.add_child(Category.create(:text => 'View'))
c1.add_child(Category.create(:text => 'Controller'))
r.add_child(c2 = Category.create(:text => 'Ext JS'))
c2.add_child(c21 = Category.create(:text => 'tree'))
c21.add_child(Category.create(:text => 'TreePanel'))
c21.add_child(Category.create(:text => 'AsyncTreeNode'))
c21.add_child(Category.create(:text => 'TreeLoader'))

On to our CategoriesController. We just need an index method that will initially deliver a (rather static) index.html.erb view. The same method can then be used to provide JSON data to be consumed by Ext.tree like this:

class CategoriesController < ApplicationController
  def index(id = params[:node])
    respond_to do |format|
      format.html # render static index.html.erb
      format.json { render :json => Category.find_children(id) }
    end
  end
end

We are giving the index method a node query parameter (assigned to a id variable) that Ext will later use to dynamically request sub-trees as they are extended in the UI.

Obviously we will have to add more code to our Category model as it currently does not respond to the find_children method. Let’s use the following code to provide it - when no id or zero is given, the root node(s) of the tree will be returned:

  # add to model/category.rb
  def self.root_nodes
    find(:all, :conditions => 'parent_id IS NULL')
  end

  def self.find_children(start_id = nil)
    start_id.to_i == 0 ? root_nodes : find(start_id).direct_children
  end

After starting script/server we can now fire up our browser and request http://localhost:3000/categories.json. Comparing the generated JSON with the format expected by Ext.tree.TreeLoader reveals that each element should supply a boolean attribute called leaf telling the tree if it can be further expanded or is a final leaf of the tree. To keep our controller skinny as we like it, we will add further methods to the Category model enabling its standard to_json method to also supply the required leaf attribute.

  # add to model/category.rb
  def leaf
    unknown? || children_count == 0
  end

  def to_json_with_leaf(options = {})
    self.to_json_without_leaf(options.merge(:methods => :leaf))
  end
  alias_method_chain :to_json, :leaf

Now that we have the complete backend code in place, we just need to put some Ext Javascript into categories/index.html.erb to try the tree live:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>Ext.tree with Ruby on Rails Example</title>
  <%= stylesheet_link_tag "../ext/resources/css/ext-all.css" %>
  <%= javascript_include_tag :defaults %>
  <%= javascript_include_tag "../ext/adapter/prototype/ext-prototype-adapter.js" %>
  <%= javascript_include_tag "../ext/ext-all.js" %>
</head>
<body>
  <div id="category-tree" style="padding:20px"></div>
  <% javascript_tag do -%>
    Ext.onReady(function(){
      // create initial root node
      root = new Ext.tree.AsyncTreeNode({
        text: 'Invisible Root',
        id:'0'
      });
      // create the tree
      new Ext.tree.TreePanel({
        loader: new Ext.tree.TreeLoader({
          url:'/categories',
          requestMethod:'GET',
          baseParams:{format:'json'}
        }),
        renderTo:'category-tree',
        root: root,
        rootVisible:false
      });
      // expand invisible root node to trigger load
      // of the first level of actual data
      root.expand();
    });
  <% end -%>
</body>
</html>

Now it is time to look at the final view (http://localhost:3000/categories). If you use Firebug or something similar you can track the AJAX requests made by Ext.tree to populate the tree dynamically when you expand nodes. Nice, huh?

Recommend Martin Rehfeld on Working With RailsIf you like this tutorial, please consider recommending me on Working with Rails. Thank you!

Related Posts

20 comments January 26th, 2008

Bridging Rails to Amazon SimpleDB using ActiveResource

With SimpleDB, Amazon added the long-awaited database-like service to it’s web services portfolio. A database was the one thing missing for building a complete web hosting stack with Amazon’s services.

Using SimpleDB together with Ruby on Rails was the thing I immediately wanted to try. Some problems had to be dealt with first:

  1. Amazon does not provide any Ruby code for SimpleDB access
  2. SimpleDB has quite some limitations as detailed in a previous post

As Lars Schenk outlines on his Blog at least three different projects on RubyForge are addressing problem number one - only one of them has actual code, though. And that is aws-sdb by Tim Dysinger from Hawaii.

OK, using Tim’s aws-sdb gem, one can get access to SimpleDB using Ruby. But using SimpleDB as a drop-in-replacement for a relational database and connecting Rails’ ActiveRecord to it would require a fairly complex adapter - I am not sure, if this can be done at all, actually.

Looking at Rails, another interface comes to mind: ActiveResource. ActiveResource was written to connect model objects to RESTful web services as their datastore. That sounds like a fit. The APIs are different, but the functionality needed by ActiveResource can be provided by SimpleDB - it’s all just CRUD after all.

All that’s needed would be a adapter, a proxy, a proxy server? Yes, that’s right. As it turned out, it’s not too hard to write one, so here it is :-)

Announcing AWS SDB Proxy

My AWS SDB Proxy is a HTTP proxy server bridging ActiveResource calls from Rails to Amazon’s SimpleDB Web Service allowing it to be used as a storage backend for Rails applications.

The proxy will listen for web service calls initiated by ActiveResource models and forward the requests to SimpleDB using the aws-sdb gem.

Install the AWS SDB Proxy Plugin from RubyForge as usual:

script/plugin install http://rug-b.rubyforge.org/svn/aws_sdb_proxy

Then follow the instructions provided in the README.

Features and Limitations

SimpleDB (and thus AWS SDB Proxy) do not use any pre-defined schema. Every record can potentially have different attributes. SimpleDB also has no data types associated with it’s attributes, all data will be stored as strings.

AWS SDB Proxy adds a special _resource attribute, allowing storage of multiple models within the same SimpleDB domain. Record ids are generated using a SHA512 hash function to make key collisions extremely unlikely.

Recommend Martin Rehfeld on Working With RailsIf you like this plugin, please consider recommending me on Working with Rails. Thank you!

Related Posts

12 comments January 20th, 2008

Ext JS and Rails, how do they get along?

The Ext JS framework got a lot of attention lately as it offers very sophisticated GUI components for web application development. But how does Ext play along with Ruby on Rails, everybody’s favorite development framework?

There is not a lot of documentation around, though some people seem to be using the combo quite successfully (if you take job posts looking for Rails and Ext skills into account).

Googling reveals some Rails plugins aiming at Ext integration, but a closer look exposes them as mostly “vaporware”, abandoned experiments or as not being actively developed.

RailsExt.pngFor my talk on Ext at the Berlin Ruby Users Group, I decided to roll my own. The presentation focuses on Rails scaffolding as a good starting point for playing with Ext. The biggest integration task from a Rails point of view is providing the correct JSON data structures that Ext can process - a task that can be solved in a re-usable way. Passing data around between Rails controllers and views and the actual JavaScript code is another focal point. Download the complete set of slides here.

The presentation features a preview version of my Ext Scaffold Generator, soon to be announced as a official Rails plugin.

[Edit by martin.rehfeld]: The Ext Scaffold Generator has been officially released on 18th January 2008, check out the details.

Related Posts

12 comments January 8th, 2008

Mac Mini Mediacenter in 3.8″ Room Space

I always wanted to have a stylish and versatile media center solution. Oh, and it had to stay out of the way, consuming very little space. As it may well become the center of my digital media world, it should definitely not dominate my living room in a similar way. Here’s what I came up with.

CIMG4440_2_200x124.shkl.JPG

Hardware Ingredients:

Mac Mini fitted behind Cinema Display Software Ingredients:

This definitely is stylish and space-efficient, isn’t it. But where did the Mini go? It’s hiding itself behind the display along with the power supplies and cabling. As you can see, the drive slot remains easily accessible from the side.

Software

The media center software was easily chosen. As I am already running a MythTV backend on a different (Linux) box, MythTV’s frontend had to be installed on the Mini. Fortunately, recent versions are available as pre-build binaries for OSX.

One Caveat: For some reason, the Apple Remote is not working with MythTV since Leopard (Tiger was fine). Looking for a fix, I stumbled across Remote Buddy. This little gem not only circumvents the MythTV/Leopard problem but also gives almost full control to the system using the only the remote. That was ridding me of the need to keep a keyboard and mouse around.

Hardware Assembly

Distancing the Display from the Wall-Mount SystemI started by putting the display at the wall using the VESA adapter and wall-mount system. With this configuration, the distance between wall and display was about 6mm too small to get the Mini behind. So I used longer screws and distanced the display using two hex nuts and a shim.

That way a little over 50mm were available between wall and display - enough for the Mini. I decided to put the mini on the right side with it’s top facing towards the wall. While it is possible to put the Mini on the left side instead, it still has to have its top facing the wall. Otherwise the connectors will not be accessible in a way that the cabling runs through the VESA adapter. And that’s a must to keep the Mini fully hidden behind the display.

To give the Mini a stand, I bolted a wooden strip to the wall. It’s top should be approximately 22.5cm below the display’s top edge. The Mini’s connectors are then at a good height relative to the VESA adapter.

For convenience, I a added another wooden strip at the left side to screen the cabling and power supplies from view. A last slat is attached horizontally just above the lower edge of the display. It provides a nice stand for the power supplies and prevents the cabling from peering below the display.

Roundup

CIMG4440_200x150.shkl.JPGFor about 1.500 EUR (at the time of writing), I build myself a good looking media center solution featuring full HD resolution, digital audio and video and a flexible software stack. While this is a lot of money, other solutions I considered don’t come cheap either. And none of the alternative approaches came close to the minimalistic design of a Cinema Display attached to the wall - even an iMac has a bigger footprint. Continuing down this road and adding a Yamaha Sound Projector to the equation, I was able to get a similarly minimalist configuration for the audio part. I must say, I am very pleased with the result.

4 comments December 13th, 2007


Posts by Category