class FlatMap::OpenMapper::Factory

Mapper factory objects are used to store mounting and trait definitions and to instantiate and setup corresponding mapper objects thereafter. Factory objects are stored by mapper classes in opposite to actual mounted mappers that are stored by mapper objects themselves.

Public Class Methods

new(identifier, options = {}, &block) click to toggle source

Initializes factory with an identifier (name of a mounted mapper, or the actual class for a trait) and a set of options. Those args are used to create actual mapper object for the host mapper.

@param [Symbol, Class] identifier name of a mapper or mapper class

itself

@param [Hash] options

# File lib/flat_map/open_mapper/factory.rb, line 14
def initialize(identifier, options = {}, &block)
  @identifier, @options, @extension = identifier, options, block
end

Public Instance Methods

create(mapper, *owner_traits) click to toggle source

Create a new mapper object for mounting. If the factory is traited, the new mapper is a part of a host mapper, and is ‘owned’ by it. Otherwise, assign the name of the factory to it to be able to find it later on.

@param [FlatMap::OpenMapper] mapper Host mapper @param [*Symbol] owner_traits List of traits to be applied to a newly created mapper

# File lib/flat_map/open_mapper/factory.rb, line 206
def create(mapper, *owner_traits)
  save_order = @options[:save] || fetch_save_order(mapper) || :after
  new_one = mapper_class.new(fetch_target_from(mapper), *(traits + owner_traits).uniq, &@extension)
  if traited?
    new_one.owner = mapper
  else
    new_one.host = mapper
    new_one.name = @identifier
    new_one.save_order = save_order

    if (suffix = @options[:suffix] || mapper.suffix).present?
      new_one.suffix = suffix
      new_one.name   = :"#{@identifier}_#{suffix}"
    else
      new_one.name = @identifier
    end
  end
  new_one
end
explicit_target(mapper) click to toggle source

Try to use explicit target definition passed in options to fetch a target. If this value is a Proc, will call it with owner target as argument.

@param [FlatMap::OpenMapper] mapper @return [Object, nil] target for new mapper.

# File lib/flat_map/open_mapper/factory.rb, line 119
def explicit_target(mapper)
  if @options.key?(:target)
    target = @options[:target]
    case target
    when Proc   then target.call(mapper.target)
    when Symbol then mapper.send(target)
    else target
    end
  end
end
fetch_save_order(mapper) click to toggle source

Return order relative to target of the passed mapper in which mapper to be created should be saved. In particular, targets of :belongs_to associations should be saved before target of mapper is saved.

@param [FlatMap::OpenMapper] mapper @return [Symbol]

# File lib/flat_map/open_mapper/factory.rb, line 191
def fetch_save_order(mapper)
  return :after unless mapper.is_a?(ModelMapper)

  reflection = reflection_from_target(mapper.target)
  return unless reflection.present?
  reflection.macro == :belongs_to ? :before : :after
end
fetch_target_from(mapper) click to toggle source

Fetch the target for the mapper being created based on target of a host mapper.

@param [FlatMap::OpenMapper] mapper Host mapper @return [Object] target for new mapper

# File lib/flat_map/open_mapper/factory.rb, line 91
def fetch_target_from(mapper)
  owner_target = mapper.target

  return owner_target if traited?

  if open_mount?
    explicit_target(mapper) || new_target
  else
    explicit_target(mapper) ||
      target_from_association(owner_target) ||
      target_from_name(owner_target) ||
      new_target
  end
end
mapper_class() click to toggle source

Return the anonymous trait class if the factory defines a trait. Fetch and return the class of a mapper defined by a symbol.

@return [Class] ancestor of {FlatMap::OpenMapper}

# File lib/flat_map/open_mapper/factory.rb, line 50
def mapper_class
  @mapper_class ||= begin
    case
    when traited?        then @identifier
    when @options[:open] then open_mapper_class
    else
      class_name = @options[:mapper_class_name] || "#{name.to_s.camelize}Mapper"
      class_name.constantize
    end
  end
end
model_mount?() click to toggle source

Return true if mapper to me mounted is ModelMapper

@return [Boolean]

# File lib/flat_map/open_mapper/factory.rb, line 76
def model_mount?
  mapper_class < ModelMapper
end
name() click to toggle source

Return the name of the mapper being defined by the factory. Return nil for the traited factory.

