flat_map 0.0.3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NjNmMzU5MGI0NjIwZGQyZmY4YzBlM2RhYzFmYTMzNGJmM2ZiMzIzMA==
4
+ NmZiN2RlYjcwY2U5YmIzMzAyYWM3ZGE3NzA4NDQyMDI2OGE0MDFhYw==
5
5
  data.tar.gz: !binary |-
6
- NjAxZjRjYThiNWMxN2QwN2NkMWNkODFhZDg0NjEzZDQ0MGE5YWM5ZQ==
7
- SHA512:
6
+ MTYwMjllODAzY2IwNDQ4ZGNiNTJmZDA2NzgxMDU0N2ZkZDEzN2EwMA==
7
+ !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- N2M2NmQzZmFkMjZjMjg3NThmZjhjN2I0YmI2NGMyMWQxZTcyNTYwYTFmNzJj
10
- NmM0Yzc1NDg4YWM1MmM1ZGE3ZTA3MTUzZTNmNjgwZGZkZDYxN2ViMzliNDVl
11
- NWQ4Yjg3ZDI5NTg2MmZiOTQ2ZjQ5ODk1YTUwODQyN2JmNzIxNDQ=
9
+ ZDU4ZDYzYzAzYTBhMDkwZDRlODliODc3ODcyMWM3ODE2YzZkYWVhMmMwN2Nj
10
+ NWJkNDdhOTc1N2RiMTkwZjFlOGYzOGVlZWY4ZjlhZDliZjQ3YmEwYWY2YjU0
11
+ YTJiMDA0ZjdiMWQ4NGQzYjQwZGEyMzIyNzAwNWYzZjBmMWUxNjQ=
12
12
  data.tar.gz: !binary |-
13
- ZTg1ZmI2NzY1NjA3ZGYyYzQ1NTAwYzFlMDUxODY2Y2M5YmNlMWI5YTNkNWYx
14
- MTI5NGQwNDA5YmQwOTNjOTJiYmRhZDdjYWY0YTkwMzU4MzQxM2FlYjU5Nzgy
15
- YzlkYmEzNmNlM2ExNTA5M2E5Y2E1OGFlNDViYmU1MGVmYzg5M2U=
13
+ N2UyMjkzNmRkNjQ0OWI0YmYyOGEwMDE2ZjFjYzc2MWFkN2MxMjZlZTM4MDQ2
14
+ YThhYzUyYzkwZDcxMzFmODI5MzQ0MDQ2M2M5MmQzY2UzMzljODYzMmI2NmUw
15
+ OTQ2MmNkNTljMjgyYmEwN2NhMzJhYTBjZjhlMDgzMDZkOTJmOTA=
@@ -1,9 +1,14 @@
1
+ require 'ostruct'
1
2
  require 'active_support/core_ext'
2
3
  require 'active_record'
3
4
 
4
5
  require "flat_map/version"
5
6
  require 'flat_map/mapping'
6
7
  require 'flat_map/errors'
7
- require 'flat_map/base_mapper'
8
- require 'flat_map/mapper'
9
- require 'flat_map/empty_mapper'
8
+ require 'flat_map/open_mapper'
9
+ require 'flat_map/model_mapper'
10
+
11
+ module FlatMap
12
+ # for backwards compatability
13
+ Mapper = ModelMapper
14
+ end
@@ -180,34 +180,15 @@ module FlatMap
180
180
  # mapper.read[:first_name] # => John
181
181
  # mapper.first_name # => 'John'
182
182
  # mapper.last_name = 'Smith'
183
- class Mapper < BaseMapper
183
+ class ModelMapper < OpenMapper
184
184
  extend ActiveSupport::Autoload
185
185
 
186
- autoload :Targeting
186
+ autoload :Persistence
187
187
  autoload :Skipping
188
188
 
189
- include Targeting
189
+ include Persistence
190
190
  include Skipping
191
191
 
192
- attr_reader :target
193
-
194
192
  delegate :logger, :to => :target
