devxlogo

A Flickr-based Introduction to Ruby on Rails 2.0

A Flickr-based Introduction to Ruby on Rails 2.0

nitially released in 2004, the Rails web application framework became the perfect killer application for the Ruby language and an extremely powerful tool for rapidly developing web applications based on the CRUD (Create, Read, Update, Delete) pattern. At the end of 2007, version 2.0 of the now popular web application framework was released. Although not as groundbreaking as the first release, Rails 2.0 still offers many innovations that make developing with it more productive and fun.

This article describes how to get started with Rails 2.0, explores some of the most prominent new features, and uses them to show how to build a simple, but visually attractive, web application. Instead of the standard “create a blog in 15 minutes” example, the application demonstrated here is a useful and immersive photo browser for the popular Flickr photo sharing website.

Figures 1 to 4 show screenshots of the photo browser (called RailTrackr), and you can see a screencast of it here.


Figure 1. RailTrackr Screenshot 1: Rails 2.0 Flickr Browser.
 
Figure 2. RailTrackr Screenshot 2: Floridapfe Photosets.


Figure 3. RailTrackr Screenshot 3: Floridapfe “Snowy Owl” Photoset.
 
Figure 4. RailTrackr Screenshot 4: Lemur Photo from “Snowy Owl” Photoset.

The RailTrackr source code is attached to this article, and it will be kept up-to-date at the author’s website. You can download it and use it as a reference while reading the rest of the article. Given the amount of features the application offers, this article will show only the relevant snippets extracted from code.

Installation and Basic Setup
The first thing you have to do is install the Rails 2.0 framework and create a basic application scaffold to verify that everything has been setup properly. If the Ruby language and RubyGems, the standard packaging system for Ruby libraries, are not already installed on your system, refer to the Ruby and RubyGems web sites for further installation information. Also, check out Pastie, a tool that checks if your applications based on Rails 1.x are ready to be migrated to the new version.

Once you have these ready to go, you can install Rails 2.0 using the same procedure as for the previous version of the framework. Then open a terminal and enter this command:

gem install rails --include-dependencies

You can also launch the gem command with explicit references to the required libraries and verify that it downloads the correct packages from the Internet:

$ sudo gem update actionmailer actionpack activerecord activesupport$ sudo gem install activeresource$ sudo gem update rails$ ruby -vruby 1.8.6 (2007-09-24 patchlevel 111) [universal-darwin9.0]$ gem -v1.0.1$ rails -vRails 2.0.2$ gem list --local*** LOCAL GEMS ***actionmailer (2.0.2, 1.3.6, 1.3.3)actionpack (2.0.2, 1.13.6, 1.13.3)actionwebservice (1.2.6, 1.2.3)activerecord (2.0.2, 1.15.6, 1.15.3)activeresource (2.0.2)activesupport (2.0.2, 1.4.4, 1.4.2)rails (2.0.2, 1.2.6, 1.2.3)... other libraries here ...

As you can see, you can keep the previous version of Rails alongside with the new one, to facilitate the transition of existing applications built upon previous Rails versions. The above example has both Rails 2.0.2 and two previous versions of Rails 1.2. (The Ruby on Rails download page describes additional ways of installing it.)

To verify that everything is working correctly, you can generate a scaffold for a new web application with this command:

rails testappcd testapp/script/server 

Open your browser at the URL http://localhost:3000 to verify that you are using the latest version of Rails. You should see a welcome screen for your newly created Rails 2.0 application like the one shown in Figure 5. If you don’t, check the RAILS_GEM_VERSION in the testapp/config/environment.rb file.

 
Figure 5. The Welcome Screen of a Newly Created Rails 2.0 Application.

The RailTrackr Application
RailTrackr, the visually rich, web-based Flickr photo browser, will demonstrate some notable Rails 2.0 capabilities. You can launch the sample application now by downloading the source code attached to this article and launching it with the traditional script/server command. Since the application uses the Flickr APIs to load photos, you have to request an API key from the Flickr services site and type it into the flickr_helper.rb file bundled with the source code.

