8

Say I have a rails app with 3 models, Person, Place and Thing. Say the Thing uses single table inheritance, so there are FancyThing and ScaryThing subclasses. Then there are routes defined with map.resources :people, :places, :things. So there are no controllers for FancyThings and ScaryThings, the ThingsController handles either type.

Now say I need to have code that shows a list of anything has links to them. If I have this code in my view:

<% @items.each do |item| %>
  <%= link_to item.name, item %>
<% end %>

If item is a Person or a Place, this works fine, polymorphic_path takes care of generating the correct route. But if item is a FancyThing or a ScaryThing, this blows up, because it will try to use fancy_thing_path, which there is no route for. I want to somehow make it use thing_path. Ideally there would be a method on Thing and/or its subclasses that somehow indicates the subclasses should use the base class to generate the route. Is there an elegant solution to this?

MarkusQ
  • 21,814
  • 3
  • 56
  • 68
pjb3
  • 5,191
  • 5
  • 25
  • 44
  • This is an [open ticket](http://dev.rubyonrails.org/ticket/10454) on rails development. See the ticket for suggestions on how to deal with it. – MarkusQ Mar 31 '09 at 20:17

3 Answers3

12

This will do the trick:

<% @items.map {|i| if i.class < Thing then i.becomes(Thing) else i end}.each do |item| %>
  <%= link_to item.name, item %>
<% end %>

This uses the ActiveRecord function "becomes" to do an "up-cast" of all subclasses of Thing to the Thing base class.

Dave Ungerer
  • 136
  • 1
  • 3
8

Try using

map.resources :things
map.resources :fancy_things, :controller => 'things'
map.resources :scary_things, :controller => 'things'
Kirschstein
  • 14,570
  • 14
  • 61
  • 79
  • 1
    +1 For two reasons 1)This works for me. 2)This answer looks more dryer than the one accepted by @pjb3. Thanks. – Rohit Mar 16 '11 at 11:26
  • This solution works, but has the down-side that it doesn't enforce the class/type , e.g. if you query an id via http://localhost:3000/fancy_things/21 and that item is actually from another sub-class ScaryThings, you would still get a valid response. It should actually give an error, because it's not of class FancyThings. – Tilo Mar 08 '12 at 01:19
0

Don't have a correct answer, but at least I can deal with this problem using non-DRY code :

map.resources :things, :has_many => :stuffs map.resources :fancy_things, :controller => 'things', :has_many => :stuffs map.resources :scary_things, :controller => 'things', :has_many => :stuffs

Hope the issue will be soon corrected in edge, since I'd like to see fancy_things only manage by :things controller. Using these routes, you will end with urls like : /fancy_things/1 whereas you maybe want /things/1

Gravis
  • 30,149
  • 5
  • 23
  • 20