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_address_contact_route46rhtml'
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? 23 @set.add_named_route(route_name, *args) 24 end 25 @@ -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.