The application provides a way to navigate through Flickr users, their photosets, and the photos contained within them. It therefore defines three entities: FlickrUser, Photoset, and Photo. In the application domain, a FlickrUser may have many Photosets, and each Photoset may have many Photos. These will be the Ruby models for RailTrackr.

Rails 2.0 Goes the REST Way
One of the major new features of Rails 2.0 is its complete “RESTfulness.” It is now extremely easy to publish resources according to the REST (Representational State Transfer) APIs. In fact, you can expose every Rails model?or more correctly, every resource of your application?as an object that you can manipulate, access, and modify using the basic commands of the HTTP protocol (GET, POST, PUT, DELETE). These commands map directly to CRUD (Create, Read, Update, Delete) operations on the exposed resource.

REST support was already available in Rails 1.x, but it is now more integrated and simpler to use. To expose a resource according to the REST principles, you only have to define a Rails controller with the right methods, as shown in Table 1 for the Photo resource.

Table 1. The Methods that a REST Controller Must Provide
Controller MethodCRUD OperationSample URL
indexRetrieve all photosGET /photos
showRetrieve photo with ID 123GET /photos/123
newRetrieve a new photo to edit itGET /photos/new
editRetrieve existing photo with ID 123 to edit itGET /photos/123/edit
createCreate (store) a new photoPOST /photos
updateUpdate existing photo 123PUT /photos/123
destroyDelete existing photo 123DELETE /photos/123

The updated Rails 2.0 scaffold generator that RailTrackr uses always creates controllers compliant to the Table 1 scheme. Launching the command script/generate scaffold Photo will create the following files and elements:

  • A RESTful controller (photos_controller.rb)
  • An ActiveRecord model (photo.rb) that will be replaced in the sample application with a proxy for the Flickr APIs
  • The associated views (edit.html.erb, index.html.erb, new.html.erb, show.html.erb)
  • A resource mapping inside the routing file routes.rb
  • The usual set of tests, fixtures, and database migration files

Unlike previous Rails versions, the controller name is now automatically pluralized, to reflect resource addressing in REST URLs more properly. Moreover, the view files that are created have completely new name patterns.

Chameleonic Views
Rails 2.0 extends the existing support for multiviews to embrace templates. Each resource can be accessed and manipulated in a variety of different formats, each of them rendered by a particular engine. Views use the filename syntax [name_of_the_view].[format].[engine]. For example, a file called show.html.erb would:

  1. Represent the view associated to the show method of its controller;
  2. Produce html output; and
  3. Use the erb (Embedded Ruby) engine. (Erb is the default engine that was used to create dynamic HTML views in previous Rails versions.)

Following the same pattern, show.csv.erb and show.atom.builder templates map the same view to erb-generated CSV (comma separated values) content and to an ATOM syndication feed generated by the XML builder engine, respectively.

Controllers can use the request.format and respond_to functions respectively to change the rendering format and to forward the execution flow to the appropriate template. If unspecified, Rails uses the mimetype of the requested resource to detect the right template. The following listing shows how the controller method can return a list of photos either as an HTML page or as an XML file:

class PhotosController < ApplicationController  # GET /photos  # GET /photos.xml  def index    @photos = # load photos from Flickr    respond_to do |format|      format.html # index.html.erb      format.xml  { render :xml => @photos }    end  endend

RailTrackr renders photos as JPG binary images whenever the user requests a specific image. To accommodate this requirement, you have to change the show method of the controller as follows:

class PhotosController < ApplicationController  # ... omissis ...  # GET /photos/1.jpg  def show    @photo = Photo.find(params[:id])    respond_to do |format|      format.jpg    end  endend

Then you have to create the view file views/photos/show.jpg.erb, which will output the binary contents of the image:

<%= @photo.image_data %>

The model object @photo has an attribute called image_data that contains the binary contents of the image itself.

In addition to the default formats (HTML, XML, ATOM, etc.), you can also create custom formats by modifying the file config/initializers/mime_types.rb and using the Mime::Type.register_alias function. Here is a possible configuration that illustrates this feature:

# Bind jpeg mime-type to the format.jpg formatMime::Type.register_alias "image/jpeg", :jpg# Bind the richtext mime-type to the format.rtf formatMime::Type.register "text/richtext", :rtf# Bind an additional formatter to the html mime-type, to be used# for mobile devices with a limited displayMime::Type.register_alias "text/html", :mobile

