1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

DRY up STI subclass logic

the newer method used for discriminating new records did not
use the older and more robust method used for instantiating
existing records, but did have a better post-check to ensure
the sublass was in the hierarchy. so move the descendants check
to find_sti_class, and then simply call find_sti_class from
subclass_from_attributes

now with fixed specs
This commit is contained in:
Cody Cutrer 2015-03-24 09:09:46 -06:00
parent d024bad4d1
commit 7c0f8c64fa
4 changed files with 29 additions and 22 deletions

View file

@ -55,7 +55,7 @@ module ActiveRecord
subclass = subclass_from_attributes(attrs) subclass = subclass_from_attributes(attrs)
end end
if subclass if subclass && subclass != self
subclass.new(*args, &block) subclass.new(*args, &block)
else else
super super
@ -167,17 +167,23 @@ module ActiveRecord
end end
def find_sti_class(type_name) def find_sti_class(type_name)
if store_full_sti_class subclass = begin
ActiveSupport::Dependencies.constantize(type_name) if store_full_sti_class
else ActiveSupport::Dependencies.constantize(type_name)
compute_type(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
rescue NameError unless subclass == self || descendants.include?(subclass)
raise SubclassNotFound, raise SubclassNotFound, "Invalid single-table inheritance type: #{type_name} is not a subclass of #{name}"
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " + end
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " + subclass
"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
def type_condition(table = arel_table) def type_condition(table = arel_table)
@ -198,14 +204,8 @@ module ActiveRecord
def subclass_from_attributes(attrs) def subclass_from_attributes(attrs)
subclass_name = attrs.with_indifferent_access[inheritance_column] subclass_name = attrs.with_indifferent_access[inheritance_column]
if subclass_name.present? && subclass_name != self.name if subclass_name.present?
subclass = subclass_name.safe_constantize find_sti_class(subclass_name)
unless descendants.include?(subclass)
raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
end
subclass
end end
end end
end end

View file

@ -1117,10 +1117,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_with_default_scope_on_the_target def test_has_many_through_with_default_scope_on_the_target
person = people(:michael) person = people(:michael)
assert_equal [posts(:thinking)], person.first_posts assert_equal [posts(:thinking).id], person.first_posts.map(&:id)
readers(:michael_authorless).update(first_post_id: 1) readers(:michael_authorless).update(first_post_id: 1)
assert_equal [posts(:thinking)], person.reload.first_posts assert_equal [posts(:thinking).id], person.reload.first_posts.map(&:id)
end end
def test_has_many_through_with_includes_in_through_association_scope def test_has_many_through_with_includes_in_through_association_scope

View file

@ -158,7 +158,7 @@ class EachTest < ActiveRecord::TestCase
end end
# posts.first will be ordered using id only. Title order scope should not apply here # posts.first will be ordered using id only. Title order scope should not apply here
assert_not_equal first_post, posts.first assert_not_equal first_post, posts.first
assert_equal posts(:welcome), posts.first assert_equal posts(:welcome).id, posts.first.id
end end
def test_find_in_batches_should_not_ignore_the_default_scope_if_it_is_other_then_order def test_find_in_batches_should_not_ignore_the_default_scope_if_it_is_other_then_order

View file

@ -185,6 +185,7 @@ class SubStiPost < StiPost
end end
class FirstPost < ActiveRecord::Base class FirstPost < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = 'posts' self.table_name = 'posts'
default_scope { where(:id => 1) } default_scope { where(:id => 1) }
@ -193,6 +194,7 @@ class FirstPost < ActiveRecord::Base
end end
class PostWithDefaultInclude < ActiveRecord::Base class PostWithDefaultInclude < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = 'posts' self.table_name = 'posts'
default_scope { includes(:comments) } default_scope { includes(:comments) }
has_many :comments, :foreign_key => :post_id has_many :comments, :foreign_key => :post_id
@ -204,16 +206,19 @@ class PostWithSpecialCategorization < Post
end end
class PostWithDefaultScope < ActiveRecord::Base class PostWithDefaultScope < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = 'posts' self.table_name = 'posts'
default_scope { order(:title) } default_scope { order(:title) }
end end
class SpecialPostWithDefaultScope < ActiveRecord::Base class SpecialPostWithDefaultScope < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = 'posts' self.table_name = 'posts'
default_scope { where(:id => [1, 5,6]) } default_scope { where(:id => [1, 5,6]) }
end end
class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = 'posts' self.table_name = 'posts'
has_many :comments, class_name: "CommentThatAutomaticallyAltersPostBody", foreign_key: :post_id has_many :comments, class_name: "CommentThatAutomaticallyAltersPostBody", foreign_key: :post_id
@ -223,6 +228,7 @@ class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
end end
class PostWithAfterCreateCallback < ActiveRecord::Base class PostWithAfterCreateCallback < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = 'posts' self.table_name = 'posts'
has_many :comments, foreign_key: :post_id has_many :comments, foreign_key: :post_id
@ -232,6 +238,7 @@ class PostWithAfterCreateCallback < ActiveRecord::Base
end end
class PostWithCommentWithDefaultScopeReferencesAssociation < ActiveRecord::Base class PostWithCommentWithDefaultScopeReferencesAssociation < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = 'posts' self.table_name = 'posts'
has_many :comment_with_default_scope_references_associations, foreign_key: :post_id has_many :comment_with_default_scope_references_associations, foreign_key: :post_id
has_one :first_comment, class_name: "CommentWithDefaultScopeReferencesAssociation", foreign_key: :post_id has_one :first_comment, class_name: "CommentWithDefaultScopeReferencesAssociation", foreign_key: :post_id