module ActiveRecord::Inheritance::ClassMethods
def abstract_class?
def abstract_class? defined?(@abstract_class) && @abstract_class == true end
def base_class?
Returns whether the class is a base class.
def base_class? base_class == self end
def compute_type(type_name)
Returns the class type of the record using the current module as a prefix. So descendants of
def compute_type(type_name) if type_name.start_with?("::") # If the type is prefixed with a scope operator then we assume that # the type_name is an absolute reference. type_name.constantize else type_candidate = @_type_candidates_cache[type_name] if type_candidate && type_constant = type_candidate.safe_constantize return type_constant end # Build a list of candidates to search for candidates = [] name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" } candidates << type_name candidates.each do |candidate| constant = candidate.safe_constantize if candidate == constant.to_s @_type_candidates_cache[type_name] = candidate return constant end end raise NameError.new("uninitialized constant #{candidates.first}", candidates.first) end end
def descends_from_active_record?
Returns +true+ if this does not need STI type condition. Returns
def descends_from_active_record? if self == Base false elsif superclass.abstract_class? superclass.descends_from_active_record? else superclass == Base || !columns_hash.include?(inheritance_column) end end
def discriminate_class_for_record(record)
record instance. For single-table inheritance, we check the record
Called by +instantiate+ to decide which class to use for a new
def discriminate_class_for_record(record) if using_single_table_inheritance?(record) find_sti_class(record[inheritance_column]) else super end end
def dup # :nodoc:
def dup # :nodoc: # `initialize_dup` / `initialize_copy` don't work when defined # in the `singleton_class`. other = super other.set_base_class other end
def find_sti_class(type_name)
def find_sti_class(type_name) type_name = base_class.type_for_attribute(inheritance_column).cast(type_name) 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
def finder_needs_type_condition? # :nodoc:
def finder_needs_type_condition? # :nodoc: # This is like this because benchmarking justifies the strange :false stuff :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true) end
def inherited(subclass)
def inherited(subclass) subclass.set_base_class subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new) super end
def initialize_clone(other) # :nodoc:
def initialize_clone(other) # :nodoc: super set_base_class end
def new(attributes = nil, &block)
and if the inheritance column is attr accessible, it initializes an
Determines if one of the attributes passed in is the inheritance column,
def new(attributes = nil, &block) if abstract_class? || self == Base raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated." end if _has_attribute?(inheritance_column) subclass = subclass_from_attributes(attributes) if subclass.nil? && scope_attributes = current_scope&.scope_for_create subclass = subclass_from_attributes(scope_attributes) end if subclass.nil? && base_class? subclass = subclass_from_attributes(column_defaults) end end if subclass && subclass != self subclass.new(attributes, &block) else super end end
def polymorphic_class_for(name)
Returns the class for the provided +name+.
def polymorphic_class_for(name) if store_full_class_name name.constantize else compute_type(name) end end
def polymorphic_name
def polymorphic_name store_full_class_name ? base_class.name : base_class.name.demodulize end
def primary_abstract_class
will share a database connection with Active Record. It is the class
ApplicationRecord for your primary abstract class. This class
This is useful if your application uses a different class than
Sets the application record class for Active Record
def primary_abstract_class if ActiveRecord.application_record_class && ActiveRecord.application_record_class.name != name raise ArgumentError, "The `primary_abstract_class` is already set to #{ActiveRecord.application_record_class.inspect}. There can only be one `primary_abstract_class` in an application." end self.abstract_class = true ActiveRecord.application_record_class = self end
def set_base_class # :nodoc:
def set_base_class # :nodoc: @base_class = if self == Base self else unless self < Base raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord" end if superclass == Base || superclass.abstract_class? self else superclass.base_class end end end
def sti_class_for(type_name)
Returns the class for the provided +type_name+.
def sti_class_for(type_name) if store_full_sti_class && store_full_class_name type_name.constantize 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 sti_name
def sti_name store_full_sti_class && store_full_class_name ? name : name.demodulize end
def subclass_from_attributes(attrs)
Detect the subclass from the inheritance column of attrs. If the inheritance column value
def subclass_from_attributes(attrs) attrs = attrs.to_h if attrs.respond_to?(:permitted?) if attrs.is_a?(Hash) subclass_name = attrs[inheritance_column] || attrs[inheritance_column.to_sym] if subclass_name.present? find_sti_class(subclass_name) end end end
def type_condition(table = arel_table)
def type_condition(table = arel_table) sti_column = table[inheritance_column] sti_names = ([self] + descendants).map(&:sti_name) predicate_builder.build(sti_column, sti_names) end
def using_single_table_inheritance?(record)
def using_single_table_inheritance?(record) record[inheritance_column].present? && _has_attribute?(inheritance_column) end