The last line fulfills the typical requirement of having two different HTML renderings: one for browsers and one for mobile devices with limited display capabilities. A controller can then choose the appropriate rendering depending on some request parameters or HTTP headers. Here is an example of that:

class PhotosController < ApplicationController  before_filter :detect_mobile_device  def index    @photos = # load photos from Flickr    respond_to do |format|      format.html   # index.html.erb      format.mobile # index.mobile.erb    end  end  ... omissis ...  def detect_mobile_device    if request.env['HTTP_USER_AGENT'] &&      request.env['HTTP_USER_AGENT'][/BlackBerry/]      request.format = :mobile    end  endend

This tutorial describes in more detail how to use custom formats to support mobile devices.

Nested Resources to Build Expressive URLs
At this point, you've learned about RailTrackr's capability to serve resources using REST URLs, such as:

  • http://yoursite.com/flickr_users/max: Show details for Flickr user max
  • http://yoursite.com/photosets/123: Show photoset with ID 123
  • http://yoursite.com/photos/456/edit: Edit photo with ID 456

In fact, if you take a look at the basic resource mapping in the routes.rb file, you'll notice that the scaffolding commands have created the following entries:

ActionController::Routing::Routes.draw do |map|  map.resources :photos  map.resources :photosets  map.resources :flickr_users  # default mapping  map.connect ':controller/:action/:id'  map.connect ':controller/:action/:id.:format'end

Rails 2.0 offers a way to further improve URLs and make them more readable. Just like the way you configure a one-to-many relationship in ActiveRecord models, you can now declare mappings between resources and use URLs like these ones to navigate across the references:

  • http://yoursite.com/flickr_users/max/photosets: Show all max's photosets
  • http://yoursite.com/photosets/123/photos: Show all photos within photoset 123

These URLs have more natural names that are easier to remember, and activating them requires just two steps. First, change the routes.rb to reflect the following contents:

ActionController::Routing::Routes.draw do |map|  map.resources :flickr_users  map.resources :flickr_users, :has_many => :photosets     map.resources :photosets, :has_many => :photos  # default mapping  map.connect ':controller/:action/:id'  map.connect ':controller/:action/:id.:format'end

Then modify your controller by declaring resource mapping between users and their photosets as follows:

class PhotosetsController < ApplicationController  before_filter :load_user    # Given a sample url like site.com/flickr_user/max/photosets ,  # params[:flickr_user_id] automagically points to 'max'  def load_user       @flickr_user = FlickrUser.find(params[:flickr_user_id])  end    # GET /photosets  def index    @photosets = Photoset.by_user(@flickr_user)[0..50]    respond_to do |format|      format.html # index.html.erb    end  endend

The before_filter statement ensures that the user is loaded before accessing his photosets. The flickr_user_id variable is automatically populated by Rails using a naming convention from the name of the parent resource (flickr_user).

