23

What i'm looking to do is have a base class for some of my models that has some default activerecord behavior:

class Option < ActiveRecord::Base
  has_many :products

  def some_method
    #stuff
  end

  #etc..etc..
end

class ColorOption < Option
  #stuff...
end


class FabricOption < Option
  #stuff...
end

However, I want ColorOption and FabricOption to each be in their own tables. I do NOT want to use STI or have a table for the base class "Option". The only way I've gotten this to work is with some non-inheritance metaprogramming magic. But I wondered if there was a way to tell AR that the base class does not need a table. Its just there for extra behavior, and to put the other subclasses in their own table as usual.

Thanks, Craig

fregas
  • 3,192
  • 3
  • 25
  • 41

3 Answers3

59

What you want is an abstract class:

class Option < ActiveRecord::Base
  self.abstract_class = true
end

class ColorOption < Option
  ...
end

class FabricOption < Option
  ...
end
michaelrbock
  • 1,160
  • 1
  • 11
  • 20
François Beausoleil
  • 16,265
  • 11
  • 67
  • 90
  • do i have to set the table name, or can AR just infer that? – fregas Aug 20 '10 at 22:45
  • 7
    No need to for the set_table_name, at least in Rails 3.2. – ndp Mar 18 '12 at 18:18
  • 1
    Combining this with Szymon Przybył's approach gave me the best results. I added the following to the abstract class to automatically handle setting the table name for the inheriting classes: def self.inherited(child) child.table_name = child.name.underscore.pluralize end – tovodeverett Nov 26 '13 at 01:36
  • All I needed in rails 2.3 was the "abstract_class" part. Cheers! – Glenn Aug 08 '17 at 02:24
17

Looks like a case for a module that you include.

module Option
  def self.included(base)
    base.has_many :products
  end

  # other instance methods
end

class ColorOption < ActiveRecord::Base
  include Option
  set_table_name '???' # unless ColorOption / FabricOption have same table -> move to Option module

  #stuff...

end


class FabricOption < Option
  include Option
  set_table_name '???' # unless ColorOption / FabricOption have same table -> move to Option module

  #stuff...
end

More info: http://mediumexposure.com/multiple-table-inheritance-active-record/

Substantial
  • 6,684
  • 2
  • 31
  • 40
Stephan Wehner
  • 246
  • 2
  • 4
  • yeah i tried the module, but the module cannot see the active record methods such as has_many :products. It says the method is undefined. – fregas Aug 20 '10 at 18:35
  • fregas: make sure to include any class method calls, like `has_many` or validations inside the `self.included` method Stephan has in his example – Kyle Slattery Aug 20 '10 at 19:22
  • ah the self.included refers back to the class that is including the module. excellent, i'll try that! – fregas Aug 20 '10 at 21:08
  • 1
    sweet, that worked beautifully! yay ruby! Much more elegant than the meta programming was doing... – fregas Aug 20 '10 at 22:31
  • Don't you have a typo in the last class definition, it should inherit from `ActiveRecord::Base` not `Option`? – Cristian Jul 19 '13 at 12:12
6

I had similar problem, but I wanted also to change the way AR was setting table_name for my models, that for example MyProject model would have table name "MY_PROJECT".

I've achieved it by creating abstract AR class, as @FFrançois said, and with inherited method, where I'm changing table name, like this:

class MyModel < ActiveRecord::Base
  self.abstract_class = true

  def self.inherited(subclass)
    subclass.table_name = subclass.name.underscore.upcase
  end
end

class MyProject < MyModel
end

Now MyProject.table_name gives MY_PROJECT :)

Szymon Przybył
  • 660
  • 9
  • 10