Rails 3 upgrade part 2: Routes

In the previous post, I outlined the steps I took to upgrade and boot a Rails 3 application. This time, I share my experience upgrading the routes file. By the way, I forgot to mention in the last post that I’m using Rails 3 Upgrade Handbook by Jeremy McAnally.

The task rails:upgrade:routes (comes with the rails_upgrade plugin) converts your Rails 2 routes into Rails 3 format. It handles most cases but you may still need to edit the generated routes depending on your setup.

map.root

Below, I show the old route and the generated version.

  # Rails 2
  map.root :controller => 'search'
  
  # Rails 3
  match '/' => 'search#index'

The conversion is correct but since I use the named route ‘root_path’ in my application, I had to change it:

  root :to => 'search#index'

:as, :member, :any, :path_names

  # Rails 2
  map.resources :workspaces, :as => 'b', :member => { :widget => :get } do |workspace|
    # ...
  end
  
  # Rails 3
  resources :workspaces do
    # ...
  end

In Rail 3, :as is for overriding the normal naming for named routes witout affecting the path. For example, the code below will recognize the path ‘/workspaces’ and the named route becomes offices_path.

  resources :workspaces, :as => 'offices'

In Rails 2, :as affects the path. In my example, ‘/b’ routes the request to WorkspacesController. So for Rails 3 to recognize the path ‘/b’, I need to add another route.

  match 'b' => 'workspaces#index'

The rails:upgrade:routes did not convert the following member route and had to be added.

  :member => { :widget => :get } 

The new route becomes:

  resources :workspaces do
    get :widget, :on => :member
  end

In Rails 2, you can use the :any option to define a custom route that responds to any request method.

  # Rails 2
  workspace.resource :twitter_account, :member => { :authorize => :any }, :path_names => { :edit => 'request_authorization' }
  
  # generated by rails:upgrade:routes
  resource :twitter_account do
    member do
      any :authorize
    end
  end

The rails:upgrade:routes converted the :any option. However, when I booted the application, it raised an exception:

  undefined method 'any' for #<ActionDispatch::Routing::Mapper:0xb71b6fcc> (NoMethodError)

To fix this, I replaced the offending line with a match method.

  resource :twitter_account do
    match :authorize, :on => :member
  end

:path_names was also not included in the generated route so has to be added as well.

  resource :twitter_account, :path_names => { :edit => 'request_authorization' } do
    match :authorize, :on => :member
  end

Specifying a different controller

  # Rails 2
  map.resource :settings, :controller => 'users' do |settings|
    settings.resource :twitter_account, :name_prefix => nil, :member => { :authorize => :any }, :path_names => { :edit => 'request_authorization' }
  end
  
  # generated by rake:upgrade:routes
  resource :settings do
    resource :twitter_account do
      member do
        any :authorize
      end
    end
  end

To fix, just specify the controller

  resource :settings, :controller => :users do
    # ...
  end

Undefined named route helper

I encountered this exception while trying the application:

  undefined method 'edit_twitter_account_path'

In Rails 2, this is the route that created this named route:

  map.resource :settings, :controller => 'users' do |settings|
    settings.resource :twitter_account, :name_prefix => nil, :member => { :authorize => :any }, :path_names => { :edit => 'request_authorization' }
  end

This is a bit tricky for me because I cannot remember why I nested it :) Nevertheless, to fix the Rails 3 error, I moved :twitter_account outside of :settings. The correct Rails routes now look like these:

  resource :settings, :controller => :users
  resource :twitter_account, :path_names => { :edit => 'request_authorization' } do
    match :authorize, :on => :member
  end

Custom polymorphic named route helper

A long time ago, I played around with polymorphic paths. In hindsight, that is a waste of time but back then it was fun or should I say a time well wasted. I have a named route helper that takes any object and used like this:

  # in views
  link_to 'invitations', invitations_path(@voteable)
  
  # definition
  module RoutesHelper
    def invitations_path(voteable)
      send("#{voteable.class.name.underscore}_invitations_path", voteable)
    end
  
    def workspace_invitations_path(workspace)
      super(:workspace_id => workspace)
    end
  
    # ...
  end

In Rails 3, my named route helper is not being called. Thus, wrong URL is generated. I know, I know it should have been a simple polymorphic_path call but I still wonder why my method is not called. Moving on, the new ruby is:

  link_to 'invitations', polymorphic_path([@voteable, :invitations])

I cheated a bit here because I want this post to focus on routes. Along the way, I had to update non-route related code to discover the route problems. You can learn more about Rails 3 routes from this RailsGuides page.

There are still more updates to be done and I’ll share them in other posts. Just like your favorite late night infomercial, “Wait! There’s more”.

4 thoughts on “Rails 3 upgrade part 2: Routes

  1. Hi,

    In your part : “:as, :member, :any, :path_names”

    I’m currently migrating a rails 2.1 to rails 3.2.19.
    I replaced every rails 2 “:as => :foo” with “:path => :foo” in rails 3.2 and it worked like a charm.

    Thank you for your post :)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s