You can use this nested resource mapping not only within controllers but also to generate URLs in your views. Suppose the @flickr_user points to the user with ID max and the @photoset to one of his photosets with ID 123, all the following statements can be legally used within views to generate URLs that you can associate to linking functions such as link_to:

  • flickr_user_photosets_url(@flickr_user) will point to www.site.com/flickr_user/max/photosets (list of all the user's photosets)
  • flickr_user_photoset_url(@flickr_user,@photoset) will point to www.site.com/flickr_user/max/photoset/123 (details of one specific photoset)
  • photoset_photos_url(@photoset) will point to www.site.com/photosets/123/photos (all the photos within photoset 123)

Looking at Performance
The user-facing side of RailTrackr is now in place. However, being a resource-intensive application that downloads and processes many photos, performance becomes critical to delivering a smooth experience.

Rails 2.0 contains various improvements to overall performance and supports advanced techniques to speed up the application. Among them, the most interesting ones are:

  • JavaScript bundling
  • Cookie-based sessions
  • Domain multi-targeting
  • Query caching

JavaScript Bundling
JavaScript bundling is the capability to bundle together all the JavaScript files of your application (or part of them) into a single file before sending it to the client. Nowadays, web applications use many JavaScript libraries to provide a better user experience and rich visual effects. The downside is degraded performance because the browser has to perform multiple requests to retrieve every single JavaScript library. By bundling all of them together, Rails 2.0 can speed up the delivery of JavaScript files since the browser needs to perform only one request.

RailTrackr is no different; it requires a lot of JavaScript files to provide its fancy effects. For comparison, Figure 6 shows the time required to access the page that contains all the photosets for a given user without JavaScript bundling and Figure 7 shows how long it takes with JavaScript bundling. Both times are reported by Firebug.


Figure 6. Downloading Lots of JavaScript Libraries Without Bundling.
 
Figure 7. Time Saved When the Same JavaScript Libraries Are Bundled in a Single File.

Activating JavaScript bundling is dead simple. You need only add the :cache attribute to the javascript_include_tag statement in your view templates, as follows:

<%= javascript_include_tag 	"prototype" , "effects" , "dragdrop" , "instant" , "reflex" , 	:cache => "railtrackr" %>

You can apply the same technique to stylesheet bundling, acting on the stylesheet_include_tag command.

Bundling is enabled when Rails is in production mode and disabled during development, to avoid unnecessary complication to the developer's debugging sessions (and life in general).

Cookie-based Sessions
Another performance improvement results from Rails 2.0 storing session data in browser cookies instead of using the database or temporary files on the server. This places all the state information on the client, allowing the application to behave statelessly?performance can then be improved by horizontal scalability. When necessary, you can add more servers, each one hosting the same exact application, to increase throughput without having to worry about maintaining session data in sync across replicas. However, you should avoid this solution when critical data are stored in session or when session is used to store very big objects, even if Rails uses encryption to store session data in cookies. The reason is the browser sends cookies back to the server for every request (so they have to remain lightweight).

Domain Multi-targeting
Modern browsers usually open only two concurrent connections toward every domain they're accessing. Since the pages of RailTrackr contain lots of images, this may result in a performance impact because of the browser queuing all the requests. With the following statement you can instruct Rails to activate domain multi-targeting and generate URLs for static resources distributed across multiple domains (four by default, whose name is created replacing the %d symbol with the numbers 0 to 3):

ActionController::Base.asset_host = "static%d.yoursite.com"

The browser will then download eight images concurrently instead of two. To activate this change, you have to configure DNS aliases on your domain first.

Query Caching
Lastly, Rails 2.0 contains other performance improvements specific to the database layer, such as query caching, to prevent executing the same query multiple times while processing a single request.

What About the Database?
The sample code is focused only on the changes that Rails 2.0 brought to the user-facing side of application development. It discounts entirely models and database mapping in favor of remote API calls to the Flickr Service. However, the new Rails version contains a whole set of changes to its backend and to the popular ActiveRecord persistence layer. These are beyond the scope of this article, but here are some brief descriptions of the most important changes:

  • Sexy Migrations: The syntax that defines database migrations is now more readable and easier to write.
  • Foxy Fixtures: Test fixtures have been simplified and can reference themselves by name instead of using record IDs as before.
  • ActiveResource: If a remote site exposes some resources through RESTful APIs, you can create a fully working model (with the same functionalities as an ActiveRecord model) by simply extending the ActiveResource class and declaring a site attribute that points to the remote site. As you have seen in this article, Rails 2.0 is fully compatible with REST, so it is trivial to connect remote Rails sites into a single distributed model. (Click here for a screencast that shows the details of ActiveResource).

What Have You Learned?
This article explored the changes Rails 2.0 brought to the web development world. It focused mostly on user-facing aspects, providing full REST support to your application and keeping an eye on performance. It also provided a glimpse at the changes that have occurred to the Rails backend.

Among the smaller features Rails 2.0 introduced are improved debugging thanks to the integration of the ruby-debug library, improved JSON support, improved builders for RSS feeds, namespaced routes, HTTP Basic authentication support, and improved security.

For further exploration of these and other features, check out the links provided in the Related Resources section in the left column, or just play around with RailTrackr and its source code. Remember, updates will be regularly released at battlehorse.net.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist