require 'active_support/concern'
module ActiveRecord
# == Multi table inheritance
#
# PostgreSQL allows for table inheritance. To enable this in ActiveRecord, ensure that the
# inheritance_column is named "tableoid" (can be changed by setting Base.inheritance_column).
# This means that an inheritance looking like this:
#
# class Company < ActiveRecord::Base;
# self.inheritance_column = 'tableoid'
# end
# class Firm < Company; end
# class Client < Company; end
# class PriorityClient < Client; end
#
# When you do Firm.create(name: "37signals"), this record will be saved in
# the firms table which inherits from companies. You can then fetch this row again using
# Company.where(name: '37signals').first and it will return a Firm object.
#
# Note, all the attributes for all the cases are kept in the same table. Read more:
# http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
#
module MTI
module Inheritance
extend ActiveSupport::Concern
included do
scope :discern_inheritance, -> {
}
end
module ClassMethods
def inherited(child)
child.uses_mti if self.uses_mti?
super
end
def uses_mti
self.inheritance_column = nil
@uses_mti = true
end
def using_multi_table_inheritance?(klass = self)
klass.uses_mti?
end
def uses_mti?
@uses_mti ||= false
end
private
# Called by +instantiate+ to decide which class to use for a new
# record instance. For single-table inheritance, we check the record
# for a +type+ column and return the corresponding class.
def discriminate_class_for_record(record)
if using_multi_table_inheritance?(base_class)
find_mti_class(record)
elsif using_single_table_inheritance?(record)
find_sti_class(record[inheritance_column])
else
super
end
end
# Search descendants for one who's table_name is equal to the returned tableoid.
# This indicates the class of the record
def find_mti_class(record)
record['tableoid'].classify.constantize
rescue NameError => e
descendants.find(Proc.new{ self }) { |d| d.table_name == record['tableoid'] }
end
# Type condition only applies if it's STI, otherwise it's
# done for free by querying the inherited table in MTI
def type_condition(table = arel_table)
if using_multi_table_inheritance?
nil
else
sti_column = table[inheritance_column]
sti_names = ([self] + descendants).map { |model| model.sti_name }
sti_column.in(sti_names)
end
end
end
end
end
end