195
-
196
- # Initializes +mapper+ with +target+ and +traits+, which are
197
- # used to fetch proper list of mounted mappers. Raises error
198
- # if target is not specified.
199
- #
200
- # @param [Object] target Target of mapping
201
- # @param [*Symbol] traits List of traits
202
- # @raise [FlatMap::Mapper::NoTargetError]
203
- def initialize(target, *traits)
204
- raise Targeting::NoTargetError.new(self.class) unless target.present?
205
-
206
- @target, @traits = target, traits
207
-
208
- if block_given?
209
- singleton_class.trait :extension, &Proc.new
210
- end
211
- end
212
193
  end
213
194
  end
@@ -1,23 +1,10 @@
1
1
  module FlatMap
2
- # This module provides some integration between mapper and its target,
3
- # which is usually an ActiveRecord model, as well as some integration
4
- # between mapper and Rails forms.
5
- module Mapper::Targeting
6
- # Raised when mapper is initialized with no target defined
7
- class NoTargetError < ArgumentError
8
- # Initializes exception with a name of mapper class.
9
- #
10
- # @param [Class] mapper_class class of mapper being initialized
11
- def initialize(mapper_class)
12
- super("Target object is required to initialize #{mapper_class.name}")
13
- end
14
- end
15
-
2
+ # This module enhances and modifies original FlatMap::OpenMapper::Persistance
3
+ # functionality for ActiveRecord models as targets.
4
+ module ModelMapper::Persistence
16
5
  extend ActiveSupport::Concern
17
6
 
18
7
  included do
19
- define_callbacks :save
20
-
21
8
  # Writer of the target class name. Allows manual control over target
22
9
  # class of the mapper, for example:
23
10
  #
@@ -29,15 +16,6 @@ module FlatMap
29
16
 
30
17
  # ModelMethods class macros
31
18
  module ClassMethods
32
- # Create a new mapper object wrapped around new instance of its
33
- # +target_class+, with a list of passed +traits+ applied to it.
34
- #
35
- # @param [*Symbol] traits
36
- # @return [FlatMap::Mapper] mapper
37
- def build(*traits, &block)
38
- new(target_class.new, *traits, &block)
39
- end
40
-
41
19
  # Find a record of the +target_class+ by +id+ and use it as a
42
20
  # target for a new mapper, with a list of passed +traits+ applied
43
21
  # to it.
@@ -71,8 +49,8 @@ module FlatMap
71
49
  #
72
50
  # @return [String]
73
51
  def default_target_class_name
74
- ancestor_classes = ancestors.select{ |ancestor| ancestor.is_a? Class }
75
- base_mapper_index = ancestor_classes.index(::FlatMap::Mapper)
52
+ ancestor_classes = ancestors.select{ |ancestor| ancestor.is_a? Class }
53
+ base_mapper_index = ancestor_classes.index(::FlatMap::ModelMapper)
76
54
  ancestor_classes[base_mapper_index - 1].name[/^([\w:]+)Mapper.*$/, 1]
77
55
  end
78
56
  end
@@ -1,8 +1,8 @@
1
1
  module FlatMap
2
- # This helper module slightly enhances functionality of the
3
- # {BaseMapper::Skipping} module for most commonly used +ActiveRecord+ targets.
4
- # This is done to improve modularity of the {FlatMap} mappers.
5
- module Mapper::Skipping
2
+ # This helper module slightly enhances ofunctionality of the
3
+ # {FlatMap::OpenMapper::Skipping} module for most commonly
4
+ # used +ActiveRecord+ targets.
5
+ module ModelMapper::Skipping
6
6
  # Extend original #skip! method for Rails-models-based targets
7
7
  #
8
8
  # Note that this will mark the target record as
@@ -1,9 +1,17 @@
1
1
  module FlatMap
2
- # +BaseMapper+ is an abstract class that hosts overwhelming majority
3
- # of common functionality of {EmptyMapper EmptyMappers} and {Mapper Mappers}.
4
- #
5
- # For more detailed information on what mappers are, refer to {Mapper} documentation.
6
- class BaseMapper
2
+ # Base Mapper that can be used for mounting other mappers, handling business logic,
3
+ # etc. For the intentional usage of mappers, pleas see {ModelMapper}
4
+ class OpenMapper
5
+ # Raised when mapper is initialized with no target defined
6
+ class NoTargetError < ArgumentError
7
+ # Initializes exception with a name of mapper class.
8
+ #
9
+ # @param [Class] mapper_class class of mapper being initialized
10
+ def initialize(mapper_class)
11
+ super("Target object is required to initialize #{mapper_class.name}")
12
+ end
13
+ end
14
+
7
15
  extend ActiveSupport::Autoload
