1. The Path to Rails 3: Introduction

    Wow, over half a year with no blog post. That may be a new record for blog laziness for me, but fear not! This bout of sloth shall not last, and the dearth of blog entries shall come to and end! This cure should come partially because I’ve switched to Tumblr and can now compose my entries in Markdown, and partially because that’s part of my whole Get a Better Life New Year’s Resolution Package 2.0™ (coming to a burned out programmer near you in 2011!). Let’s hope this pans out, or, at least, I don’t end up strung out in the gutter tapping out entries into Textmate. Anyhow, to catch you up, here’s what’s happened since my last post:

    But that’s not what this post is about. This post is kicking off a series that I’m doing about moving your skills and migrating your code to Rails 3. I’ll be sharing some practical insights and covering some pretty in-depth topics as we go along (I’ve got some notes for entries about upgrading plugins, taking advantage of new features like the agnosticism, migrating applications, and so on), but before I go into a lot of specifics, I thought it might be useful to go over some of the high-level philosophical and architectural changes that have gone on in the Rails code between versions 2 and 3.

    The Big Picture

    When the Merb/Rails merge was announced, I was worried that we were going to end up in some weird tangle of Merbilicity and Railsishness when the final product came around. I don’t think anyone wants some Brangelina of the web framework world all up in their business. Fortunately, the gents on the Rails core team are smart and classy and have navigated the waters of cooperation and compromise extremely well. We’re getting the best of both world here, folks: the ease of use and packaging of Rails with the juicy technical bits of Merb. Who can argue with that?

    But to make that Epic Code Merge of Awesome™ happen, of course there had to be some changes. These big picture changes have concentrated on a few key areas:

    In order to hit these objectives, DHH, Yehuda, Josh, and the rest of the Rails team have extracted things into some new components, expanded others, and removed others to allow for agnosticism.

    Overview

    The general movement seems to be from a monolithic, one-stop shop approach to a looser ecosystem of code that works together with a straightforward set of sensible defaults. You’re no longer “locked in” to ActiveRecord or made to use code injection and hacks and such to get your testing framework integrated. Instead, there are hooks all over the place to cover this sort of stuff (which I will cover later on in the series!) that let generators generate things for the various options or helpers include different modules. It’s a great way to support an ecosystem with an established API.

    Lifecycle changes

    One of the biggest movements in the codebase has been a shift towards using simple, composed components and a lot of Rack in the request chain rather than specialized, one-off classes. This has affected a lot of things, but one of the major changes has been the addition of Action Dispatch.

    The request chain

    Action Dispatch is a “new” component in Action Pack (extracted and expanded from the previous logic) that handles a number of things related to requests and responses:

    Breaking this functionality out into its own component and decoupling much of it creates a much more flexible call stack for requests, meaning you can jack into the process easier with your own logic or improve the existing functionality. I’m sure we’ll see a lot of plugins taking advantage of this to create interesting middleware hacks, improve callbacks and lifecycle methods, hack in their own middlewares to handle specialized logic, or even plug in improved or application-specific routers. This is one of the more interesting pieces I’m interested in seeing develop, since it opens a lot of possibilities that were previously much more difficult to reach.

    Making controllers flexible

    As a result of the changes in the request chain, the controller stack has also seen a significant overhaul. Previously, every controller inherited from ActionController::Base (either directly or by inheriting from ApplicationController) and slimming down the call stack was accomplished by either (a) previous to Rails 2.3, building a smaller app with Sinatra or Rack to sit next to your main Rails application or (b) post-Rails 2.3, using Rack Metal/middlewares.

    In Rails 3.0, this concept of middleware plays an even more central role to how the controller hierarchy is arranged.

    The new controller stack

    The bottom of the stack is AbstractController, a very low level “controller.” Rails uses this class to abstract away essentials like rendering, layouts, managing template paths, and so on, while leaving more concrete implementation details to its subclasses. AbstractController exists only to provide these facilities to subclasses. That is, you should not use this class directly; if you want something super-slim, create a subclass and implement render and a few other pieces).

    Each subsequent jump up the hierarchy is actually a class that inherits from the previous, each including modules to compose its behavior. So, if you want to create something slim without implementing a lot of plumbing, use the next rung on the compositional ladder: ActionController::Metal. Metal essentially exposes super simple Rack endpoints that you can then include extra modules into to add more ActionController functionality (check out an example here). These little classes are excellent for replacing those Rack/Sinatra apps for file uploads or what have you while still having the power to easily build out to rather rich controller objects.

    Finally, if you need the full monty (i.e., like a controller in Rails 2), then you’ll need to inherit from ActionController::Base. This class inherits from ActionController::Metal and includes a slew of modules to handle things like redirecting the user, handling implicit rendering, and a number of helpers for other stuff like caching.

    The advantage of taking this approach is that you can take one of the base classes like Metal and include your own modules to create specialized controllers. I foresee someone using this to create a simple way to serve up resources (e.g., PostsController < ResourcesController(:posts) or something like that) much like people have done previously (José Valim’s inherited_resources jumps to mind) or using it as a way to quickly build API backends. This is the other piece of the major refactor that excites me, since we’re looking at a new way to construct reusable code and assemble it into usable applications.

    Where models are concerned

    Though the public API for models is generally the same (with a few additions and changes that I’ll cover in a subsequent post), Active Record is now powered by the brain-melting Active Relation, a powerful relational algebra layer.

    Intelligent SQL generation

    What does that mean for you? Well, basically it means that Active Record will be smarter and more powerful. Rather than fairly naïve SQL generation, it uses some fancy mathemagical approach that should generate smarter queries. Frankly, I haven’t had a lot of time to research these features for myself, but when I do, I’ll be sure to post (or if you’ve posted about this stuff somewhere, then by all means let me know).

    The second big change in Model Land is the extraction of much of the rich logic in Active Record objects like callbacks, validations, serialization, and so on into the Active Model module.

    ActiveModel rawks.

    You can use this module to make any object behave like an Active Record object; for example, let’s say you wanted to add some validations to a PORO representing a host on a network:

    class Host
      include ActiveModel::Validations
    
      validates_presence_of :hostname
    
      attr_accessor :ip_address, :hostname, :operating_system
      def initialize(hostname, ip_address, operating_system)
        @hostname, @ip_address, @operating_system = host, ip_address, operating_system
      end
    end
    
    h  = Host.new("skull", "24.44.129.10", "Linux")
    h.valid?    # => true
    h.hostname = nil
    h.valid?    # => false
    

    To get this functionality, simply include ActiveModel::Validations and start implementing the methods. It’s possible to exercise fine-grained control over how the validations operate, how the validator gets the object’s attributes, and so on. To get the other functionality like observing or callbacks, just include the relevant module (e.g., ActiveModel::Observing) and implement the required methods. It’s fantastically clever.

    Other pieces

    ActionMailer is also getting some love in Rails 3. A new API pointed out by DHH in this gist is looking especially delicious; it’s much more like a controller with some excellent helpers mixed in just for mailing.

    Rails is also getting a rather robust instrumentation framework. In essence, an instrumentation framework lets you subscribe to events inside of a system and respond to them in meaningful ways (e.g., an action renders and the logger logs its result). Internally the framework is used for things like logging and debugging, but you could easily repurpose the code for other things. For example, let’s say you want to log to the system logger when a particular e-mail is sent out:

    # Subscribe to the event...
    ActiveSupport::Notifications.subscribe do |*args|
      @events << ActiveSupport::Notifications::Event.new(*args)
    end
    
    # Fire the event...
    ActiveSupport::Notifications.instrument(:system_mail, :at => Time.now) do
      #SystemMailer.important_email.deliver
      log "Important system mail sent!"
    end
    
    # Do something with it...
    event = @events.first
    event.name        # => :system_mail
    event.payload     # => { :at => Wed Jan 16 00:51:14 -0600 2010 }
    event.duration    # => 0.063
    system_log(event) # => <whatever>
    

    Of course, this is arbitrary, but it adds a really powerful way to respond to certain events in your application. For example, someone could probably rewrite exception_notification to use the instrumentation framework to handle and send error e-mails.

    Getting started

    So, how does one get a piece of this sweet, sweet Rails 3 action? You can install the old pre-release gems, but they’re pretty out of date. To get rolling on edge, I’ve found two ways that work well. First, you can use Yehuda’s directions; these worked great for me and they’re not a lot of hassle (and how awesome is the bundler?). If that seems a bit much or you want to automate it, Bryan Goines has made a pretty awesome script to handle installing and bundling all you need to make it work.

    So, go ahead, install Rails 3, get setup, and I’ll meet you on the other side. I’ll be dropping another post this week about how to get started upgrading an existing application/plugin to Rails 3.

    Posts in this series

    I’m posting a whole series on Rails 3; be sure to catch these other posts!

    1. Introduction
    2. Approaching the Upgrade
    Comments

Powered by Tumblr; designed by Adam Lloyd.