N00b: heterogeneous instances in views: any ideas?

Hey all --

This is a problem I've been banging my head against for a week or so,
because Rails doesn't provide a decent way to use inheritance in one's
model very well.

Let's suppose I have the following class definitions:

class Person < ActiveRecord::Base
   attr_accessible :name
   attr_accessor :name
end

class Employee < Person
  attr_accessible :serial_number
  attr_accessor :serial_number
end

class Manager < Employee
  attr_accessible :group_name
  attr_accessor :group_name
end

I'd like for a view to display all the people in the system, but only
display, for example, group_names for those people who are also
managers. This would come in handy if I wanted to show a table of all
employees, for example.

The only decent way I can come up with is to use helper modules to do
the dirty work of rendering. (Not too disgusting, but I wish the helper
modules could just be classes, so I could use inheritance here too. I'd
rather not have helper modules replicate the inheritance tree, e.g.
forcing the ManagerHelper module to know that it must call the
EmployeeHelper module's methods. I'd rather just use 'super'.)

The problem is that I'm now stuck either 1) adding information and the
appropriate reader method to my model classes about which columns to
display (e.g. let's show 'serial_number', but not 'hashed_password'),
and in what order (e.g name first, then serial_number), or 2)
delegating this to other classses, which will be by their very
definition coupled to the model classes -- almost as ugly IMO.

I've looked at the Streamlined framework, but it doesn't quite
accomplish what I want.

Has anyone tried to do something like this? What's the best way to
approach this issue?

Have you considered using respond_to?(:group_names) ?

I'd like for a view to display all the people in the system, but only
display, for example, group_names for those people who are also
managers. This would come in handy if I wanted to show a table of all
employees, for example.

You could create separate partials _employee.rhtml, _manager.rhtml and
use them to render partials based on the type of the model object

<% for person in Person.find(:all) %>
  <%= render :partial=person.class.name.underscore,
:locals=>{:person=>person} %>
<% end %>

Max

Ah, but I have another problem: I can't seem to pass locals into
partials.

This is truly maddening. My problems would otherwise be solved. In
fact, I would
probably put the code in your post in another file in the interest of
DRYness, since
it could be re-factored rather nicely.

Another issue I'd like to tackle is the way the attributes to render
are distributed
among the partials. I don't necessarily want the _employee.rhtml
partial to know
anything explicitly about the attributes of the base Person class. But
I think this may
be my problem.. I think I'm stuck having the model classes telling the
view code what
to render.

render :partial => ‘my_partial’, :locals => { :a_local_variable_something => my_local }

See
http://api.rubyonrails.org/classes/ActionController/Base.html#M000206
for more information

Daniel N wrote:

That looks quite interesting and very handy.

Have you looked at the simply helpful plugin? It does similar
things, but one thing I found that it doesn’t do that your method does
is take care of STI. If you have a collection of different
objects with simply helpful, the first object found specifies the
partial for all, but I think your method would not suffer from
this.

Thanx for the link

Cheers

Daniel N wrote:

>
> Hey Dan,
>
> Take a look at this... It has a few helper methods that I've found
> handy for making links and rendering partials when you don't know what
> the item is ahead of time.
>
> http://www.sciwerks.com/blog/2006/05/24/polymorphic-partials/
>
> _Kevin
>
> That looks quite interesting and very handy.

Have you looked at the simply helpful plugin? It does similar things, but
one thing I found that it doesn't do that your method does is take care of
STI. If you have a collection of different objects with simply helpful, the
first object found specifies the partial for all, but I think your method
would not suffer from this.

Thanx for the link

Cheers

Yeah, it's pretty robust. I think I wrote this before the simply
helpful plugin came out.
You can pass my method a collection of dissimilar objects and it will
call the correct partial for each one. Sometimes that is a good idea,
and sometimes it isn't.

I also like being able to write links like this..

<%= link_to @item.name, to_url(@item) %>

_Kevin

Hi Kevin --

Thanks! This is very close to what I need.

The only other fly in the ointment (for me anyway) is how to render
some
attributes of instances but not others -- in effect, circumventing the
"union of
all attributes" problem of STI.

The only real way I can see to do this is with a method in a given
model classes
that returns an array of symbols corresponding to the attributes of the
class (using
the order in the array as an optional presentation "hint"):

module Viewable
    def viewables
        self.attrs
    end
end

class Employee < Person
    include Viewable
    # probably should be private.. I need to flesh this out..
    def self.attrs
         [ :type, :name, :serial_number]
    end
end

I'm loathe to force this on the model, but that's where the
responsibility seems to lie.