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.