mhuggins / multiple_table_inheritance

Multiple Table Inheritance is a plugin designed to allow for multiple table inheritance between your database tables and your ActiveRecord models.
30 stars 4 forks source link

How to reference a child attribute from an inherited method in the parent? #18

Open creativetags opened 9 years ago

creativetags commented 9 years ago

Is it possible to do something like:

class Employee < ActiveRecord::Base
  acts_as_superclass
  belongs_to :team

  def give_date
    self.training_completed_at
  end
end

class Programmer < ActiveRecord::Base
  inherits_from :employee, :methods => true
end

@programmer = Programmer.first
@programmer.give_date

This is a silly example but is something like this possible?

mhuggins commented 9 years ago

Yeah, that's the intention of the :methods option on the inherits_from call. I will say though that I created this gem for Rails 3.x, and the internals have changed quite a bit since then that I can't promise this gem will still work under 4.x. (In fact, I'm pretty certain that it won't.)

mhuggins commented 9 years ago

With that said, I'm definitely open to updates on the gem to make it work, but it's not something I have time for or actually need right now to be able to focus any kind of time on it. If you wish to pursue, I'm happy to accept pull requests to get it fixed up! :)

creativetags commented 9 years ago

I'm running rails 3.2.20 so it's ok at the moment but I would like to upgrade my app to rails 4 at some point.

Actually what I'm trying to do is: @employee = Employee.first # assume first employee is a Programmer @employee.give_date

I'm getting undefined method training_completed_at for #<Employee

mhuggins commented 9 years ago

Where is training_completed_at defined?

creativetags commented 9 years ago

I'm using your example so:

create_table :programmers, :inherits => :employee do |t|
  t.datetime :training_completed_at
end
mhuggins commented 9 years ago

Okay, I can explain what is going on. The give_date method is properly being delegated to the Employee object, but then the Employee object tries to call training_completed_at on itself. This doesn't work because you defined it on Programmer, not Employee. The delegation is intended for methods that exist on the parent record. Since training_completed_at exists on the programmer itself and not other inheriting classes, why not just call that method directly?

creativetags commented 9 years ago

So I have 7 child classes and 5 of them have :name attributes. I've a method first_name that returns the first word from the name attribute. I want to go through each of the parent's objects (filtered to just the 5 that have names) and print their first_name.

mhuggins commented 9 years ago

This sounds like something that can't (currently) be done in the approach you're taking, in that there's no existing method to access the child from the parent. Either this method could be added (shouldn't be too hard), or else you'll need to try a slightly different approach. Adding this method would probably look as easy as something like:

module MultipleTableInheritance::Parent::Base::InstanceMethods
  def child
    send(subtype_column.to_sym).constantize.find_by_id(id)
  end
end

You could then call child.whatever from the superclass.

If you're trying to access first_name, and that only exists on 5 of the 7 models, I'd consider defining them on the remaining 2 models, e.g.:

class Employee < ActiveRecord::Base
  acts_as_superclass
end

class Programmer < ActiveRecord::Base
  inherits_from :employee

  validates :name, presence: true

  def first_name
    name.strip.split(/\s+/).first
  end
end

class Manager < ActiveRecord::Base
  inherits_from :employee

  validates :first_name, presence: true
end