Class: FlatMap::OpenMapper::Factory

Inherits:
Object
  • Object
show all
Defined in:
lib/flat_map/open_mapper/factory.rb

Overview

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.

Instance Method Summary collapse

Constructor Details

#initialize(identifier, options = {}, &block) ⇒ Factory

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.

Parameters:

  • identifier (Symbol, Class)

    name of a mapper or mapper class itself

  • options (Hash) (defaults to: {})


14
15
16
# File 'lib/flat_map/open_mapper/factory.rb', line 14

def initialize(identifier, options = {}, &block)
  @identifier, @options, @extension = identifier, options, block
end

Instance Method Details

#create(mapper, *owner_traits) ⇒ Object

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.

Parameters:

  • mapper (FlatMap::OpenMapper)

    Host mapper

  • owner_traits (*Symbol)

    List of traits to be applied to a newly created mapper



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/flat_map/open_mapper/factory.rb', line 207

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) ⇒ Object?

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.

Parameters:

Returns:

  • (Object, nil)

    target for new mapper.



120
121
122
123
124
125
126
127
128
129
# File 'lib/flat_map/open_mapper/factory.rb', line 120

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) ⇒ Symbol

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.

Parameters:

Returns:

  • (Symbol)


192
193
194
195
196
197
198
# File 'lib/flat_map/open_mapper/factory.rb', line 192

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) ⇒ Object

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

Parameters:

Returns:

  • (Object)

    target for new mapper



92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/flat_map/open_mapper/factory.rb', line 92

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_classClass

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

Returns:



50
51
52
53
54
55
56
57
58
59
60
61
# 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
    when @options[:mapper_class] then @options[:mapper_class]
    else
      class_name = @options[:mapper_class_name] || "#{name.to_s.camelize}Mapper"
      class_name.constantize
    end
  end
end

#model_mount?Boolean

Return true if mapper to me mounted is ModelMapper

Returns:

  • (Boolean)


77
78
79
# File 'lib/flat_map/open_mapper/factory.rb', line 77

def model_mount?
  mapper_class < ModelMapper
end

#nameSymbol?

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

Returns:

  • (Symbol, nil)


29
30
31
# File 'lib/flat_map/open_mapper/factory.rb', line 29

def name
  traited? ? nil : @identifier
end

#new_targetObject

Return new instance of target_class of the mapper.

Returns:

  • (Object)


110
111
112
# File 'lib/flat_map/open_mapper/factory.rb', line 110

def new_target
  mapper_class.target_class.new
end

#open_mapper_classClass

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

Returns:

  • (Class)


67
68
69
70
71
72
# File 'lib/flat_map/open_mapper/factory.rb', line 67

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?Boolean

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

Returns:

  • (Boolean)


84
85
86
# File 'lib/flat_map/open_mapper/factory.rb', line 84

def open_mount?
  !model_mount?
end

#reflection_from_target(target) ⇒ ActiveRecord::Reflection::AssociationReflection?

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

Parameters:

  • target (ActiveRecord::Base)

Returns:

  • (ActiveRecord::Reflection::AssociationReflection, nil)


171
172
173
174
175
176
# File 'lib/flat_map/open_mapper/factory.rb', line 171

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) ⇒ Boolean

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.

Parameters:

  • traits (Array<Symbol>)

    list of traits

Returns:

  • (Boolean)


234
235
236
237
238
239
240
241
242
# File 'lib/flat_map/open_mapper/factory.rb', line 234

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) ⇒ Object

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


149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/flat_map/open_mapper/factory.rb', line 149

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) ⇒ Object

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.

Returns:

  • (Object)


182
183
184
# File 'lib/flat_map/open_mapper/factory.rb', line 182

def target_from_name(target)
  target.send(name)
end

#trait_nameObject

Return the trait name if the factory defines a trait.



34
35
36
# File 'lib/flat_map/open_mapper/factory.rb', line 34

def trait_name
  @options[:trait_name] if traited?
end

#traited?Boolean

Return true if factory defines a trait.

Returns:

  • (Boolean)


21
22
23
# File 'lib/flat_map/open_mapper/factory.rb', line 21

def traited?
  @identifier.is_a?(Class)
end

#traitsArray<Symbol>

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

Returns:

  • (Array<Symbol>)

    list of traits



42
43
44
# File 'lib/flat_map/open_mapper/factory.rb', line 42

def traits
  Array(@options[:traits]).compact
end