8
16
 
9
17
  autoload :Mapping
@@ -22,8 +30,8 @@ module FlatMap
22
30
  include Persistence
23
31
  include Skipping
24
32
 
25
- attr_reader :traits
26
33
  attr_writer :host, :suffix
34
+ attr_reader :target, :traits
27
35
  attr_accessor :owner, :name
28
36
 
29
37
  # Callback to dup mappings and mountings on inheritance.
@@ -38,11 +46,21 @@ module FlatMap
38
46
  subclass.mountings = mountings.dup
39
47
  end
40
48
 
41
- # Raise exception on trying to initialize an instance.
49
+ # Initializes +mapper+ with +target+ and +traits+, which are
50
+ # used to fetch proper list of mounted mappers. Raises error
51
+ # if target is not specified.
42
52
  #
43
- # @raise [RuntimeError]
44
- def initialize
45
- raise 'BaseMapper is abstract class and cannot be initialized'
53
+ # @param [Object] target Target of mapping
54
+ # @param [*Symbol] traits List of traits
55
+ # @raise [FlatMap::Mapper::NoTargetError]
56
+ def initialize(target, *traits)
57
+ raise NoTargetError.new(self.class) unless target.present?
58
+
59
+ @target, @traits = target, traits
60
+
61
+ if block_given?
62
+ singleton_class.trait :extension, &Proc.new
63
+ end
46
64
  end
47
65
 
48
66
  # Return a simple string representation of +mapper+. Done so to
@@ -8,22 +8,22 @@ module FlatMap
8
8
  # NOTE: :to_ary method is called internally by Ruby 1.9.3 when we call
9
9
  # something like [mapper].flatten. And we DO want default behavior
10
10
  # for handling this missing method.
11
- module BaseMapper::AttributeMethods
11
+ module OpenMapper::AttributeMethods
12
12
  # Lazily define reader and writer methods for all mappings available
13
13
  # to the mapper, and extend +self+ with it.
14
14
  def method_missing(name, *args, &block)
15
15
  if name == :to_ary ||
16
16
  @attribute_methods_defined ||
17
- FlatMap::BaseMapper.protected_instance_methods.include?(name)
17
+ self.class.protected_instance_methods.include?(name)
18
18
  return super
19
19
  end
20
20
 
21
21
  mappings = all_mappings
22
- valid_names = mappings.map{ |_mapping|
23
- mapping_full_name = _mapping.full_name
24
- [ mapping_full_name,
25
- "#{mapping_full_name}=".to_sym ]
26
- }.flatten
22
+ valid_names = mappings.map do |mapping|
23
+ full_name = mapping.full_name
24
+ [full_name, "#{full_name}=".to_sym]
25
+ end
26
+ valid_names.flatten!
27
27
 
28
28
  return super unless valid_names.include?(name)
29
29
 
@@ -39,12 +39,13 @@ module FlatMap
39
39
  # @return [Module] module with method definitions
40
40
  def attribute_methods(mappings)
41
41
  Module.new do
42
- mappings.each do |_mapping|
43
- mapping_full_name = _mapping.full_name
44
- define_method(mapping_full_name){ |*args| _mapping.read(*args) }
42
+ mappings.each do |mapping|
43
+ full_name = mapping.full_name
44
+
45
+ define_method(full_name){ |*args| mapping.read(*args) }
45
46
 
46
- define_method("#{mapping_full_name}=") do |value|
47
- _mapping.write(value)
47
+ define_method("#{full_name}=") do |value|
48
+ mapping.write(value)
48
49
  end
49
50
  end
50
51
  end
@@ -3,7 +3,7 @@ module FlatMap
3
3
  # and to instantiate and setup corresponding mapper objects thereafter.
4
4
  # Factory objects are stored by mapper classes in opposite to actual
5
5
  # mounted mappers that are stored by mapper objects themselves.
6
- class BaseMapper::Factory
6
+ class OpenMapper::Factory
7
7
  # Initializes factory with an identifier (name of a mounted mapper,
