Named resources with name prefixes
Rails allows you to define routes with named prefixes. I expected Rails to somehow know that I wanted my prefix to separate the rest of the name with an underscore.
Here was my original routes:
config/routes.rb
1 ActionController::Routing::Routes.draw do |map|
2 map.resources :parties, :path_prefix => "/admin" do |parties|
3 parties.resources :addresses,
4 :controller => "address_contact_routes",
5 :name_prefix => :party
6 end
7
8 map.resources :addresses,
9 :controller => "address_contact_routes"
10 end
Then, when I tried to use my route, I was getting a NoMethodError:
1 1) Error:
2 test_can_show(AddressContactRoutesControllerTest::PartyWithAddressTest):
3 ActionView::TemplateError: undefined method `party_address_path’ for #<#<Class:0xb768f6ac>:0xb63ee41c>
4 On line #3 of app/views/address_contact_routes/address_contact_route.rhtml
5
6 1: <%
7 2: if address_contact_route.routable && !address_contact_route.new_record? then
8 3: update_url = "#{party_address_path(address_contact_route.routable, address_contact_route)}.txt"
9 4: end
10 5: -%>
11 6: <% inline_fields_for(:address, address_contact_route, :url => update_url) do |f| -%>
12
13 #{RAILS_ROOT}/app/views/address_contact_routes/address_contact_route.rhtml:3:in `_run_rhtml_47app47views47address_contact_routes47_addresscontactroute46rhtml’
After a bit of sleuthing (and adding messages to ActionController::Routing::RouteSet#add_named_route), I found out that I was supposed to put the underscore myself on the prefix, like this:
1 map.resources :parties, :path_prefix => "/admin" do |parties|
2 parties.resources :addresses,
3 :controller => "address_contact_routes",
4 :name_prefix => :party_
5 end
It was also interesting to see all those generated routes. I discovered that the route with a format was named formatted_whatever.
Here’s a diff against the 1.2 branch of Rails that allows you to see all the generated routes as they are read:
1 $ svn diff vendor/rails/actionpack/lib/action_controller
2 Index: vendor/rails/actionpack/lib/action_controller/routing.rb
3 ===============
4-vendor/rails/actionpack/lib/action_controller/routing.rb (revision 6424)
5 + vendor/rails/actionpack/lib/action_controller/routing.rb (working copy)
6@ -349,7 +349,9 @
7
8 method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend"
9 instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
10 – raw_method
11 + returning raw_method do
12 + RAILS_DEFAULT_LOGGER.debug {raw_method}
13 + end
14 end
15
16 # Build several lines of code that extract values from the options hash. If any
17@ -999,6 +1001,7 @
18 deprecate :root => "(as the the label for a named route) will become a shortcut for map.connect ’’, so find another name"
19
20 def method_missing(route_name, *args, &proc)
21 + RAILS_DEFAULT_LOGGER.debug {"ActionController::Routing::RouteSet::Mapper#method_missing(#{route_name.inspect}, #{args.inspect})"}
22 super unless args.length >= 1 && proc.nil?
23set.add_named_route(route_name, *args) <span class="no">24</span> end <span class="no"><strong>25</strong></span> @-1175,6 +1178,7 @@
26 end
27
28 def add_named_route(name, path, options = {})
29 + RAILS_DEFAULT_LOGGER.debug {"ActionController::Routing::RouteSet#add_named_route(#{name.inspect}, #{path.inspect}, #{options.inspect})"}
30 named_routes[name] = add_route(path, options)
31 end
32
For reference, here are all the generated routes for this declaration:
config/routes.rb
1 ActionController::Routing::Routes.draw do |map|
2 map.resources :layouts, :path_prefix => "/admin"
3 end
layouts => "/admin/layouts" formatted_layouts => "/admin/layouts.:format" new_layout => "/admin/layouts/new" formatted_new_layout => "/admin/layouts/new.:format" edit_layout => "/admin/layouts/:id;edit" formatted_edit_layout => "/admin/layouts/:id.:format;edit" layout => "/admin/layouts/:id" formatted_layout => "/admin/layouts/:id.:format"
EDIT (2007-03-14 16:52 EDT): Changed link to routing.rb file to the Rails Trac Browser.