module ActiveRecord::Mti::ClassMethods

Public Class Methods

as_base_instance() { || ... } click to toggle source

Thread safe execution of a block in a “base instance mode”

# File lib/mti.rb, line 55
def self.as_base_instance
  @@base_instance_mode_lock.synchronize do
    @@base_instance_mode = true
    result = yield
    @@base_instance_mode = false
    result
  end
end
instantiate(*_) click to toggle source

Override ActiveRecord's instantiation method which builds an object from a record Return the base class object when in “base instance mode” and the implementation object otherwise

Calls superclass method
# File lib/mti.rb, line 46
def self.instantiate(*_)
  if @@base_instance_mode
    super
  else
    super.public_send(mti_name)
  end
end

Public Instance Methods

mti_base() click to toggle source
# File lib/mti.rb, line 27
def mti_base
  class_attribute :mti_name
  self.mti_name = self.to_s.underscore.to_sym

  @@base_instance_mode = false
  @@base_instance_mode_lock = Mutex.new

  # Always fetch with the implementation
  default_scope lambda { includes(mti_name) }

  # Implementation model association
  belongs_to mti_name,
             polymorphic: true,
             inverse_of: mti_name

  # Override ActiveRecord's instantiation method
  # which builds an object from a record
  # Return the base class object when in "base instance mode"
  # and the implementation object otherwise
  def self.instantiate(*_)
    if @@base_instance_mode
      super
    else
      super.public_send(mti_name)
    end
  end

  # Thread safe execution of a block in a "base instance mode"
  def self.as_base_instance
    @@base_instance_mode_lock.synchronize do
      @@base_instance_mode = true
      result = yield
      @@base_instance_mode = false
      result
    end
  end
end
mti_implementation_of(mti_base_name) click to toggle source
# File lib/mti.rb, line 65
def mti_implementation_of(mti_base_name)
  class_attribute :mti_base
  self.mti_base = mti_base_name.to_s.classify.constantize

  # Base model association
  has_one mti_base_name.to_sym,
          :as => mti_base_name.to_sym,
          :autosave => true,
          :dependent => :destroy,
          :validate => true,
          :inverse_of => mti_base_name.to_sym

  # When calling the base object from the implementation
  # switch the base's class to the "base instance mode"
  # to receive the base class object instead of another
  # implementation object and avoid an infinite loop
  define_method "#{mti_base_name}_with_reverse" do          # def role_with_reverse
    mti_base.as_base_instance do                            #   Role.as_base_instance do
      send("#{mti_base_name}_without_reverse")              #     role_without_reverse
    end                                                     #   end
  end                                                       # end
  alias_method_chain mti_base_name, :reverse                # alias_method_chain :role, :reverse

  # Auto build base model
  define_method "#{mti_base_name}_with_autobuild" do        # def role_with_autobuild
    public_send("#{mti_base_name}_without_autobuild") ||    #   role_without_autobuild ||
        public_send("build_#{mti_base_name}")               #     build_role
  end                                                       # end
  alias_method_chain mti_base_name, :autobuild              # alias_method_chain :role, :autobuild

  # Delegate attributes
  mti_base.content_columns.map(&:name).each do |attr|
    delegate attr, "#{attr}=", "#{attr}?",
             :to => mti_base_name.to_sym
  end

  # Delegate associations
  mti_base.reflections.keys
          .tap { |k| k.delete(mti_base_name.to_sym) }
          .each do |association|
    delegate association, "#{association}=",
             :to => mti_base_name.to_sym
  end

  delegate_missing_to mti_base_name

  accepts_nested_attributes_for mti_base_name

  define_method "#{mti_base_name}_id" do
    public_send(mti_base_name).id
  end
end