1. The Path to Rails 3: Approaching the upgrade

    Now that we’ve looked at some of the core architecture, I’d like to shift my focus first to upgrading an application. Originally I had planned on writing about upgrading plugins first, but apparently that API isn’t quite stable. So, I figured rather than write a blog post that will be deprecated in 2 weeks, I’d rather write one that will be deprecated in 3-6 months instead. So, this post will focus on getting your app bootable, and it will be followed by a succession of articles that contain tips and scripts to help you upgrade the various components (i.e., routes, models, etc. are topics I’m working on right now).

    The first step towards an upgraded app you need to take is to actually get Rails 3. As noted in the previous post, you can follow Yehuda’s directions or use Bryan Goines’s great little script. Once you’ve got it up and running, I suggest you “generate a new app” on top of your current one (i.e., run the generator and point the app path to your current Rails 2.x app’s path). Running the generator again will actually update the files you need to update, generate the new ones, and so on.

    ruby /path/to/rails/railties/bin/rails ~/code/my_rails2_app/
    

    Note that the argument is a path, not a name as in previous Rails versions. If you got an error about your Ruby version, upgrade it! If you use rvm it’ll be totally painless. Now, be careful which files you let Rails replace since a lot of them can be edited much more simply (I’ll show you how here) than they can be reconstructed (unless you really like digging around in git diff and previous revisions), but do take note of what they are since you will likely need to change something in them. As a general list, it’s probably safe to let it update these files:

    And, you probably don’t want to let it update these files since you’ve likely made modifications:

    Of course, these lists won’t apply in every situation, but in general I think that’s how it’ll break down. Now, on to the things you’ll need to change…

    config.gem is dead, long live bundler

    Everyone and their brother complained about Rails’ handling of vendored/bundled gems since config.gem was added sometime ago (just search for “config.gem sucks” or “config.gem issues OR problems” and you’ll see). Between issues with requiring the gems properly to problems with the gem detection (I can’t tell you how many times I nixed a gem from the list because it kept telling me to install it even though it was already installed), Rails seriously needed a replacement for such a vital piece of infrastructure. These days we have Yehuda Katz’s excellent bundler, which will be the standard way to do things in Rails 3.

    Essentially, bundler works off of Gemfiles (kind of like Rakefiles in concept) that contain a description of what gems to get and how to get them. Moving your gem requirements to a Gemfile isn’t as simple as copying them over, but it’s not terribly difficult:

    # This gem requirement...
    config.gem "aws-s3", :version => "0.5.1", 
               :lib => "aws/s3", :source => "http://gems.omgbloglol.com"
    
    # ...becomes:
    source "http://gems.omgbloglol.com"
    gem "aws-s3", "0.5.1", :require_as => "aws/s3"
    

    As you can see, it’s not too hard. It’s basically just removing the config object and moving some keys around. Here’s a specific list of changes:

    Once you create a Gemfile, you simply have to run bundle pack and you’re done!

    The bundler is much more powerful than config.gem, and it helps you do more advanced tasks (e.g., bundle directly from a Git repository, specify granular paths, etc.). So, once you move your config.gem calls over, you may want to look into the new features; they may be something you had wished config.gem had but didn’t!

    Note: I’ve noticed some activity in Yehuda/Carl’s Githubs to do with a bundler replacement called gemfile; I’ll watch that closely to make sure there are no major breaking changes in the API/operation. If there are, I’ll definitely post here!

    Move to Rails::Application

    In all previous Rails version, most configuration and initialization happened in config/environment.rb, but in Rails 3, most of this logic is moved to config/application.rb and a host of special initializers in config/initializers. The config/environment.rb file basically looks like this now:

    # Load the rails application
    require File.expand_path('../application', __FILE__)
    
    # Initialize the rails application
    YourApp::Application.initialize!
    

    Simple: the application.rb file is required and then the Application is initialized. The YourApp constant is generated based on the folder name for your app (i.e., rails ~/code/my_super_app would make it MySuperApp), so name it wisely! It doesn’t have any special relationship to the folder the app lives in so you can rename it at will (so long as you do it everywhere it’s used), but you’ll be using this constant in a few places so make it something useful.

    Now you need an application.rb; if you generated the files using the Rails 3 generator, you should have one that looks something like this:

    module TestDevApp
      class Application < Rails::Application
        # ...Insert lots of example comments here...
    
        # Configure sensitive parameters which will be filtered from the log file.
        config.filter_parameters << :password
      end
    end
    

    For the most part, your config.* calls should transfer straight over: just copy and paste them inside the class body. There are a few new ones that I’ll be covering later on in this series that you might want to take advantage of. If you run into a config.* method that doesn’t work (other than config.gem which obviously won’t work), then please post in the comments, and I’ll add it into a list here.

    You’ll also notice that many things that were once in environment.rb have been moved out into new initializers (such as custom inflections). You’ll probably want to/have to move these things out of application.rb and into the proper initializer. If you opted to keep any custom initializers or specialized environment file during the generation process, you’ll probably need to go in there and update the syntax. Many of these (especially the environment files) now requires a new block syntax:

    # Rails 2.x
    config.cache_classes = false
    config.action_controller.perform_caching = true
    
    # Rails 3.x
    YourApp::Application.configure do
      config.cache_classes = false
      config.action_controller.perform_caching = true
    end
    

    All configuration happens inside the Application object for your Rails app, so these, too, need to be executed inside of it. As I said previously, most things in there should still work fine once wrapped in the block, but if they don’t please comment so I can post about it/figure out the issue.

    Ch-ch-chaaange in the router

    You’ve probably heard a lot of talk about Rails and routes and new implementations and this and that. Let me tell you: the new router is pretty awesome. The problem is that it’s not exactly easy to migrate existing routes over to the new hotness. Fortunately (for now, at least) they have a legacy route mapper so your routes won’t break any time soon. Of course, you should always try to update things like to this to keep up with the version you’re running (i.e., never depend on the benevolence of the maintainers to keep your ghetto legacy code going while using a new version for everything else).

    But don’t worry. Upgrading your routes is fairly simple so long as you haven’t done anything complex; it’s just not as easy as copying and pasting. Here are a few quick run-throughs (a detailed guide is coming later)…

    Upgrading a basic route looks like this:

    # Old style
    map.connect '/posts/mine', :controller => 'posts', :action => 'index'
    
    # New style
    match '/posts/mine', :to => 'posts#index'
    

    A named route upgrade would look like:

    # Old style
    map.login '/login', :controller => 'sessions', :action => 'new'
    
    # New style
    match '/login', :to => 'sessions#new', :as => 'login'
    

    Upgrading a resource route looks like this:

    # Old style
    map.resources :users, :member => {:ban => :post} do |users|
      users.resources :comments
    end
    
    # New style
    resources :users do
      member do
        post :ban
      end
    
      resources :comments
    end
    

    And upgrading things like the root path and so on looks like this:

    # Old style
    map.root :controller => 'home', :action => 'index'
    
    map.connect ':controller/:action/:id.:format'
    map.connect ':controller/:action/:id'
    
    # New style
    root :to => 'home#index'
    
    match '/:controller(/:action(/:id))'
    

    I’ll be writing another entry later on about the router’s new DSL and looking at some common patterns from Rails 2 apps and how they can work in Rails 3. Some of the new methods add some very interesting possibilities.

    Some minor changes

    There are a few minor changes that shouldn’t really mess with too much (except perhaps the first one here…).

    Constants are out, module methods are in Ah, nostalgia. Remember when RAILS_ROOT and friends were cool? Well, now they’re lame and are going away in a flare of fire and despair. The new sexy way to do it: Rails.root and its module method pals. So, remember. Old and busted: RAILS_ROOT and its depraved, constant brethren. New hotness: Rails.root and its ilk.

    Rack is Serious Business™ You might have noticed that the Rails 3 generator gives you a config.ru in your application root. Rails 3 is going gung ho on Rack, everyone’s favorite web server interface, and as such, a config.ru is now required in your application to tell Rack how to mount it. Like I said, the Rails 3 generator will spit one out for you, but if you’re doing a manual upgrade for some reason, then you’ll need to add one yourself.

    Interesting note: Remember that YourApp::Application class you created earlier in application.rb? That’s your Rack endpoint; that’s why your config.ru looks like this:

    # This file is used by Rack-based servers to start the application.
    
    require ::File.expand_path('../config/environment',  __FILE__)
    run YourApp::Application.instance
    

    Neat, eh? That’s why I suggested you pick a meaningful name for that class: its touch runs quite deep in the stack.

    .gitignore to the rescue Rails also now automatically generates a .gitignore file for you (you can tell it not to by providing the --skit-git option to the generator). It’s fairly simple, but covers the 95% case for most Rails developers, and it’s certainly a welcome addition to the toolbox. It was always annoying having to create one every time and either dig up a previous one to copy or try to remember the syntax of how to make it ignore the same stuff.

    After upgrading this stuff, you probably have a booting application. Of course, these are a lot of moving parts that could derail this plan: old and busted plugins, gem stupidity, weirdness in your config files, lib code that’s problematic, application code that needs upgrading (I can almost guarantee that), and so on. In any event, these are a few steps in the right direction; subsequent posts will show you the rest.

    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.