Rails Dispatch

Rails news delivered fresh

Presented by Engine Yard

You say, “No, not another Rails upgrade!”. While it can be daunting and frustrating to see a new release of software, as it usually means more learning or changing the workflow for what you’ve just grown accustomed to. Unlike what you might think, Rails 3 is a different beast, it’s been remodeled to make your life as a developer easier. So roll up your sleeves and dive through what’s new in Rails 3.

In this article, we’ll go through the main areas of Rails 3 that have seen major improvements. We’ll see how the evolution of Rails into its current mature form makes it easy to accomplish usual tasks, while also packing up new features any serious developer would appreciate.

As we know, Rails 3 is a result of the merge between Rails and Merb. Merb is famous for honoring agnosticism and Rails has a long history of providing rock-solid defaults. We’ll see how Rails 3 hits a sweet spot in being an opinionated and agnostic framework at the same time, resulting in a framework that’s sure to be appreciated by developers of both camps.

This week’s video explains the high-level overview on upgrading to Rails 3. The post goes into more detail on each area.

Though this article covers the aspects that affect an everyday Rails developer, there has been a huge work to make Rails a better framework architecturally. The Rails core components in previous versions were tightly coupled with each other, but with Rails 3, all Rails components share the same API so that anyone can come up with a replacement component.

Dependency Management with Bundler

Though not mandatory, Rails 3 prefers the fantastic Bundler to address dependency with requiring gems and libraries in an application. A Gemfile is used to load all the dependencies required by the application. It can be used to require a Gem from a gems repository, git repository or a local path. Here’s a typical Gemfile located in the root of the generated application:

source "http://rubygems.org/

gem "rails", :git => "http://github.com/rails/rails"
gem "mongoid"

The Gemfile is generated by Rails along side the default application files. According to the web application’s requirements, you can change the file and issue bundle install in the command-line to bundle up all of the gems, ready to be used by the application.

The following boilerplate code generated by Rails, loads up the gems, honoring different Rails environments.

Bundler.require(:default, Rails.env)

What this means is you can have gems in the Gemfile that are specific to a Rails environment. For example:

group :test do 
  gem "rspec-rails", "2.0.0.pre.8"
end

When deploying, Bundler can create a snapshot so the development and production machines run the exact same versions to avoid the possibility of conflict. bundle lock creates a Gemfile.lock file with all of the metadata necessary to replicate the exact pack of gems on different machines. With that set, bundle install uses this file to install all gems.

Bundler was specifically made out of the need to resolve problems around gem version conflicts. I’d definitely recommend giving a read to the article by Andre Arko on RailsDispatch.

To upgrade your Rails 2.3 app, simply grab all instances of config.gem found in your application, and move them into the Gemfile. If you have uses of config.gem in specific environment files, move them into Gemfile groups.

Resources

GemBundler
Managing Gems with Bundler

Scripts

