# module ActiveRecord::Inheritance::ClassMethods

### Attributes

abstract_class[RW]

Set this to true if this is an abstract class (see `abstract_class?`). If you are using inheritance with ActiveRecord and don't want child classes to utilize the implied STI table name of the parent class, this will need to be true. For example, given the following:

    class SuperClass < ActiveRecord::Base
      self.abstract_class = true
    end
    class Child < SuperClass
      self.table_name = 'the_table_i_really_want'
    end

`self.abstract_class = true` is required to make `Child<.find,.create, or any Arel method>` use `the_table_i_really_want` instead of a table called `super_classes`

### Public Instance Methods

abstract_class?() click to toggle source

Returns whether this class is an abstract class or not.

    # File lib/active_record/inheritance.rb, line 125
    def abstract_class?
      defined?(@abstract_class) && @abstract_class == true
    end

base_class() click to toggle source

Returns the class descending directly from ActiveRecord::Base, or an abstract class, if any, in the inheritance hierarchy.

If A extends ActiveRecord::Base, A.base_class will return A. If B descends from A through some arbitrarily deep hierarchy, B.base_class will return A.

If B < A and C < B and if A is an #abstract_class then both B.base_class and C.base_class would return B as the answer since A is an abstract_class.

    # File lib/active_record/inheritance.rb, line 95
    def base_class
      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

descends_from_active_record?() click to toggle source

Returns `true` if this does not need STI type condition. Returns `false` if STI type condition needs to be applied.

    # File lib/active_record/inheritance.rb, line 72
    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

inherited(subclass) click to toggle source
Calls superclass method

    # File lib/active_record/inheritance.rb, line 133
    def inherited(subclass)
      subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
      super
    end

new(*args, &block) click to toggle source

Determines if one of the attributes passed in is the inheritance column, and if the inheritance column is attr accessible, it initializes an instance of the given subclass instead of the base class.

Calls superclass method

    # File lib/active_record/inheritance.rb, line 49
    def new(*args, &block)
      if abstract_class? || self == Base
        raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
      end

      attrs = args.first
      if has_attribute?(inheritance_column)
        subclass = subclass_from_attributes(attrs)

        if subclass.nil? && base_class == self
          subclass = subclass_from_attributes(column_defaults)
        end
      end

      if subclass && subclass != self
        subclass.new(*args, &block)
      else
        super
      end
    end

sti_name() click to toggle source

    # File lib/active_record/inheritance.rb, line 129
    def sti_name
      store_full_sti_class ? name : name.demodulize
    end

### Protected Instance Methods

compute_type(type_name) click to toggle source

Returns the class type of the record using the current module as a prefix. So descendants of MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.

    # File lib/active_record/inheritance.rb, line 142
    def compute_type(type_name)
      if type_name.start_with?("::".freeze)
        # If the type is prefixed with a scope operator then we assume that
        # the type_name is an absolute reference.
        ActiveSupport::Dependencies.constantize(type_name)
      else
        type_candidate = @_type_candidates_cache[type_name]
        if type_candidate && type_constant = ActiveSupport::Dependencies.safe_constantize(type_candidate)
          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 = ActiveSupport::Dependencies.safe_constantize(candidate)
          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

### Private Instance Methods

discriminate_class_for_record(record) click to toggle source

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.

Calls superclass method

    # File lib/active_record/inheritance.rb, line 175
    def discriminate_class_for_record(record)
      if using_single_table_inheritance?(record)
        find_sti_class(record[inheritance_column])
      else
        super
      end
    end

find_sti_class(type_name) click to toggle source

    # File lib/active_record/inheritance.rb, line 187
    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
      unless subclass == self || descendants.include?(subclass)
        raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
      end
      subclass
    end

subclass_from_attributes(attrs) click to toggle source

Detect the subclass from the inheritance column of attrs. If the inheritance column value is not self or a valid subclass, raises ActiveRecord::SubclassNotFound

    # File lib/active_record/inheritance.rb, line 217
    def subclass_from_attributes(attrs)
      attrs = attrs.to_h if attrs.respond_to?(:permitted?)
      if attrs.is_a?(Hash)
        subclass_name = attrs.with_indifferent_access[inheritance_column]

        if subclass_name.present?
          find_sti_class(subclass_name)
        end
      end
    end

type_condition(table = arel_table) click to toggle source

    # File lib/active_record/inheritance.rb, line 208
    def type_condition(table = arel_table)
      sti_column = arel_attribute(inheritance_column, table)
      sti_names  = ([self] + descendants).map(&:sti_name)

      sti_column.in(sti_names)
    end

using_single_table_inheritance?(record) click to toggle source

    # File lib/active_record/inheritance.rb, line 183
    def using_single_table_inheritance?(record)
      record[inheritance_column].present? && has_attribute?(inheritance_column)
    end