8
8
  # or the actual class for a trait) and a set of options. Those args
9
9
  # are used to create actual mapper object for the host mapper.
@@ -46,69 +46,83 @@ module FlatMap
46
46
  # Return the anonymous trait class if the factory defines a trait.
47
47
  # Fetch and return the class of a mapper defined by a symbol.
48
48
  #
49
- # @return [Class] ancestor of {FlatMap::BaseMapper}
49
+ # @return [Class] ancestor of {FlatMap::OpenMapper}
50
50
  def mapper_class
51
- return @identifier if traited?
51
+ @mapper_class ||= begin
52
+ case
53
+ when traited? then @identifier
54
+ when @options[:open] then open_mapper_class
55
+ else
56
+ class_name = @options[:mapper_class_name] || "#{name.to_s.camelize}Mapper"
57
+ class_name.constantize
58
+ end
59
+ end
60
+ end
52
61
 
53
- class_name = @options[:mapper_class_name] || "#{name.to_s.camelize}Mapper"
54
- class_name.constantize
62
+ # Return descendant of +OpenMapper+ class to be used when mounting
63
+ # open mappers.
64
+ #
65
+ # @return [Class]
66
+ def open_mapper_class
67
+ klass = Class.new(OpenMapper)
68
+ klass_name = "#{name.to_s.camelize}Mapper"
69
+ klass.singleton_class.send(:define_method, :name){ klass_name }
70
+ klass
55
71
  end
56
72
 
57
- # Return +true+ if factory should create targeted mapper.
73
+ # Return +true+ if mapper to me mounted is +ModelMapper+
58
74
  #
59
75
  # @return [Boolean]
60
- def targeted_mount?
61
- mapper_class < Mapper
76
+ def model_mount?
77
+ mapper_class < ModelMapper
78
+ end
79
+
80
+ # Return +true+ if mapper to me mounted is not +ModelMapper+, i.e. +OpenMapper+.
81
+ #
82
+ # @return [Boolean]
83
+ def open_mount?
84
+ !model_mount?
62
85
  end
63
86
 
64
87
  # Fetch the target for the mapper being created based on target of a host mapper.
65
88
  #
66
- # @param [FlatMap::BaseMapper] mapper Host mapper
89
+ # @param [FlatMap::OpenMapper] mapper Host mapper
67
90
  # @return [Object] target for new mapper
68
91
  def fetch_target_from(mapper)
69
- return explicit_target!(mapper) if mapper.is_a?(EmptyMapper) && targeted_mount?
70
-
71
92
  owner_target = mapper.target
72
93
 
73
94
  return owner_target if traited?
74
95
 
75
- explicit_target(owner_target) ||
76
- target_from_association(owner_target) ||
77
- target_from_name(owner_target)
96
+ if open_mount?
97
+ explicit_target(mapper) || new_target
98
+ else
99
+ explicit_target(mapper) ||
100
+ target_from_association(owner_target) ||
101
+ target_from_name(owner_target) ||
102
+ new_target
103
+ end
78
104
  end
79
105
 
80
- # When creating mappers mounted on top of EmptyMapper, target cannot be implicitly
81
- # fetched from it and should be specified explicitly.
106
+ # Return new instance of +target_class+ of the mapper.
82
107
  #
83
- # @param [FlatMap::EmptyMapper] mapper Host empty mapper
84
- # @return [Object] target for new mapper
85
- def explicit_target!(mapper)
86
- target = @options[:target]
87
-
88
- if target.present?
89
- case target
90
- when Proc then target.call
91
- when Symbol then mapper.send(target)
92
- else target
93
- end
94
- else
95
- raise Mapper::Targeting::NoTargetError.new(mapper_class)
96
- end
108
+ # @return [Object]
109
+ def new_target
110
+ mapper_class.target_class.new
97
111
  end
98
112
 
99
113
  # Try to use explicit target definition passed in options to fetch a
100
114
  # target. If this value is a +Proc+, will call it with owner target as
101
115
  # argument.
102
116
  #
103
- # @param [Object] owner_target
117
+ # @param [FlatMap::OpenMapper] mapper
104
118
  # @return [Object, nil] target for new mapper.
105
- def explicit_target(owner_target)
119
+ def explicit_target(mapper)
106
120
  if @options.key?(:target)
