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