module ActsAsInheritable

Constants

VERSION

Public Instance Methods

acts_as_inheritable(options) click to toggle source
# File lib/acts_as_inheritable.rb, line 5
def acts_as_inheritable(options)
  fail ArgumentError, "Hash expected, got #{options.class.name}" unless options.is_a?(Hash)
  fail ArgumentError, 'Empty options' if options[:attributes].blank? && options[:associations].blank?

  class_attribute :inheritable_configuration

  self.inheritable_configuration = {}
  self.inheritable_configuration.merge!(options)

  class_eval do
    def has_parent?
      parent.present?
    end

    # This is an inheritable recursive method that iterates over all of the
    # relations defined on `INHERITABLE_ASSOCIATIONS`. For each instance on
    # each relation it re-creates it.
    def inherit_relations(model_parent = send(:parent), current = self)
      if model_parent && current.class.method_defined?(:inheritable_configuration) && current.class.inheritable_configuration[:associations]
        current.class.inheritable_configuration[:associations].each do |relation|
          parent_relation = model_parent.send(relation)
          relation_instances = parent_relation.respond_to?(:each) ? parent_relation : [parent_relation].compact
          relation_instances.each do |relation_instance|
            inherit_instance(current, model_parent, relation, relation_instance)
          end
        end
      end
    end

    def inherit_instance(current, model_parent, relation, relation_instance)
      new_relation = relation_instance.dup
      belongs_to_associations_names = model_parent.class.reflect_on_all_associations(:belongs_to).collect(&:name)
      saved =
        # Is a `belongs_to` association
        if belongs_to_associations_names.include?(relation.to_sym)
          # You can define your own 'dup' method with a `duplicate!` signature
          new_relation = relation_instance.duplicate! if relation_instance.respond_to?(:duplicate!)
          current.send("#{relation}=", new_relation)
          current.save
        else
          # Is a `has_one | has_many` association
          parent_name = verify_parent_name(new_relation, model_parent)
          new_relation.send("#{parent_name}=", current)
          new_relation.save
        end
      inherit_relations(relation_instance, new_relation) if saved
    end

    def verify_parent_name(new_relation, model_parent)
      parent_name = model_parent.class.to_s.downcase
      return parent_name if new_relation.respond_to?(parent_name)
      many_and_one_associations = model_parent.class.reflect_on_all_associations.select { |a| a.macro != :belongs_to }
      many_and_one_associations.each do |association|
        next unless association.klass.to_s.downcase == new_relation.class.to_s.downcase && association.options.key?(:as)
        as = association.options[:as].to_s
        if new_relation.respond_to?(as) && !new_relation.respond_to?(parent_name)
          parent_name = as
          break
        end
      end
      # Relations has a diffeent name
      unless new_relation.respond_to?(parent_name)
        new_relation.class.reflections.each_key do |reflection|
          next unless new_relation.class.reflections[reflection].class_name == model_parent.class.name
          parent_name = reflection
          break
        end
      end
      parent_name
    end

    def inherit_attributes(force = false, not_force_for = [], method_to_update = nil)
      available_methods = ['update_attributes', 'update_columns']
      if has_parent? && self.class.inheritable_configuration[:attributes]
        # Attributes
        self.class.inheritable_configuration[:attributes].each do |attribute|
          current_val = send(attribute)
          if (force && !not_force_for.include?(attribute)) || current_val.blank?
            if method_to_update && available_methods.include?(method_to_update)
              send(method_to_update, {attribute => parent.send(attribute)})
            else
              send("#{attribute}=", parent.send(attribute))
            end
          end
        end
      end
    end
  end
end
has_parent?() click to toggle source
# File lib/acts_as_inheritable.rb, line 15
def has_parent?
  parent.present?
end
inherit_attributes(force = false, not_force_for = [], method_to_update = nil) click to toggle source
# File lib/acts_as_inheritable.rb, line 76
def inherit_attributes(force = false, not_force_for = [], method_to_update = nil)
  available_methods = ['update_attributes', 'update_columns']
  if has_parent? && self.class.inheritable_configuration[:attributes]
    # Attributes
    self.class.inheritable_configuration[:attributes].each do |attribute|
      current_val = send(attribute)
      if (force && !not_force_for.include?(attribute)) || current_val.blank?
        if method_to_update && available_methods.include?(method_to_update)
          send(method_to_update, {attribute => parent.send(attribute)})
        else
          send("#{attribute}=", parent.send(attribute))
        end
      end
    end
  end
end
inherit_instance(current, model_parent, relation, relation_instance) click to toggle source
# File lib/acts_as_inheritable.rb, line 34
def inherit_instance(current, model_parent, relation, relation_instance)
  new_relation = relation_instance.dup
  belongs_to_associations_names = model_parent.class.reflect_on_all_associations(:belongs_to).collect(&:name)
  saved =
    # Is a `belongs_to` association
    if belongs_to_associations_names.include?(relation.to_sym)
      # You can define your own 'dup' method with a `duplicate!` signature
      new_relation = relation_instance.duplicate! if relation_instance.respond_to?(:duplicate!)
      current.send("#{relation}=", new_relation)
      current.save
    else
      # Is a `has_one | has_many` association
      parent_name = verify_parent_name(new_relation, model_parent)
      new_relation.send("#{parent_name}=", current)
      new_relation.save
    end
  inherit_relations(relation_instance, new_relation) if saved
end
inherit_relations(model_parent = send(:parent), current = self) click to toggle source

This is an inheritable recursive method that iterates over all of the relations defined on `INHERITABLE_ASSOCIATIONS`. For each instance on each relation it re-creates it.

# File lib/acts_as_inheritable.rb, line 22
def inherit_relations(model_parent = send(:parent), current = self)
  if model_parent && current.class.method_defined?(:inheritable_configuration) && current.class.inheritable_configuration[:associations]
    current.class.inheritable_configuration[:associations].each do |relation|
      parent_relation = model_parent.send(relation)
      relation_instances = parent_relation.respond_to?(:each) ? parent_relation : [parent_relation].compact
      relation_instances.each do |relation_instance|
        inherit_instance(current, model_parent, relation, relation_instance)
      end
    end
  end
end
verify_parent_name(new_relation, model_parent) click to toggle source
# File lib/acts_as_inheritable.rb, line 53
def verify_parent_name(new_relation, model_parent)
  parent_name = model_parent.class.to_s.downcase
  return parent_name if new_relation.respond_to?(parent_name)
  many_and_one_associations = model_parent.class.reflect_on_all_associations.select { |a| a.macro != :belongs_to }
  many_and_one_associations.each do |association|
    next unless association.klass.to_s.downcase == new_relation.class.to_s.downcase && association.options.key?(:as)
    as = association.options[:as].to_s
    if new_relation.respond_to?(as) && !new_relation.respond_to?(parent_name)
      parent_name = as
      break
    end
  end
  # Relations has a diffeent name
  unless new_relation.respond_to?(parent_name)
    new_relation.class.reflections.each_key do |reflection|
      next unless new_relation.class.reflections[reflection].class_name == model_parent.class.name
      parent_name = reflection
      break
    end
  end
  parent_name
end