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”.

About these ads

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