@return [Symbol, nil]

# File lib/flat_map/open_mapper/factory.rb, line 29
def name
  traited? ? nil : @identifier
end
new_target() click to toggle source

Return new instance of target_class of the mapper.

@return [Object]

# File lib/flat_map/open_mapper/factory.rb, line 109
def new_target
  mapper_class.target_class.new
end
open_mapper_class() click to toggle source

Return descendant of OpenMapper class to be used when mounting open mappers.

@return [Class]

# File lib/flat_map/open_mapper/factory.rb, line 66
def open_mapper_class
  klass = Class.new(OpenMapper)
  klass_name = "#{name.to_s.camelize}Mapper"
  klass.singleton_class.send(:define_method, :name){ klass_name }
  klass
end
open_mount?() click to toggle source

Return true if mapper to me mounted is not ModelMapper, i.e. OpenMapper.

@return [Boolean]

# File lib/flat_map/open_mapper/factory.rb, line 83
def open_mount?
  !model_mount?
end
reflection_from_target(target) click to toggle source

Try to retreive an association reflection that has a name corresponding to the one of self

@param [ActiveRecord::Base] target @return [ActiveRecord::Reflection::AssociationReflection, nil]

# File lib/flat_map/open_mapper/factory.rb, line 170
def reflection_from_target(target)
  return unless name.present? && target.is_a?(ActiveRecord::Base)
  target_class = target.class
  reflection  = target_class.reflect_on_association(name)
  reflection || target_class.reflect_on_association(name.to_s.pluralize.to_sym)
end
required_for_any_trait?(traits) click to toggle source

Return true if the factory is required to be able to apply a trait for the host mapper. For example, it is required if its name is listed in traits. It is also required if it has nested traits with names listed in traits.

@param [Array<Symbol>] traits list of traits @return [Boolean]

# File lib/flat_map/open_mapper/factory.rb, line 233
def required_for_any_trait?(traits)
  return true unless traited?

  traits.include?(trait_name) ||
    mapper_class.mountings.any? do |factory|
      factory.traited? &&
        factory.required_for_any_trait?(traits)
    end
end
target_from_association(owner_target) click to toggle source

Try to fetch the target for a new mapper being mounted, based on correspondence of the mounting name and presence of the association with a similar name in the host mapper.

For example:

class Foo < ActiveRecord::Base
  has_one :baz
  has_many :bars
end

class FooMapper < FlatMap::Mapper
  # target of this mapper is the instance of Foo. Lets reference it as 'foo'
  mount :baz # This will look for BazMapper, and will try to fetch a target for
             # it based on :has_one association, i.e. foo.baz || foo.build_baz

  mount :bar # This will look for BarMapper, and will try to fetch a target for
             # it based on :has_many association, i.e. foo.bars.build
end
# File lib/flat_map/open_mapper/factory.rb, line 148
def target_from_association(owner_target)
  return unless owner_target.is_a?(ActiveRecord::Base)

  reflection = reflection_from_target(owner_target)
  return unless reflection.present?

  reflection_macro = reflection.macro
  case
  when reflection_macro == :has_one && reflection.options[:is_current]
    owner_target.send("effective_#{name}")
  when reflection_macro == :has_one || reflection_macro == :belongs_to
    owner_target.send(name) || owner_target.send("build_#{name}")
  when reflection_macro == :has_many
    owner_target.association(reflection.name).build
  end
end
target_from_name(target) click to toggle source

Send the name of the mounting to the target of the host mapper, and use return value as a target for a mapper being created.

@return [Object]

# File lib/flat_map/open_mapper/factory.rb, line 181
def target_from_name(target)
  target.send(name)
end
trait_name() click to toggle source

Return the trait name if the factory defines a trait.

# File lib/flat_map/open_mapper/factory.rb, line 34
def trait_name
  @options[:trait_name] if traited?
end
traited?() click to toggle source

Return true if factory defines a trait.

@return [Boolean]

# File lib/flat_map/open_mapper/factory.rb, line 21
def traited?
  @identifier.is_a?(Class)
end
traits() click to toggle source

Return the list of traits that should be applied for a mapper being mounted on a host mapper.

@return [Array<Symbol>] list of traits

# File lib/flat_map/open_mapper/factory.rb, line 42
def traits
  Array(@options[:traits]).compact
end