mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Move instantiation responsibilities from Inheritance to Persistence. Have Inheritance#discriminate_class_for_record handle STI lookup duties.
This commit is contained in:
parent
7ad590d25f
commit
f2902eb8e0
2 changed files with 51 additions and 25 deletions
|
@ -92,15 +92,6 @@ module ActiveRecord
|
||||||
store_full_sti_class ? name : name.demodulize
|
store_full_sti_class ? name : name.demodulize
|
||||||
end
|
end
|
||||||
|
|
||||||
# Finder methods must instantiate through this method to work with the
|
|
||||||
# single-table inheritance model that makes it possible to create
|
|
||||||
# objects of different types from the same table.
|
|
||||||
def instantiate(record, column_types = {})
|
|
||||||
sti_class = find_sti_class(record[inheritance_column])
|
|
||||||
column_types = sti_class.decorate_columns(column_types)
|
|
||||||
sti_class.allocate.init_with('attributes' => record, 'column_types' => column_types)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
# Returns the class type of the record using the current module as a prefix. So descendants of
|
# Returns the class type of the record using the current module as a prefix. So descendants of
|
||||||
|
@ -132,26 +123,35 @@ module ActiveRecord
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def find_sti_class(type_name)
|
# Called by +instantiate+ to decide which class to use for a new
|
||||||
if type_name.blank? || !columns_hash.include?(inheritance_column)
|
# record instance. For single-table inheritance, we check the record
|
||||||
self
|
# for a +type+ column and return the corresponding class.
|
||||||
|
def discriminate_class_for_record(record)
|
||||||
|
if using_single_table_inheritance?(record)
|
||||||
|
find_sti_class(record[inheritance_column])
|
||||||
else
|
else
|
||||||
begin
|
super
|
||||||
if store_full_sti_class
|
|
||||||
ActiveSupport::Dependencies.constantize(type_name)
|
|
||||||
else
|
|
||||||
compute_type(type_name)
|
|
||||||
end
|
|
||||||
rescue NameError
|
|
||||||
raise SubclassNotFound,
|
|
||||||
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
|
|
||||||
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
|
|
||||||
"Please rename this column if you didn't intend it to be used for storing the inheritance class " +
|
|
||||||
"or overwrite #{name}.inheritance_column to use another column for that information."
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def using_single_table_inheritance?(record)
|
||||||
|
record[inheritance_column].present? && columns_hash.include?(inheritance_column)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_sti_class(type_name)
|
||||||
|
if store_full_sti_class
|
||||||
|
ActiveSupport::Dependencies.constantize(type_name)
|
||||||
|
else
|
||||||
|
compute_type(type_name)
|
||||||
|
end
|
||||||
|
rescue NameError
|
||||||
|
raise SubclassNotFound,
|
||||||
|
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
|
||||||
|
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
|
||||||
|
"Please rename this column if you didn't intend it to be used for storing the inheritance class " +
|
||||||
|
"or overwrite #{name}.inheritance_column to use another column for that information."
|
||||||
|
end
|
||||||
|
|
||||||
def type_condition(table = arel_table)
|
def type_condition(table = arel_table)
|
||||||
sti_column = table[inheritance_column.to_sym]
|
sti_column = table[inheritance_column.to_sym]
|
||||||
sti_names = ([self] + descendants).map { |model| model.sti_name }
|
sti_names = ([self] + descendants).map { |model| model.sti_name }
|
||||||
|
|
|
@ -38,6 +38,32 @@ module ActiveRecord
|
||||||
object
|
object
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
||||||
|
# the appropriate class.
|
||||||
|
#
|
||||||
|
# For example, +Post.all+ may return Comments, Messages, and Emails
|
||||||
|
# by storing the record's subclass in a +type+ attribute. By calling
|
||||||
|
# +instantiate+ instead of +new+, finder methods ensure they get new
|
||||||
|
# instances of the appropriate class for each record.
|
||||||
|
#
|
||||||
|
# See +ActiveRecord::Inheritance#discriminate_class_for_record+ to see
|
||||||
|
# how this "single-table" inheritance mapping is implemented.
|
||||||
|
def instantiate(record, column_types = {})
|
||||||
|
klass = discriminate_class_for_record(record)
|
||||||
|
column_types = klass.decorate_columns(column_types)
|
||||||
|
klass.allocate.init_with('attributes' => record, 'column_types' => column_types)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
# Called by +instantiate+ to decide which class to use for a new
|
||||||
|
# record instance.
|
||||||
|
#
|
||||||
|
# See +ActiveRecord::Inheritance#discriminate_class_for_record+ for
|
||||||
|
# the single-table inheritance discriminator.
|
||||||
|
def discriminate_class_for_record(record)
|
||||||
|
self
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns true if this object hasn't been saved yet -- that is, a record
|
# Returns true if this object hasn't been saved yet -- that is, a record
|
||||||
|
|
Loading…
Reference in a new issue