Rails Dispatch

Rails news delivered fresh

Presented by Engine Yard

This week, we’re going to cover several Rails plugins that demonstrate some of the new features in Rails 3. In the screencast, we will show you how to use them. In the post, we’ll go into more detail about how they work internally.

Responders

Github Repository | Blog posts

Rails 3 introduces two new methods at the controller level, the class method respond_to and the instance method respond_with, aiming to encapsulate how our applications behave. Let’s consider both index and create actions in a Rails 2.3 controller:

class UsersController < ApplicationController
  def index
    @users = User.all
    
    respond_to do |format|
      format.html
      format.xml { render :xml => @users }
    end
  end

  def create
    @user = User.new(params[:user])

    respond_to do |format|
      if @user.save
        flash[:notice] = "User was successfully created"
        format.html { redirect_to @user }
        format.xml  { render :xml => @user, :status => :created, :location => @user }
      else
        format.html { render "new" }
        format.xml  { render :xml => @user.errors, :status => :unprocessable_entity  }
      end
    end
  end
end

As you add others controllers to your application, is quite likely that the respond_to blocks for different controller are quite familiar. With that in mind, in Rails 3, we could replace this controller by the following:

class UsersController < ApplicationController
  respond_to :html, :xml

  def index
    @users = User.all
    respond_with @users
  end

  def create
    @user = User.new(params[:user])
    flash[:notice] = "User was successfully created" if @user.save
    respond_with @user
  end
end

The respond_with method is a simple wrapper to an object called responder. A responder is any object that responds to call and receives the current controller and the given resource as parameters. Rails ships with a default responder implementation called ActionController::Responder which implements exactly the same logic as scaffold.

To understand how powerful this abstraction is, let’s imagine we have a medium-sized application at our hands. This application also has an admin panel with 15 to 20 controllers. Now imagine that, at some point, you decide that it would adapt better to your workflow if, after creating or update a resource, it redirected to the collection (index) page instead of the show view.

In Rails 2.3, your only option was to go through all controllers and change all create and update action. However, in Rails 3, if you are using responders and respond_with, you just need to configure it and change the behavior all across the board.

The responders gem is nothing more than a few modules to include and extend the way your application responder works. Right now, in version 0.6.1, it ships with two modules: FlashResponder and HttpCacheResponder.

Remember the create action above? When using responders, you may have notice that, even though you removed all the respond_to blocks, the flash messages are repeating from one controller to another. The FlashResponder changes this by automatically setting a flash message if an entry in the I18n backend is found. By default it ships with these translations, which are copied to your application on install:

en:
flash:
  actions:
    create:
      notice: '%{resource_name} was successfully created.'
    update:
      notice: '%{resource_name} was successfully updated.'
    destroy:
      notice: '%{resource_name} was successfully destroyed.'
      alert: '%{resource_name} could not be destroyed.'

If you want to customize a flash message for a specific controller, let’s say the create action for the users controller, you can do:

en:
flash:
  users:
    create:
      notice: 'Welcome! We are sure you are going to love our web app!'

And most of all, it’s I18n ready! On other side, the HttpCacheResponder, as the name says, adds Last-Modified to responses and reads If-Modified-Since headers on API requests. In this case, if the client is accessing a resource it has cached and the cache is not stale, the server responds with status 304 skipping the whole resource rendering.

There is more you can do with the responders gem and, in case you are interested, you can learn more in the README. For now, we will keep focused on the Rails 3 integration side.

Since the responders gem brings all this goodies to respond_with, it would be a pity if, whenever we want to use it, we needed to scaffold a new controller and replace all respond_to blocks by respond_with in all actions manually. For this reason, the responders gem provide a responders controller generator which uses respond with in the controller by default. Not only that, the responders gem can successfully hook into Rails scaffold and change it to always use the new responders controller.

In Rails 2.3, if you used rspec or datamapper, you may remember it provided specific generators, like rspec_scaffold and dm_model. This always led to a frustrating experience because sometimes a developer called script/generate scaffold User name:string just to find out that he should use script/generate rspec_scaffold User name:string instead. In Rails 3, no longer!

All Rails 3 generators provides several hooks allowing you to change scaffold, model and several other generators to adapt to your workflow. For example, let’s start a fresh application and scaffold a new resource:

$ rails foo
$ cd foo
$ rails g scaffold User name:string
        invoke  active_record
        create    db/migrate/20100526195347_create_users.rb
        create    app/models/user.rb
        invoke    test_unit
        create      test/unit/user_test.rb
        create      test/fixtures/users.yml
         route  resources :users
        invoke  scaffold_controller
        create    app/controllers/users_controller.rb
        invoke    erb
        create      app/views/users
        create      app/views/users/index.html.erb
        create      app/views/users/edit.html.erb
        create      app/views/users/show.html.erb
        create      app/views/users/new.html.erb
        create      app/views/users/_form.html.erb
        invoke    test_unit
        create      test/functional/users_controller_test.rb
        invoke    helper
        create      app/helpers/users_helper.rb
        invoke      test_unit
        create        test/unit/helpers/users_helper_test.rb
        invoke  stylesheets
        create    public/stylesheets/scaffold.css

Each invoke command above is a hook and the value showed just after is the default. So the :orm hook, uses :active_record. The :test_framework one, uses :test_unit and then it goes. If you notice, there is also a hook called :scaffold_controller. This is the hook the responders gem hooks into to replace the scaffold controller.

Creating a generator hook is quite straight-forward mainly because Rails 3 generators plays very well with inheritance. For example, the responders gem generator looks just like this:

require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'

module Rails
  module Generators
    class RespondersControllerGenerator < ScaffoldControllerGenerator
      def self.source_root
        @_source_root ||= File.expand_path("templates", File.dirname(__FILE__))
      end

    protected

      def flash?
        !ApplicationController.responder.ancestors.include?(Responders::FlashResponder)
      end
    end
  end
end

It simply requires the rails generator, inherits from it and overwrite the source root method so it uses the new templates inside the responders gem. The flash? method is just a helper using inside templates (in Rails 3, generators templates are evaluated in the generator context).

To hook a generator into Rails scaffold, all you need to do is to create a Rails::Railtie, which was also introduced in Rails 3. The responders gem railtie is only a few lines of code:

module Responders
  class Railtie < ::Rails::Railtie
    config.responders = ActiveSupport::OrderedOptions.new
    config.generators.scaffold_controller = :responders_controller

    initializer "responders.flash_responder" do |app|
      if app.config.responders.flash_keys
        Responders::FlashResponder.flash_keys = app.config.responders.flash_keys
      end
    end
  end
end

The first line sets an instance of ActiveSupport::OrderedOptions at config.responders. This allows you to do the following inside your Rails::Application at config/application.rb:

module Rails
  class Blog < Rails::Application
    config.responders.flash_keys = [:success, :failure]
  end
end

The second line is where the generator configuration happens. In case you are wondering, the config.generators object available in Rails::Railtie is exactly the same object as the one in our Rails::Application. That said, any gem can configure and manipulate the whole generators workflow in the application.

While this is amazingly amazing, you may be asking: “what if a gem changes my generators to use defaults that I don’t want?”. If you are having this doubt, you need treatment because this is a famous Rails 2.3 syndrome. Luckily, the treatment is: continue reading.

The gems environment had a significant change from Rails 2.3 to Rails 3. In Rails 2.3, gems are loaded during or after your application initialization. This means that, if any gem was changing some configuration value, it would overwrite the value you chose inside your application and you needed to move the configuration to initializers, inside controllers or somewhere else.

In Rails 3, gems are loaded before your application initializes. This is quite easy to notice if you look at the config/application.rb file:

require File.expand_path('../boot', __FILE__)

require 'rails/all'

# If you have a Gemfile, require the gems listed there, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(:default, Rails.env) if defined?(Bundler)

module Foo
  class Application < Rails::Application

By requiring gems earlier, Rails 3 can give more power to gems because we are sure that the final configuration value will be set by the application developer. The official API for configuring a Rails application is a Rails::Railtie as we just saw above. There is much more you can do inside a railtie, like configuring the middleware stack or any other Rails framework. Since all Rails frameworks (like Active Record, Active Resource, Action Pack…) are also railties, they are a great source of knowledge in case you are interested in learning more.

Simple Form

Github Repository

Scaffold customization is a feature which took much attention in Rails 3. After all, it plays an important role in agnosticism. We don’t want to be productive only with Active Record, Test Unit and Erb, but with whatever tool we choose. While the responders gem customize scaffold using a configuration hook, another interesting technique is to create a new template.

As we saw in the screencast, if we want to customize the _form partial generated on scaffold, all you need to do is to copy the _form partial template from Rails source code to lib/templates/erb/scaffold/_form.html.erb.

If you want to customize the show view, you just need to copy it as well, and this is true for all generators and all templates! This allows generators to grow and adapt as your application changes! For example, in the screencast, the simple form gem creates a template to change scaffold to use the simple_form_for instead of form_for.

In the core of the Simple Form gem, there is a form builder which provides the f.input API exhibited in the screencast and much more. While the form builder abstract exists since Rails early version, some of the features provided by Simple Form are only possible in Rails 3. One example is the HTML 5 support for showing email and url fields! Another one is the ability to introspect which validations exists in the model and to be able to mark a field as required or not accordingly.

Overall, the Simple Form gem is quite simple on the Rails 3 customization side as well. It only has the basic install generator used in the screencast and does not even provide a Rails::Railtie. This is important to notice because most of the time plugin developers will not need a railtie. But, in case you need, it will be there, waiting for you.

Devise

Github Repository | Blog Posts

Devise is a full stack authentication framework for Rails on top of Rack. Since in Rails 3 you can easily dispatch a request to a Sinatra application, it’s important to have a way to share the authentication solution across different applications and this is really were Devise stands out.

In fact, Devise relies on another gem to handle the rack authentication layer called Warden. Warden is simply a middleware injected in the Rails middleware stack by Devise. Upon a request, the warden middleware inject an object inside the env hash, which can be accessed as env["warden"]. From this middleware on, you just need to call env["warden"].authenticate! and different authentication strategies will be kicked trying to authenticate the user based on the current request information. If the user cannot be authenticate, a failure app is called by Warden. It does not matter if you are inside a Rails application or a Sinatra application, the behavior after invoking Warden is exactly the same. As we can see in the picture above, Devise helpers just delegates the logic to the warden object.

Besides that, Devise also defines several authentication strategies (through database, http, token or even cookies) and also implements Warden’s failure app. This is where Rails 3 makes the difference by providing a bare bone controller structure called ActionController::Metal to build this rack application.

The ActionController::Base class simply inherits from ActionController::Metal and include all available behaviors. However, if you want to have a bare metal structure, you are allowed to and this is exactly what Devise does:

require "action_controller/metal"

module Devise
  class FailureApp < ActionController::Metal
    include ActionController::RackDelegation
    include ActionController::UrlFor
    include ActionController::Redirecting
    include Rails.application.routes.url_helpers

    delegate :flash, :to => :request

    def redirect
      flash[:alert] = i18n_message unless flash[:notice]
      redirect_to send(:"new_#{scope}_session_path")
    end
  end
end

In the example above, Devise includes all the behavior it needs and then it can use the same methods we use in controllers. If we didn’t have this facility, we would need to access the flash object and set the responder headers for redirection by hand.

Devise also provides a Railtie and also hooks into Rails 3 in other places. Don’t forget to take a look at the source code in case you want to learn more.

Mail Form

Github Repository

The last plugin we are going to discuss is called Mail Form and, as you may have noticed in the screencast, it strongly relies on Active Model. In versions previous to Rails 3, Mail Form had to duplicate a lot of logic from Rails source code to handle validation, i18n and provide the API required by form builders. Even worse, since an API was not defined, it was common to break Mail Form between Rails 2.x releases.

However, in Rails 3 version, all Mail Form does is to include the functionality it needs:

require 'active_model'

module MailForm
  module Shim
    def self.included(base)
      base.class_eval do
        extend ActiveModel::Naming
        extend ActiveModel::Translation
        extend ActiveModel::Callbacks
        include ActiveModel::Validations
        include ActiveModel::Conversion

        extend MailForm::Shim::ClassMethods
        define_model_callbacks :deliver
      end
    end

    module ClassMethods
      def i18n_scope
        :mail_form
      end
    end

    # Always return true so when using form_for, the default method will be post.
    def new_record?
      true
    end

    def persisted?
      false
    end

    # Always return nil so when using form_for, the default method will be post.
    def id
      nil
    end
  end
end

This is also a benefit to Active Record and Mail Form uses, which can always use the same API, even though in different models. Finally, the fact Mail Form only requires your object to respond to a headers method returning a hash is a functionality exclusive to Rails 3 as well, since the new Rails 3 API for Action Mailer allows you to set all Mail headers through the mail method as mail(headers).

All-in-all, Rails 3 modularity really aids plugin developers to concern with the logic specific to their plugins, instead of duplicating code from the framework. Being a Rails plugin developer was never as good as today!