views:

34

answers:

2

Assume a polymorphic association, say 'business' and 'staff', both of which are 'hourable' (meaning they have hours assigned to them). What's the recommended approach to have the 'hour' model performs the same methods on the hours of either a business object or a staff object?

For a simple example, the 'hour' model might contain:

  def self.find_hours_for_id(id)
    Business.find( id ).hours
  end

However, I may want to perform this same method on a Staff member, in which case the same method would instead call the equivalent of

Staff.find( id ).hours

Is it good or bad form to set the model name within the base model class:

class BusinessHour < Hour
  @mymodel = "Business"
end

class Hour < ActiveRecord::Base
  def self.find_hours_for_id(id)
    @mymodel.constantize.find( id ).hours
  end
end

Then from the controller, call:

BusinessHour.find_hours_for_id(id)

Is there a better solution?

+1  A: 

You can use a module too, like that.

module Hour
  def hours_by(model)
    class_eval do
      def self.find_hours_for_id(id)
        model.find(id).hours
      end
    end
  end
end

class BusinessHour < AR
  extends Hour
  hours_by Business
end

BusinessHour.find_hours_for_id(id)
shingara
Thanks! Would you say this is better than what I did? Would my solution be acceptable as well? Just curious.
99miles
your solution is acceptable. It's just a different way to do. And my solution works better if you have "multiple" inheritance
shingara
+1  A: 

Assuming, oh for instance, your object model looks like:

class Business < ActiveRecord::Base
  has_many :hours, :as => :hourable
end

class Staff < ActiveRecord::Base
  has_many :hours, :as => :hourable
end

class Hour < ActiveRecord::Base
  belongs_to :hourable, :polymorphic => true
end

class BusinessHour < Hour
  # etc
end

Then you already have a reference to the class you need in 'hourable,' and you can add the finder as follows:

class Hour < ActiveRecord::Base
  belongs_to :hourable, :polymorphic => true

  def self.get_hours_by_hourable_id(id)
    hourable.class.find(id).hours
  end
end

Just a suggestion, but you might consider putting a validation on the subclasses to guarantee that :hourable is the appropriate type, such as:

class BusinessHour < Hour
  validates :hourable_must_be_business

  def hourable_must_be_business
    unless hourable_type == 'Business'
      self.errors.add_to_base("Hourable for BusinessHour must be Business") 
    end
  end
end
Dave Sims
Hmm, that is what my models looks like, however when I try to access 'hourable' in the Hour model I get: "undefined local variable or method `hourable' for #<Class:0x000001045233c0>"
99miles
Doh. Yep, that's an instance call in a class scope. My bad. Lemme work on that.
Dave Sims
lol...off to planning session for the afternoon. I'll work on this. Interesting problem. The meta-module solution from shingara looks like a good option at first glance.
Dave Sims
Cool, thanks, Dave.
99miles