Friday, January 2, 2009

This Week in Edge Rails

Posted by Mike Gunderloy

December 27, 2008-January 2, 2009

Happy New Year! Apparently the Rails core team was not doing too much partying to end the old year: we had 35 commits hit the edge tree, and some of them involved very substantial work. Here’s my weekly overview of some of the most visible and significant changes.

Optimization of respond_to

In some of the first fruits of the Rails-Merb team merger, Yehuda Katz took a look at the respond_to method, which is of course heavily used in many Rails applications to allow your controller to format results differently based on the MIME type of the incoming request. After eliminating a call to method_missing and some profiling and tweaking, he reports an 8% improvement in the number of requests per second served with a simple respond_to that switches between three formats. The best part? No change at all required to the code of your application to take advantage of this speedup. commit commit

If you want a preview of what else to expect from Rails 3, you might want to dip into Yehuda’s own fork of the Rails tree; I’ll be covering these changes as they make their way back into the master copy of edge Rails.

Dynamic Scopes for Active Record

You know about dynamic finders in Rails (which allow you to concoct methods like find_by_color_and_flavor on the fly) and named scopes (which allow you to encapsulate reusable query conditions into friendly names like currently_active). Well, now you can have dynamic scope methods. The idea is to put together syntax that allows filtering on the fly and method chaining. For example:


Order.scoped_by_customer_id(12)
Order.scoped_by_customer_id(12).find(:all, 
  :conditions => "status = 'open'")
Order.scoped_by_customer_id(12).scoped_by_status("open")

There’s some further discussion of this over on Ryan Daigle’s blog. commit

Other Active Record Updates

There were a few changes going on in Active Record this week. A trio of commits cleaned up some behavior of associations when the :primary_key option is specified. commit commit commit

On another front, ActiveRecord::Base#new_record? now returns false rather than nil when confronted with an existing record. While there was some discussion of the wisdom of this change, the consensus seems to be that it can’t hurt and might make things less surprising for some developers. commit

HTTP Digest Authentication

Rails now has built-in support for HTTP digest authentication. To use it, you call authenticate_or_request_with_http_digest with a block that returns the user’s password (which is then hashed and compared against the transmitted credentials):


class PostsController < ApplicationController
  Users = {"dhh" => "secret"}
  before_filter :authenticate
  
  def secret
    render :text => "Password Required!"
  end

  private
  def authenticate
    realm = "Application"
    authenticate_or_request_with_http_digest(realm) do |name|
      Users[name]
    end
  end
end

commit

Multiple Conditions for Callbacks

When using Active Record callbacks, you can now combine :if and :unless options on the same callback, and supply multiple conditions as an array:


before_save :update_credit_rating, :if => :active, 
  :unless => [:admin, :cash_only]

commit

Testing and continuous integration

A little flurry of activity cleaned up some loose ends in our testing strategy for Rails itself. This included not running symlink tests on Windows, adding test coverage to Rails::TemplateRunner, removing some assumptions in various tests, and getting the FCGI and sqlite2 tests working again. This is all part of a longer-term effort to make the Rails continuous integration server more useful moving forward. As you’ll see if you peek at the current build status, we’re not quite there yet, but we’re getting close.

By the way, if you want to set up your own CI server for Rails, there are instructions right in the source code.

Code Comments for Metaprogramming

One side effect of the changes to respond_to is that people really liked the inline comments that make the intent of the class_eval code clear. As a result, we now have similar comments throughout the Rails source code. For example:


def #{method_name}     # def year
  time.#{method_name}  #   time.year
end                    # end

If you’re just using Rails, you’ll never see these comments – but if you’re helping to maintain and improve the framework, you’ll appreciate them. commit