107
121
  target = @options[:target]
108
- if target.is_a? Proc
109
- target.call(owner_target)
110
- else
111
- target
122
+ case target
123
+ when Proc then target.call(mapper.target)
124
+ when Symbol then mapper.send(target)
125
+ else target
112
126
  end
113
127
  end
114
128
  end
@@ -132,7 +146,7 @@ module FlatMap
132
146
  # # it based on :has_many association, i.e. foo.bars.build
133
147
  # end
134
148
  def target_from_association(owner_target)
135
- return unless owner_target.kind_of?(ActiveRecord::Base)
149
+ return unless owner_target.is_a?(ActiveRecord::Base)
136
150
 
137
151
  reflection = reflection_from_target(owner_target)
138
152
  return unless reflection.present?
@@ -145,7 +159,6 @@ module FlatMap
145
159
  owner_target.send(name) || owner_target.send("build_#{name}")
146
160
  when reflection_macro == :has_many
147
161
  owner_target.association(reflection.name).build
148
- else # nil
149
162
  end
150
163
  end
151
164
 
@@ -157,8 +170,8 @@ module FlatMap
157
170
  def reflection_from_target(target)
158
171
  return unless name.present? && target.is_a?(ActiveRecord::Base)
159
172
  target_class = target.class
160
- reflection = target_class.reflect_on_association(name)
161
- reflection || target_class.reflect_on_association(name.to_s.pluralize.to_sym)
173
+ reflection = target_class.reflect_on_association(name)
174
+ reflection || target_class.reflect_on_association(name.to_s.pluralize.to_sym)
162
175
  end
163
176
 
164
177
  # Send the name of the mounting to the target of the host mapper, and use
@@ -173,10 +186,10 @@ module FlatMap
173
186
  # be created should be saved. In particular, targets of <tt>:belongs_to</tt>
174
187
  # associations should be saved before target of +mapper+ is saved.
175
188
  #
176
- # @param [FlatMap::BaseMapper] mapper
189
+ # @param [FlatMap::OpenMapper] mapper
177
190
  # @return [Symbol]
178
191
  def fetch_save_order(mapper)
179
- return :after if mapper.is_a?(EmptyMapper)
192
+ return :after unless mapper.is_a?(ModelMapper)
180
193
 
181
194
  reflection = reflection_from_target(mapper.target)
182
195
  return unless reflection.present?
@@ -188,24 +201,16 @@ module FlatMap
188
201
  # Otherwise, assign the name of the factory to it to be able to find it
189
202
  # later on.
190
203
  #
191
- # @param [FlatMap::BaseMapper] mapper Host mapper
204
+ # @param [FlatMap::OpenMapper] mapper Host mapper
192
205
  # @param [*Symbol] owner_traits List of traits to be applied to a newly created mapper
193
206
  def create(mapper, *owner_traits)
194
207
  save_order = @options[:save] || fetch_save_order(mapper) || :after
195
- all_traits = (traits + owner_traits).uniq
196
-
197
- new_one =
198
- if targeted_mount? then
199
- mapper_class.new(fetch_target_from(mapper), *all_traits, &@extension)
200
- else
201
- mapper_class.new(*all_traits, &@extension)
202
- end
203
-
208
+ new_one = mapper_class.new(fetch_target_from(mapper), *(traits + owner_traits).uniq, &@extension)
204
209
  if traited?
205
210
  new_one.owner = mapper
206
211
  else
207
- new_one.host = mapper
208
- new_one.name = @identifier
212
+ new_one.host = mapper
213
+ new_one.name = @identifier
209
214
  new_one.save_order = save_order
210
215
 
211
216
  if (suffix = @options[:suffix] || mapper.suffix).present?
@@ -229,10 +234,10 @@ module FlatMap
229
234
  return true unless traited?
230
235
 
231
236
  traits.include?(trait_name) ||
232
- mapper_class.mountings.any?{ |factory|
237
+ mapper_class.mountings.any? do |factory|
233
238
  factory.traited? &&
234
- factory.required_for_any_trait?(traits)
235
- }
239
+ factory.required_for_any_trait?(traits)
240
+ end
236
241
  end
237
242
  end
238
243
  end