Rails is an open-source framework for developing web applications. Release 2.0, a major update to the Rails framework, was made available in December 2007 and includes a load of new features and fixes. More recently this was followed by Rails 2.1 (available for download here). In this article, I examine some of the major new features in Rails 2.x.
To install Rails 2.x, simply run the following command:
gem install rails -y
The output from the command (Figure 1)shows that the most recent version, Rails 2.1, has been installed. The rails 2.1.0, activesupport-2.1.0, activerecord-2.1.0, actionpack-2.1.0, actionmailer-2.1.0, and activeresource-2.1.0 gems get installed.
Rails Web Services
RESTful web services have additional support, while the ActionWebService gem has been removed from the default package. ActiveResource gem has been added to the default bundle. In Rails 2.0 RESTful routes use a new custom delimiter. For example, a custom action route is defined as follows:
map.resources :catalog, :collection => { :entry => :get }
Before Rails 2.0, the resulting route uses a semicolon (;) to delimit the custom action:
GET /catalog;entry
In Rails 2.1 you use the standard forward slash (/):
GET /catalog/entry
In Rails 2.1, nested REST resources have been replaced with has_one and has_many to define resource associations. For example, associations between resources using the nested resources is defined as follows in config/routes.rb:
map.resources :catalogs do |catalogs| posts.resource :journal posts.resource :edition posts.resources :entries end
In the example, the catalogs resource is mapped to journal, edition, and entries resources. Using has_one and has_many, the associations between resources may be defined as follows:
map.resources :catalogs, :has_one => [:author,:edition],:has_many => [:entries]
Named prefixes in nested resources is no longer required in Rails 2.0. Previously you had to specify a named prefix using the name_prefix option as follows:
map.resources :catalogs do |catalogs| catalogs.resources :entries, :name_prefix => "catalog_" end
In Rails 2.0 named prefix is implicit. For example, the following nested resource provides a helper method catalog_entries_url(catalog_id):
map.resources :catalogs do |catalogs| catalogs.resources :entries end
If you don't want the named prefix to be implicit set name_prefix to nil explicitly.
map.resources :catalogs do |catalogs| catalogs.resources :entries, :name_prefix => nil end
In Rails 2.0 the namespace feature (map.namespace) has been added to the routing resources. Using the namespace feature, an admin interface may be defined like this:
map.namespace(:admin) do |admin| admin.resources :journals, :collection => { :catalog => :get }, :member => { :duplicate => :post }, :has_many => [ :articles, :images ] end
The admin interface generates the admin_journals_url, which points to "admin/journals" and looks for the Admin::JournalsController. Corresponding to the has_many relationship the admin_journal_articles_url, which points to "admin/journals/#{journal_id}/articles" and looks for Admin::ArticlesController, is also created. The "rake routes" task has been added to list all the named routes. A new feature in ActiveResource is that the ability to invoke custom methods has been added. Consider a REST web service that has the standard CRUD-based actions for a Employee class including a custom element method promoted and a custom collection method sorted. The ActiveResource class Employee is defined as follows:
class Employee < ActiveResource::Base self.site = "http://employeeblog.com/" end
The custom methods are invoked using one of GET, POST, PUT, or DELETE. For a collection custom method the get/post/put/delete is invoked on the class and for a element custom method the get/post/put/delete is invoked on an instance of the class. You would invoke the collection custom method sorted with the following URI:
GET /employees/sorted.xml?by=first_name
In the ActiveResource, the collection method sorted is invoked this way:
employees = Employee.get(:sorted, :by => 'first_name')
The custom collection method sorted is specified as the first argument to the get method. Similarly the custom element method promoted is invoked with the following URI:
PUT /employees/1/promoted.xml?position=Manager
In the ActiveResource the custom element method promoted is invoked by specifying the method as the first argument to the put method:
steve = Employee.find(1) steve.put(:promoted, :position => 'Manager')
The ActiveResource find method supports a new option called :from for a custom method or a resource. The first argument to the find method specifies a resource id or the scope (:all, :first, :one). For example, all the recent catalog entries can be found as follows:
Catalog.find(:all, :from => :recent)
ActiveResource also supports custom headers using the headers option. In the following example, every request from Catalog would include the specified header:
class Catalog < ActiveResource::Base headers['X-MyHeader'] = 'steve' end