STI Problems

Hi,
  I've been trying to figure out STI and I'm having some trouble
getting things to work smoothly. I can generate the STI hierarchy with
no problem but then things get strange.
I ran an experiment to try and figure it out.
Here's what I did:

I try to create 2 completely scaffolded models, create a simple STI
hierarchy between them (Son < Parent) and see how Rails handles it.

At the command line:

    ruby script\generate scaffold Parent name:string description:text
    rake db:migrate
    ruby script\generate scaffold Son member:string

Make 2 changes to the generated code:

   1. Change the migration from creating a new sons table to adding 2
new columns to the parents table:
          * add_column :parents, :member, :string
          * add_column :parents, :type, :string # needed for STI
   2. Make Son (in app/models/son.rb) derive from Parent (instead of
ActiveRecord::Base)

rake db:migrate again, and add a few Parents and a Son (through the
console).

Result: At URL /parents all Parents and Sons are listed, since they
are all saved in the same table. This is the desired behavior which
would be expected with an is-a relationship between Sons and Parents.
(Note that I use parent and son names in the parent i.e. base class
and son i.e. derived class meaning and not in the genealogical sense)
This view is generated by the scaffold generator script, and is
actually app\views\parents\index.html.erb.

Since Sons are derived but different types from Parents it might be
expected that the Show, Edit and Destroy links for actual concrete
Sons will link to the (automatically generated) Son controller and the
not to the Parent controller. This would essentially be equivalent to
calling virtual methods.

This actually happens for the Show and Destroy links. However, the
Edit link still redirects to the Parent edit page. The problem
apparently stems from the Parent index view:

<%= link_to 'Show', parent %>
<%= link_to 'Edit', edit_parent_path(parent) %>
<%= link_to 'Destroy', parent, :confirm => 'Are you sure?', :method
=> :delete %>

the edit_parent_path(parent) seems to be is not as smart as link_to in
detecting the STI hierarchy on the parent object.

Can anyone shed some light on this?
What does link_to do right that edit_parent_path does wrong?

There is a problem of consistency, either all actions (Show, Edit,
Destroy) should point to Parent actions or they should all point to
Son actions (this is arguable smarter and more in the spirit of
Polymorphism). Having a mixed behavior is at best confusing.

Is it possible that the new RESTful helpers are "dumber" than link_to
or url_for?

Interestingly enough, when I edit a Son object via the Parent edit
view, the submit button actually calls Son's update method!

Is there a away to simulate edit_parent_path() with link_to in such a
way to get consistent behavior?

Thanks,
Adi

Hi Adish,

I've actually faced the same problem this morning. Basically my models
look like this

User < ActiveRecord::Base
then
Administrator < User,
Lecturer < User
Student < User.

I am using the same STI pattern with Rails 2.0.2.

In order to get the problems you've mentioned solved, for the "show"
function, I used the following:

<%= link_to user.full_name, user_path(user) %>

This would make sure that the link always refers to the parent.

Now about the destroy, I actually did not have a destroy link because
I just needed the User to be suspended / unsuspended depending on the
case. So I created those two methods in the UsersController (def
suspend ... end / def unsuspend ... end).

And I tried the following:

<%= link_to 'Suspend', suspend_user_path(user), :confirm => 'Are you
sure?', :method => :put %>

Then I added the following in my "routes.rb":

map.resources :users, :member => { :suspend => :put, :unsuspend
=> :put }

in your case, it'd probably be the following for the "routes.rb":

map.resources :users, :member => { :destroy => :delete }

although I suspect Rails already has that implicitly defined.

The point is to note the difference between what you'd normally do for
a destroy as in:

<%= link_to 'Destroy', parent, :confirm => 'Are you sure?', :method
=> :delete %>

versus what was done for the "suspend/unsuspend" above.

This creates a consistency between the "show/edit/destroy/
other_methods" as I believe the edit_parent_path(parent) is the way
it's supposed to work.

I may be wrong on this but I hope this helps somehow.