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.