All scripts in the script/* directory have been renamed to rails, or short for script/rails. The framework detects if you’re inside a Rails 3 application and appropriately runs the requested script. Let’s see how they work:

$ rails dispatch

This generates the generic boilerplate code. Once you’re into the directory, it detects that you’re in a Rails application directory so it can run different commands on top. Here is an example:

$ cd dispatch
$ rails generate scaffold Post title:string body:text # You can also use rails g (short for generate)

This should not result in significant code changes, but you may need to update scripts that use the script/* commands directly.

Code Generating

Before Rails 3, each gem that wanted to generate some code would’ve had to create separate generators, hence requiring you to memorize different names. So, for example, RSpec had generators named with the rspec prefix. In Rails 3, you can define different frameworks you would like to base your application on inside the config/application.rb file. Here’s an application that uses Mongoid, RSpec and Haml:

config.generators do |g|
  g.orm              :mongoid
  g.template_engine  :haml
  g.test_framework   :rspec
end

With this in place, Rails would delegate the default commands to proper generators that use defined frameworks. However, if you install the rspec-rails gem, for instance, you will not need to do this manually.

This should not affect your upgrade, except that if you’re using a gem like rspec-rails, you should not generate your models, controller, mailers, and scaffolds using the normal Rails generator commands, instead of the special commands shipped with those gems.

ActiveModel

Developing another ORM (or ODM) for Rails has been a pain in the butt. Not so with Rails 3. Any ORM can be hooked up with ActiveModel to provide a unified interface to the developer easily. ActiveRecord’s core functionality has been abstracted into ActiveModel to support this. That means if an ORM is compatible with ActiveModel’s generic interface, it can take advantage of the same API that Rails uses, allowing greater flexibility to switch ORMs.

Configuring an application to use Mongoid so it uses MongoDB as the database backend is very easy. You can generate a Rails app that skips ActiveRecord by specifying --skip-activerecord in the end. Then, specify mongoid in Gemfile and change the config/application.rb to use Mongoid as its ORM. In this case, the mongoid gem does not register itself as the default ORM, so you will need to tell Rails to use its generators.

As you can see, it becomes very easy for Rails to plug itself in and use different ORMs if it complies. There might be more steps to configuring a different ORM for your application if you wish to use a non-compliant one.

If you’re using something like tableless model in Rails 2.3, you can take the chance to use a simple ActiveModel wrapper and pull in the modules from ActiveModel that you need as activerecord-tabless is deprecated.

Resources

Supermodel
Rails and Merb Merge: ORM Agnosticism

ActiveRecord

ActiveRecord now sports a robust query interface that you’re sure to appreciate. It is backed by ActiveRelation, a Ruby implementation of Relational Algebra. It’s based on a model that allows data representation through relations and operators. The biggest advantage of this overhaul is the fact that a relation object stays that way until you absolutely need the data back from the SQL Database. This allows us to chain methods and lazy load the data from the database. All operations on a relation returns another relation object. This allows us to build a relation and lazy load the data, resulting in better performance through-out.

This migration is also evident in ActiveRecord’s finder API. Rather than chaining different criteria via keys and values, you now use methods on a Relation instance.

@users = User.where(:name => “John”)
=> ActiveRecord::Relation

@desc_users = @users.order(“created_at DESC”)
=> ActiveRecord::Relation

@desc_users.all
=> Array

post = Post.where(:title => “routes”).limit(3).order(“DESC date”)
=> ActiveRecord::Relation

As you can see, ActiveRecord tries its best to keep the query as a Relation object so it can be manipulated further. This means you can chain the object as much as you need and never worry about SQL performance.

Rails 2.3-style queries will continue to work, but you should look at updating your queries to the new API to gain flexibility and improve your code.

Resources

ActiveRelation: ActiveRecord Gets a Facelift

Controllers

Controllers are a major part of the MVC architecture that Rails supports. Before Rails 3, the implementation code of controllers was tightly coupled and there was less abstraction. This meant that ActionMailer couldn’t easily use the parts of ActionController that it had in common. The new AbstractController class moves the core controller ideas (such as rendering, callbacks, and helpers) to a decoupled superclass that other controllers, such as ActionMailer, can easily use.

With that out of the way, one of the big wins of using Rails 3 has to be the pick what-you-want nature of Rails 3 controllers. Instead of writing a raw Rack handler, like the “metals” in Rails 2.3, you can implement a stripped down controller with only the features that you need:

class AjaxController < ActionController::Metal
  def index
    self.response_body = "Hello World"
  end
end

With that in place, your controller would skip the whole stack, giving the application considerable speed gains for performance hogging resources. And if in any case, you want any specific functionality back, you can include it in the controller.

Because AjaxController is still a regular controller, you can route to it in the same standard way that you would route to any normal controller.

It’s worth mentioning that there are already plugins that are adding further functionality to ActionController, taking direct benefit from the hard work done in this realm of Rails. Astaire is one such example, and I know Sinatra peeps are going to love it. :)

If you’re using Metals in Rails 2.3, you will want to update them to use the new Rails 3 style. Let’s use the example Ryan Bates used in his original screencast on Rails Metal when it was introduced:

class ProcessesList
  def self.call(env)
    if env["PATH_INFO"] =~ /^\/processes_list/
      [200, {"Content-Type" => "text/html"}, [`ps -axcr -o "pid,pcpu,pmem,time,comm"`]]
    else
      [404, {"Content-Type" => "text/html"}, ["Not Found"]]
    end
  end
end

And after:

class ProcessesController < ActionController::Metal
  def index
    self.response_body = ``ps -axcr -o "pid,pcpu,pmem,time,comm"`
  end
end
match "/processes_list" => "processes#index"

This should be as fast as Rails 2.3 metals, but more integrated into the general Rails stack.

Resources

Rails and Merb Merge: Plugin API

Routes

Routing syntax, in particular, is of great importance since every Rails developer, be it frontend or backend, has to deal with it in some way. I start reviewing an application by first looking at Routes. It shows the overall structure of the application while also giving enough information to test the application by entering up few URLs in the browser.

Here’s how basic routes would look in Rails 3:

match "welcome" => "welcome#index" # same as: map.connect "welcome", :controller => "welcome", :action => "index"
resources :profiles # same as: map.resources :profiles
root :to => "home#index" # Defines root path so '/' connects to index in HomeController

Let’s move on to some fairly advanced examples, showing the real value of Routes in Rails 3:

match "/articles(/:id)" => "articles#index"

This line has an optional parameter, which is set to nil if not specified. Otherwise, the parameter is available to the action with params[:id].

get "/:id" => "users#show", :id => /\d+/

Rails 3 routes support constraints. This is a wide topic on its own but here’s a basic example. This is a regular expression constraint so that only numbers are allowed for id parameter. Anything other than numbers is ignored. While this particular example has an analogue in Rails 2.3, you can constrain a Rails 3 route by anything in the Request object, and even provide an object that will run arbitrary code using the request object.

scope "admin" do
  resources :articles
end

Scopes allow routes to be defined for controllers in path or in a specific module. This example has articles defined as resources in the admin scope, that means the articles controller is inside the admin directory in our application

get '/ads' => MySinatraAdServer

Much to our surprise, the endpoint of any route in Rails 3 can be a Rack application. That means your application can be a bunch of small applications residing in one Rails app, all handled by Routes.

Upgrading your routes file is a manual process, but the Rails core team will continue to support the old mapper at least until Rails 3.2, so you can do it at your leisure.

Resources

The Lowdown on Routes in Rails 3
The Powerful New Rails Router

More Emphasis on Security

Rails 3 puts more emphasis on security, now that cross site scripting attacks are handled by default. Cross site scripting attacks are one of the common methods of compromising a website. The attacker injects a Javascript into the HTML using text fields. When this Javascript is unescaped, it means that the code is executed.

Rails 2 had a helper to escape the data, but developers often tend to forget using this helper. Thus, the Rails core team decided that all returned data be escaped by default, unless otherwise explicitly specified. You can mark a string safe by calling raw helper on the returned data.

This is the only application-wide change that you must make in order to upgrade to Rails 3. However, you only need to take action if you are creating HTML snippets outside of your templates (for instance, in helpers or your models), which is a generally bad practice.

Your code before:

def boxy(string)
  out = "<div class='box'>"
  out << h(string)
  out << "</div>"
end
<%= boxy(@post.box_content) %>
<h1><%=h @post.title %></h1>
<div class='entry'>
  <%=h @post.body %>
</div>

And after:

def boxy(string)
  out = "<div class='box'>".html_safe
  # if this is not safe, it will be escaped when 
  # concatted onto the safe String
  out << string
  out << "</div>".html_safe
end
<%= boxy(@post.box_content) %>

<h1><%= @post.title %></h1>
<div class='entry'>
  <%= @post.body %>
</div>

Note that you could eliminate the error-prone uses of h, and that the boxy helper will automatically handle escaping only unsafe Strings in Rails 3.

Resources

Secure by Default: Rails 3 Security Strategy

Unobtrusive Javascript

People hated the fact that Rails injected obtrusive Javascript right inside the code. Moreover, the injected code was Prototype specific, which meant Rails had no built-in functionality to support other Javascript libraries. With Rails 3, all that is history.

View helpers such as link_to_remote follow HTML5 specification to provide data-* attributes that can be picked up by any Javascript library and worked on, from there onwards. This makes things easier for different libraries to pick up the attributes and do their magic, not to mention that the generated HTML is back to sanity as well.

Additionally, link_to_remote(post.title, post) has become link_to(post.title, post, :remote => true) since it is exactly identical to the regular link_to, but adds a single HTML attribute to the resulting a tag that rails.js can pick up.

Rails ships with a version of rails.js that works with Prototype, but you can replace it easily with the version hosted at the jquery-ujs project on Github under the rails user. The Rails team will maintain the jQuery version, which is now a first-class option. Additionally, the abstraction into HTML5-esque attributes means that any Javascript library can pick up where Rails left off.

If you’re using link_to_remote, the upgrade steps involve updating these helpers to use the new :remote => true syntax. If you need the semantics of the generated code for some reason, you can use the prototype_legacy_helper plugin, also maintained by the Rails core team in the rails user on Github.

Resources

jQuery-UJS
Prototype Legacy Helper

Block Helpers

In Rails 2.3, block helpers, such as form_for worked by using &lt;% %>. This was a bit confusing, because they emitted content to the page, so you’d expect them to use &lt;%= %>.

Additionally, to make your own block helper, you needed quite a few incantations:

def boxy(&block)
  content = content_tag(:div, capture(&block), :class => "box")

  if block_called_from_erb?
    concat(content)
  else
    content
  end
end

In Rails 3, you use &lt;%= %> for block helpers, which vastly simplifies the process of building block helpers yourself.

def boxy(&block)
  content_tag(:div, capture(&block), :class => "box")
end

Rails 3.0 will continue to work with the old syntax, but it emits deprecation warnings, and the core team will remove the old syntax in Rails 3.1.

Conclusion

As you can see, Rails 3 brings lots of big changes, but there is more to it than meets the eye! The refactorings and more emphasis on the backend has led to cleaner code altogether. There is now an extensive API for creating plugins, which means that all of the Rails core components are nothing more than plugins themselves. Moreover, ActionMailer has seen an overhaul in its API, which is now a wrapper to the fantastic mail gem by Mikel Lindsaar.

It’s also worth noting that with the exception of the new security changes to secure your site against XSS attacks by default, there are few mandatory changes that you must make to your models, views, or controllers to upgrade to Rails 3.0. There are a number of deprecations, and you’ll want to take care of those sooner rather than later, but you can freely upgrade to Rails 3 without needing to rewrite large chunks of your application.