diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb index 9ae452e7a1..6f17b755db 100644 --- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb @@ -6,7 +6,7 @@ module ActiveRecord class BelongsToPolymorphicAssociation < BelongsToAssociation #:nodoc: def klass type = owner[reflection.foreign_type] - type.presence && type.constantize + type.presence && owner.class.polymorphic_class_for(type) end def target_changed? diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index 5ca48fa18c..bb67f7f1b0 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -162,14 +162,40 @@ module ActiveRecord defined?(@abstract_class) && @abstract_class == true end + # Returns the value to be stored in the inheritance column for STI. def sti_name store_full_sti_class ? name : name.demodulize end + # Returns the class for the provided +type_name+. + # + # It is used to find the class correspondent to the value stored in the inheritance column. + def sti_class_for(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 + + # Returns the value to be stored in the polymorphic type column for Polymorphic Associations. def polymorphic_name base_class.name end + # Returns the class for the provided +name+. + # + # It is used to find the class correspondent to the value stored in the polymorphic type column. + def polymorphic_class_for(name) + name.constantize + end + def inherited(subclass) subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new) super @@ -224,22 +250,12 @@ module ActiveRecord def find_sti_class(type_name) type_name = base_class.type_for_attribute(inheritance_column).cast(type_name) - subclass = begin - 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 + subclass = sti_class_for(type_name) + unless subclass == self || descendants.include?(subclass) raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}" end + subclass end