module EnterpriseMti::ClassMethods

Public Class Methods

container_classes() click to toggle source
# File lib/enterprise_mti/class_methods/class_methods.rb, line 76
def container_classes
  @container_classes
end

Public Instance Methods

has_one_superclass(superclass_symbol, options={}) click to toggle source
# File lib/enterprise_mti/class_methods/class_methods.rb, line 82
def has_one_superclass(superclass_symbol, options={})

  superclass_name                 = superclass_symbol.to_s.camelize
  superclass_qualified_name       = (options[:module] || '') + '::' + superclass_name
  superclass                      = superclass_qualified_name.constantize
  container_class                 = self
  container_class_camel_name      = self.name.demodulize
  container_class_underscore_name = self.name.demodulize.underscore
  superclass_relation_name        = superclass_symbol.to_s + "_superclass"
  superclass_relation_symbol      = superclass_relation_name.to_sym

  has_one superclass_relation_symbol, class_name: superclass_qualified_name

  # Create build and create methods for each subclass
  superclass.descendants.each do |subclass|
    subclass_underscore_name = subclass.name.demodulize.underscore
    actions = [:build, :create, :create!]
  
    actions.each do |action|
      action_suffix = nil
      action_name   = action.to_s
    
      if action.to_s.last(1) == '!'
        action_suffix = '!'
        action_name   = action.to_s[0..-2]
      end

      subclass.define_singleton_method "#{action_name}_with_superclass_instance#{action_suffix}" do |*args, &block|
        self.send("#{action_name}#{action_suffix}", *args, &block).superclass_instance = args.last[:superclass_instance]
      end
    
      superclass.class_eval do
      
        define_method "#{action_name}_#{subclass_underscore_name}_subclass#{action_suffix}" do |*args, &block|
          args.last[:superclass_instance] = self
          subclass.send "#{action_name}_with_superclass_instance#{action_suffix}", *args, &block
        end
      
        define_method "#{subclass_underscore_name}_with_superclass_instance=" do |value|
          value.superclass_instance = self
          self.send "#{subclass_underscore_name}=", value
        end
      end

      container_class.class_eval do
      
        define_method "#{action_name}_#{subclass_underscore_name}#{action_suffix}" do |*args, &block|
          unless superclass_instance = self.send(superclass_relation_symbol)
            superclass_instance = self.send("build_#{superclass_relation_name}")
          end
          superclass_instance.instance_eval do
            send "#{action_name}_#{subclass_underscore_name}_subclass#{action_suffix}", *args, &block
          end
        end
      end
    end
  end

  # Create getter
  define_method superclass_symbol do

    association_methods = superclass.descendants.collect { |subclass|
      reflection_symbol =
        subclass.to_s.demodulize.underscore.to_sym
      assoc = superclass.reflect_on_association(reflection_symbol)
      assoc ? assoc.name : nil
    }.compact
  
    if superclass_model_instance = self.send(superclass_relation_symbol)
      association_methods.collect{ |a|
        superclass_model_instance.send a
      }.inject do |a, b|
        a || b
      end
    end
  end

  # Create setter
  define_method "#{superclass_symbol.to_s}=" do |value|
    reflection_symbol =
      value.class.name.demodulize.underscore.to_sym

    unless superclass_instance = self.send(superclass_relation_symbol)
      superclass_instance = self.send("build_#{superclass_relation_name}")
    end
  
    reflection_assignment_method =
      superclass.reflect_on_association(reflection_symbol).name.to_s + '_with_superclass_instance='
    
    superclass_instance.send reflection_assignment_method, value
  end
end
has_subclass(subclass_symbol, options={}) click to toggle source
# File lib/enterprise_mti/class_methods/class_methods.rb, line 3
def has_subclass(subclass_symbol, options={})

  subclass_name           = subclass_symbol.to_s.camelize
  subclass                = nil
  subclass_qualified_name = (options[:module] || '') + '::' + subclass_name
  superclass              = self
  superclass_name         = self.name.demodulize.underscore
  superclass_symbol       = self.name.demodulize.underscore.to_sym
  table_name              = options[:table_name] || subclass_symbol.to_s.pluralize

  # Get subclass
  Kernel.const_get(options[:module] || 'Kernel').module_eval {
    subclass = const_get subclass_name }

  subclass.class_eval do
    # Set table name on subclass to prevent it from inheriting
    # superclass' attributes, etc.
    self.table_name = table_name
    has_one superclass_symbol
    attr_accessor :superclass_instance
    validates :superclass_instance, presence: true
  
    define_method "#{superclass_name}_transaction" do
      superclass_instance.send "#{subclass_symbol.to_s}_id=", self.id
      superclass_instance.save
    end
    after_save "#{superclass_name}_transaction".to_sym, on: :create
  end

  # Superclass belongs to subclass
  self.belongs_to subclass_symbol, :class_name => subclass_qualified_name
end
is_a_superclass() click to toggle source
# File lib/enterprise_mti/class_methods/class_methods.rb, line 36
def is_a_superclass

  # Require subclasses' files as dependencies, otherwise we get circular
  # dependency errors (see https://github.com/rails/rails/issues/3364)
  if descendants.empty?
    module_path_tokens = self.name.underscore.split('/')
    module_path_tokens[-1] += '_subclasses'
    Dir[Rails.root.join('app', 'models', File.join(module_path_tokens), "*.rb")].each do |file|
      require_dependency file
    end
  end

  # Call has_sublcass on the superclass for each subclass
  descendants.each do |subclass|
    name_tokens = subclass.to_s.split('::')
    subclass_name = name_tokens.last
    module_name = name_tokens[0...-1].join('::') if name_tokens.count > 1
    self.send :has_subclass, subclass_name.underscore.to_sym, :module => module_name
  end

  # Populate container_classes, an array that contains classes that "have"
  # the superclass (and thus have foreign key constraints on the
  # superclass)
  reflection_class = Proc.new do |r|
    if r.options[:class_name]
      r.options[:class_name].constantize
    else
      prefix = self.name.deconstantize || ''
      "#{prefix}::#{r.name.to_s.camelize}".constantize
    end
  end
  
  @container_classes = self.reflect_on_all_associations.keep_if { |r|
    r.macro == :belongs_to &&
    #self.column_names.include?(r.association_foreign_key) &&
    !self.descendants.include?(reflection_class.call(r))
  }.collect { |r| reflection_class.call(r) }

  # Add read-only access to container_classes
  class << self
    def container_classes
      @container_classes
    end
  end
end