form_for using wrong dynamic path helper?

Hi guys,

I've come across what I think is a bug in form_for in Rails 3 beta 3, and just wanted to run it by you before I needlessly created a ticket. Here's the situation:

Named route:   resources :thread, :class_name => "forum_thread" Controller name:   forum_thread_controller Model object:   forum_thread

The following throws an exception when rendering the view: <%= form_for @thread, :as => :thread do |f| %> .... <% end %>

Exception: undefined method `forum_threads_path' for #<#<Class:0x00000103a45098>: 0x000001039575a0>

Running the same form through an edit action gives this exception: undefined method `forum_thread_path' for #<#<Class:0x00000103b6c750>: 0x00000103b5f870>

Shouldn't form_for be keying off of ":as => :thread" and using threads_path/thread_path instead of forum_threads_path/ forum_thread_path ?

-J

Shouldn't form_for be keying off of ":as => :thread" and using threads_path/thread_path instead of forum_threads_path/ forum_thread_path ?

No, not as currently designed and implemented. The mapping from instance to route helper name is done without consulting the routing definitions at all, it's driven entirely off the class name and a few simple conventions. This has the downside that you get the errors that you're seeing now. But there's also the upside that you can have those helpers call routes defined in ways other than resources. e.g. manually connecting posts to a URL then using form_for @post.

I don’t get why you’re doing :class_name in the routes. I think the :as should be there. :class_name is more an ARec thing.

Hi Ryan,

I specify :class_name in the router so that I can generate URLs that look like:

/thread /thread/new /thread/5/edit etc

while having the requests dispatched to the ForumThreadController (forum_thread_controller.rb).

Unfortunately, due to namespace constraints, I can't just call the controller ThreadController (or at least the associated generators yelled at me when I tried).

-J

As far as I know, this is what :as is for. I’ve never seen :class_name used within the context of routes, but it may be an available option I don’t know about.

Thanks for the reply, Michael.

> Shouldn't form_for be keying off of ":as => :thread" and using > threads_path/thread_path instead of forum_threads_path/ > forum_thread_path ?

No, not as currently designed and implemented. The mapping from instance to route helper name is done without consulting the routing definitions at all, it's driven entirely off the class name and a few simple conventions. This has the downside that you get the errors that you're seeing now. But there's also the upside that you can have those helpers call routes defined in ways other than resources. e.g. manually connecting posts to a URL then using form_for @post.

That's what I was afraid of.

I certainly enjoy the simplicity of form_for @post, but it would also be very useful for :as => :thread to influence the routing that's generated (or to have some option that influences the generated routing). The downside would appear to be that if you have a controller whose name doesn't perfectly match to a named route that you're stuck having to implement a separate form for the new and edit cases, which is a non-DRY bummer.

-J

It would appear that it renames the helper route methods, but not the URLs that are actually used (and exposed to the user):

  resources :forum_thread, :as => :thread

From rake routes:                   GET /forum_thread(.:format) {:action=>"index", :controller=>"forum_threads"}      threads POST /forum_thread(.:format) {:action=>"create", :controller=>"forum_threads"} new_thread GET /forum_thread/new(.:format) {:action=>"new", :controller=>"forum_threads"}                  GET /forum_thread/:id(.:format) {:action=>"show", :controller=>"forum_threads"}                  PUT /forum_thread/:id(.:format) {:action=>"update", :controller=>"forum_threads"}      thread DELETE /forum_thread/:id(.:format) {:action=>"destroy", :controller=>"forum_threads"} edit_thread GET /forum_thread/:id/edit(.:format) {:action=>"edit", :controller=>"forum_threads"}

How about if you pass the :controller option to it as well as :as?

I'm sorry, I mistyped earlier. The route should have read:

  resources :thread, :controller => "forum_thread"

Even with that being the case (it was correct in the source, just not in my post) form_for is unhappy as previously described.

-J

In digging into this a bit more, I had mistakenly been under the impression that it was the controller that was used to infer this path, but it actually turns out the be the model object. Does it really make sense to let the naming of a model object bleed so far through the application layer without a "no, use this name instead" fallback?

-J

In digging into this a bit more, I had mistakenly been under the impression that it was the controller that was used to infer this path, but it actually turns out the be the model object. Does it really make sense to let the naming of a model object bleed so far through the application layer without a "no, use this name instead" fallback?

There actually is a fallback at present:

<%= form_for @person, :url=>some_other_path do ...

You could probably also override the values returned by YourClass.model_name to achieve the same thing.

ree-1.8.7-2010.01 > Person.model_name => "Person" ree-1.8.7-2010.01 > Person.model_name.plural => "people"

Personally though I just wear the url ugliness and have /forum_threads rather than worrying too much about it. Sure it's not gorgeous but you stay on the golden path and you're finished thinking about it in a few minutes.