flat_map 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +31 -0
  3. data/.metrics +17 -0
  4. data/.rspec +4 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +9 -0
  8. data/Gemfile +20 -0
  9. data/LICENSE +20 -0
  10. data/README.markdown +211 -0
  11. data/Rakefile +15 -0
  12. data/flat_map.gemspec +30 -0
  13. data/lib/flat_map.rb +9 -0
  14. data/lib/flat_map/base_mapper.rb +95 -0
  15. data/lib/flat_map/base_mapper/attribute_methods.rb +54 -0
  16. data/lib/flat_map/base_mapper/factory.rb +238 -0
  17. data/lib/flat_map/base_mapper/mapping.rb +123 -0
  18. data/lib/flat_map/base_mapper/mounting.rb +168 -0
  19. data/lib/flat_map/base_mapper/persistence.rb +145 -0
  20. data/lib/flat_map/base_mapper/skipping.rb +62 -0
  21. data/lib/flat_map/base_mapper/traits.rb +94 -0
  22. data/lib/flat_map/empty_mapper.rb +29 -0
  23. data/lib/flat_map/errors.rb +57 -0
  24. data/lib/flat_map/mapper.rb +213 -0
  25. data/lib/flat_map/mapper/skipping.rb +45 -0
  26. data/lib/flat_map/mapper/targeting.rb +130 -0
  27. data/lib/flat_map/mapping.rb +124 -0
  28. data/lib/flat_map/mapping/factory.rb +21 -0
  29. data/lib/flat_map/mapping/reader.rb +12 -0
  30. data/lib/flat_map/mapping/reader/basic.rb +28 -0
  31. data/lib/flat_map/mapping/reader/formatted.rb +45 -0
  32. data/lib/flat_map/mapping/reader/formatted/formats.rb +28 -0
  33. data/lib/flat_map/mapping/reader/method.rb +25 -0
  34. data/lib/flat_map/mapping/reader/proc.rb +15 -0
  35. data/lib/flat_map/mapping/writer.rb +11 -0
  36. data/lib/flat_map/mapping/writer/basic.rb +25 -0
  37. data/lib/flat_map/mapping/writer/method.rb +28 -0
  38. data/lib/flat_map/mapping/writer/proc.rb +18 -0
  39. data/lib/flat_map/version.rb +3 -0
  40. data/spec/flat_map/empty_mapper_spec.rb +36 -0
  41. data/spec/flat_map/errors_spec.rb +23 -0
  42. data/spec/flat_map/mapper/attribute_methods_spec.rb +36 -0
  43. data/spec/flat_map/mapper/callbacks_spec.rb +76 -0
  44. data/spec/flat_map/mapper/factory_spec.rb +258 -0
  45. data/spec/flat_map/mapper/mapping_spec.rb +98 -0
  46. data/spec/flat_map/mapper/mounting_spec.rb +142 -0
  47. data/spec/flat_map/mapper/skipping_spec.rb +91 -0
  48. data/spec/flat_map/mapper/targeting_spec.rb +156 -0
  49. data/spec/flat_map/mapper/traits_spec.rb +172 -0
  50. data/spec/flat_map/mapper/validations_spec.rb +72 -0
  51. data/spec/flat_map/mapper_spec.rb +9 -0
  52. data/spec/flat_map/mapping/factory_spec.rb +12 -0
  53. data/spec/flat_map/mapping/reader/basic_spec.rb +15 -0
  54. data/spec/flat_map/mapping/reader/formatted_spec.rb +62 -0
  55. data/spec/flat_map/mapping/reader/method_spec.rb +13 -0
  56. data/spec/flat_map/mapping/reader/proc_spec.rb +13 -0
  57. data/spec/flat_map/mapping/writer/basic_spec.rb +15 -0
  58. data/spec/flat_map/mapping/writer/method_spec.rb +13 -0
  59. data/spec/flat_map/mapping/writer/proc_spec.rb +13 -0
  60. data/spec/flat_map/mapping_spec.rb +123 -0
  61. data/spec/spec_helper.rb +7 -0
  62. data/tmp/metric_fu/_data/20131218.yml +6902 -0
  63. data/tmp/metric_fu/_data/20131219.yml +6726 -0
  64. metadata +184 -0
@@ -0,0 +1,54 @@
1
+ module FlatMap
2
+ # This module allows mappers to return and assign values via method calls
3
+ # which names correspond to names of mappings defined within the mapper.
4
+ #
5
+ # This methods are defined within anonymous module that will extend
6
+ # mapper on first usage of this methods.
7
+ #
8
+ # NOTE: :to_ary method is called internally by Ruby 1.9.3 when we call
9
+ # something like [mapper].flatten. And we DO want default behavior
10
+ # for handling this missing method.
11
+ module BaseMapper::AttributeMethods
12
+ # Lazily define reader and writer methods for all mappings available
13
+ # to the mapper, and extend +self+ with it.
14
+ def method_missing(name, *args, &block)
15
+ if name == :to_ary ||
16
+ @attribute_methods_defined ||
17
+ FlatMap::BaseMapper.protected_instance_methods.include?(name)
18
+ return super
19
+ end
20
+
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
27
+
28
+ return super unless valid_names.include?(name)
29
+
30
+ extend attribute_methods(mappings)
31
+ @attribute_methods_defined = true
32
+ send(name, *args, &block)
33
+ end
34
+
35
+ # Define anonymous module with reader and writer methods for
36
+ # all the +mappings+ being passed.
37
+ #
38
+ # @param [Array<FlatMap::Mapping>] mappings list of mappings
39
+ # @return [Module] module with method definitions
40
+ def attribute_methods(mappings)
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) }
45
+
46
+ define_method("#{mapping_full_name}=") do |value|
47
+ _mapping.write(value)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ private :attribute_methods
53
+ end
54
+ end
@@ -0,0 +1,238 @@
1
+ module FlatMap
2
+ # Mapper factory objects are used to store mounting and trait definitions
3
+ # and to instantiate and setup corresponding mapper objects thereafter.
4
+ # Factory objects are stored by mapper classes in opposite to actual
5
+ # mounted mappers that are stored by mapper objects themselves.
6
+ class BaseMapper::Factory
7
+ # Initializes factory with an identifier (name of a mounted mapper,
8
+ # or the actual class for a trait) and a set of options. Those args
9
+ # are used to create actual mapper object for the host mapper.
10
+ #
11
+ # @param [Symbol, Class] identifier name of a mapper or mapper class
12
+ # itself
13
+ # @param [Hash] options
14
+ def initialize(identifier, options = {}, &block)
15
+ @identifier, @options, @extension = identifier, options, block
16
+ end
17
+
18
+ # Return +true+ if factory defines a trait.
19
+ #
20
+ # @return [Boolean]
21
+ def traited?
22
+ @identifier.is_a?(Class)
23
+ end
24
+
25
+ # Return the name of the mapper being defined by the factory.
26
+ # Return +nil+ for the traited factory.
27
+ #
28
+ # @return [Symbol, nil]
29
+ def name
30
+ traited? ? nil : @identifier
31
+ end
32
+
33
+ # Return the trait name if the factory defines a trait.
34
+ def trait_name
35
+ @options[:trait_name] if traited?
36
+ end
37
+
38
+ # Return the list of traits that should be applied for a mapper being
39
+ # mounted on a host mapper.
40
+ #
41
+ # @return [Array<Symbol>] list of traits
42
+ def traits
43
+ Array(@options[:traits]).compact
44
+ end
45
+
46
+ # Return the anonymous trait class if the factory defines a trait.
47
+ # Fetch and return the class of a mapper defined by a symbol.
48
+ #
49
+ # @return [Class] ancestor of {FlatMap::BaseMapper}
50
+ def mapper_class
51
+ return @identifier if traited?
52
+
53
+ class_name = @options[:mapper_class_name] || "#{name.to_s.camelize}Mapper"
54
+ class_name.constantize
55
+ end
56
+
57
+ # Return +true+ if factory should create targeted mapper.
58
+ #
59
+ # @return [Boolean]
60
+ def targeted_mount?
61
+ mapper_class < Mapper
62
+ end
63
+
64
+ # Fetch the target for the mapper being created based on target of a host mapper.
65
+ #
66
+ # @param [FlatMap::BaseMapper] mapper Host mapper
67
+ # @return [Object] target for new mapper
68
+ def fetch_target_from(mapper)
69
+ return explicit_target!(mapper) if mapper.is_a?(EmptyMapper) && targeted_mount?
70
+
71
+ owner_target = mapper.target
72
+
73
+ return owner_target if traited?
74
+
75
+ explicit_target(owner_target) ||
76
+ target_from_association(owner_target) ||
77
+ target_from_name(owner_target)
78
+ end
79
+
80
+ # When creating mappers mounted on top of EmptyMapper, target cannot be implicitly
81
+ # fetched from it and should be specified explicitly.
82
+ #
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
97
+ end
98
+
99
+ # Try to use explicit target definition passed in options to fetch a
100
+ # target. If this value is a +Proc+, will call it with owner target as
101
+ # argument.
102
+ #
103
+ # @param [Object] owner_target
104
+ # @return [Object, nil] target for new mapper.
105
+ def explicit_target(owner_target)
106
+ if @options.key?(:target)
107
+ target = @options[:target]
108
+ if target.is_a? Proc
109
+ target.call(owner_target)
110
+ else
111
+ target
112
+ end
113
+ end
114
+ end
115
+
116
+ # Try to fetch the target for a new mapper being mounted, based on
117
+ # correspondence of the mounting name and presence of the association
118
+ # with a similar name in the host mapper.
119
+ #
120
+ # For example:
121
+ # class Foo < ActiveRecord::Base
122
+ # has_one :baz
123
+ # has_many :bars
124
+ # end
125
+ #
126
+ # class FooMapper < FlatMap::Mapper
127
+ # # target of this mapper is the instance of Foo. Lets reference it as 'foo'
128
+ # mount :baz # This will look for BazMapper, and will try to fetch a target for
129
+ # # it based on :has_one association, i.e. foo.baz || foo.build_baz
130
+ #
131
+ # mount :bar # This will look for BarMapper, and will try to fetch a target for
132
+ # # it based on :has_many association, i.e. foo.bars.build
133
+ # end
134
+ def target_from_association(owner_target)
135
+ return unless owner_target.kind_of?(ActiveRecord::Base)
136
+
137
+ reflection = reflection_from_target(owner_target)
138
+ return unless reflection.present?
139
+
140
+ reflection_macro = reflection.macro
141
+ case
142
+ when reflection_macro == :has_one && reflection.options[:is_current]
143
+ owner_target.send("effective_#{name}")
144
+ when reflection_macro == :has_one || reflection_macro == :belongs_to
145
+ owner_target.send(name) || owner_target.send("build_#{name}")
146
+ when reflection_macro == :has_many
147
+ owner_target.association(reflection.name).build
148
+ else # nil
149
+ end
150
+ end
151
+
152
+ # Try to retreive an association reflection that has a name corresponding
153
+ # to the one of +self+
154
+ #
155
+ # @param [ActiveRecord::Base] target
156
+ # @return [ActiveRecord::Reflection::AssociationReflection, nil]
157
+ def reflection_from_target(target)
158
+ return unless name.present? && target.is_a?(ActiveRecord::Base)
159
+ 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)
162
+ end
163
+
164
+ # Send the name of the mounting to the target of the host mapper, and use
165
+ # return value as a target for a mapper being created.
166
+ #
167
+ # @return [Object]
168
+ def target_from_name(target)
169
+ target.send(name)
170
+ end
171
+
172
+ # Return order relative to target of the passed +mapper+ in which mapper to
173
+ # be created should be saved. In particular, targets of <tt>:belongs_to</tt>
174
+ # associations should be saved before target of +mapper+ is saved.
175
+ #
176
+ # @param [FlatMap::BaseMapper] mapper
177
+ # @return [Symbol]
178
+ def fetch_save_order(mapper)
179
+ return :after if mapper.is_a?(EmptyMapper)
180
+
181
+ reflection = reflection_from_target(mapper.target)
182
+ return unless reflection.present?
183
+ reflection.macro == :belongs_to ? :before : :after
184
+ end
185
+
186
+ # Create a new mapper object for mounting. If the factory is traited,
187
+ # the new mapper is a part of a host mapper, and is 'owned' by it.
188
+ # Otherwise, assign the name of the factory to it to be able to find it
189
+ # later on.
190
+ #
191
+ # @param [FlatMap::BaseMapper] mapper Host mapper
192
+ # @param [*Symbol] owner_traits List of traits to be applied to a newly created mapper
193
+ def create(mapper, *owner_traits)
194
+ 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
+
204
+ if traited?
205
+ new_one.owner = mapper
206
+ else
207
+ new_one.host = mapper
208
+ new_one.name = @identifier
209
+ new_one.save_order = save_order
210
+
211
+ if (suffix = @options[:suffix] || mapper.suffix).present?
212
+ new_one.suffix = suffix
213
+ new_one.name = :"#{@identifier}_#{suffix}"
214
+ else
215
+ new_one.name = @identifier
216
+ end
217
+ end
218
+ new_one
219
+ end
220
+
221
+ # Return +true+ if the factory is required to be able to apply a trait
222
+ # for the host mapper.
223
+ # For example, it is required if its name is listed in +traits+.
224
+ # It is also required if it has nested traits with names listed in +traits+.
225
+ #
226
+ # @param [Array<Symbol>] traits list of traits
227
+ # @return [Boolean]
228
+ def required_for_any_trait?(traits)
229
+ return true unless traited?
230
+
231
+ traits.include?(trait_name) ||
232
+ mapper_class.mountings.any?{ |factory|
233
+ factory.traited? &&
234
+ factory.required_for_any_trait?(traits)
235
+ }
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,123 @@
1
+ module FlatMap
2
+ # This module hosts all definitions required to define and use mapping
3
+ # functionality within mapper classes. This includes mapping definition
4
+ # methods and basic reading and writing methods.
5
+ module BaseMapper::Mapping
6
+ extend ActiveSupport::Concern
7
+
8
+ # Mapping class macros
9
+ module ClassMethods
10
+ # Mapping-modifier options to distinguish options from mappings
11
+ # themselves:
12
+ MAPPING_OPTIONS = [:reader, :writer, :format, :multiparam].freeze
13
+
14
+ # Define single or multiple mappings at a time. Usually, a Hash
15
+ # is passed in a form !{mapping_name => target_attribute}. All keys
16
+ # that are listed under {MAPPING_OPTIONS} will be extracted and used
17
+ # as modifiers for new mappings.
18
+ #
19
+ # Also, mapping names may be listed as an array preceding the hash.
20
+ # In that case, its elements are treated as !{mapping_name => mapping_name}
21
+ # mapping elements.
22
+ #
23
+ # Example:
24
+ # map :brand, :account_source => :source, :format => :enum
25
+ # # is equivalent to:
26
+ # map :brand => :brand, :format => :enum
27
+ # map :account_source => :source, :format => :enum
28
+ def map(*args)
29
+ mapping_options = args.extract_options!
30
+ mappings = mapping_options.slice!(*MAPPING_OPTIONS)
31
+ mappings_from_array = args.zip(args).flatten
32
+ mappings.merge!(Hash[*mappings_from_array]) unless mappings_from_array.empty?
33
+
34
+ define_mappings(mappings, mapping_options)
35
+ end
36
+
37
+ # Define a set of +mappings+, passed as a {Hash} with +options+ as modifiers.
38
+ # Eventually, adds a mapping factories to list of class mappings. Those
39
+ # factory objects are used to create actual mappings for specific mapper
40
+ # object.
41
+ #
42
+ # @param [Hash] mappings
43
+ # @param [Hash] options
44
+ # @return [Array<FlatMap::Mapping::Factory>] list of mappings
45
+ def define_mappings(mappings, options)
46
+ mappings.each do |name, target_attribute|
47
+ self.mappings << FlatMap::Mapping::Factory.new(name, target_attribute, options)
48
+ end
49
+ end
50
+ private :define_mappings
51
+
52
+ # List of class mappings (mapping factories).
53
+ #
54
+ # @return [Array<FlatMap::Mapping::Factory>]
55
+ def mappings
56
+ @mappings ||= []
57
+ end
58
+
59
+ # Writer for mappings.
60
+ def mappings=(val)
61
+ @mappings = val
62
+ end
63
+ end
64
+
65
+ # Send passed +params+ +write_from_params+ method of each
66
+ # of the mappings of +self+.
67
+ #
68
+ # Overloaded in {BaseMapper::Mounting}.
69
+ #
70
+ # @param [Hash] params
71
+ # @return [Hash] params
72
+ def write(params)
73
+ mappings.each do |mapping|
74
+ mapping.write_from_params(params)
75
+ end
76
+ params
77
+ end
78
+
79
+ # Send +read_as_params+ method to all mappings associated with
80
+ # self. And consolidate results in a single hash.
81
+ #
82
+ # @return [Hash] set of read values
83
+ def read
84
+ mappings.inject({}) do |params, mapping|
85
+ params.merge(mapping.read_as_params)
86
+ end
87
+ end
88
+
89
+ # Retrieve mapping value via its name, which might differ from its
90
+ # full_name, if suffix was used.
91
+ #
92
+ # @param [Symbol] name
93
+ def [](name)
94
+ mapping(name).try(:read)
95
+ end
96
+
97
+ # Write value to mapping specified by name, which might differ from its
98
+ # full_name, if suffix was used.
99
+ #
100
+ # @param [Symbol] name
101
+ # @param [Object] value
102
+ def []=(name, value)
103
+ mapping(name).try(:write, value)
104
+ end
105
+
106
+ # Lookup mapping by its name, which might differ from its
107
+ # full_name, if suffix was used.
108
+ #
109
+ # @param [Symbol] name
110
+ # @return [FlatMap::Mapping]
111
+ def mapping(name)
112
+ mappings.find{ |_mapping| _mapping.name == name }
113
+ end
114
+
115
+ # Return a list of mappings associated to +self+.
116
+ #
117
+ # @return [FlatMap::Mapping]
118
+ def mappings
119
+ @mappings ||= self.class.mappings.map{ |factory| factory.create(self) }
120
+ end
121
+ private :mappings
122
+ end
123
+ end
@@ -0,0 +1,168 @@
1
+ module FlatMap
2
+ # This module hosts definitions required for mounting functionality
3
+ # of the mappers. This includes mounting definition methods, overloaded
4
+ # +read+ and +write+ methods to make them aware of mounted mappers and
5
+ # other methods.
6
+ #
7
+ # Also, the +method_missing+ method is defined here to delegate the missing
8
+ # method to the very first mounted mapper that responds to it.
9
+ module BaseMapper::Mounting
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ attr_accessor :save_order
14
+ end
15
+
16
+ # Mounting class macros.
17
+ module ClassMethods
18
+ # Add a mounting factory to a list of factories of a class
19
+ # These factories are used to create actual mounted objects,
20
+ # which are mappers themselves, associated to a particular
21
+ # mapper.
22
+ #
23
+ # @param [*Object] args
24
+ # @return [Array<FlatMap::BaseMapper::Factory>]
25
+ def mount(*args, &block)
26
+ mountings << FlatMap::BaseMapper::Factory.new(*args, &block)
27
+ end
28
+
29
+ # List of mountings (factories) of a class.
30
+ #
31
+ # @return [Array<FlatMap::BaseMapper>]
32
+ def mountings
33
+ @mountings ||= []
34
+ end
35
+
36
+ # Writer for @mountings.
37
+ def mountings=(val)
38
+ @mountings = val
39
+ end
40
+ end
41
+
42
+ # Extend original {Mapping#read} method to take
43
+ # into account mountings of mounted mappers.
44
+ #
45
+ # @return [Hash] read values
46
+ def read
47
+ mountings.inject(super) do |result, mapper|
48
+ result.merge(mapper.read)
49
+ end
50
+ end
51
+
52
+ # Extend original {Mapping#write} method to pass
53
+ # +params+ to mounted mappers.
54
+ #
55
+ # Overridden in {Persistence}. Left here for consistency.
56
+ #
57
+ # @param [Hash] params
58
+ # @return [Hash] params
59
+ def write(params)
60
+ super
61
+
62
+ mountings.each do |mapper|
63
+ mapper.write(params)
64
+ end
65
+
66
+ params
67
+ end
68
+
69
+ # Return list of mappings to be saved before saving target of +self+
70
+ #
71
+ # @return [Array<FlatMap::BaseMapper>]
72
+ def before_save_mountings
73
+ nearest_mountings.select{ |mount| mount.save_order == :before }
74
+ end
75
+
76
+ # Return list of mappings to be saved after target of +self+ was saved
77
+ #
78
+ # @return [Array<FlatMap::BaseMapper>]
79
+ def after_save_mountings
80
+ nearest_mountings.reject{ |mount| mount.save_order == :before }
81
+ end
82
+
83
+ # Return all mountings that are mouted on +self+ directly or through
84
+ # traits.
85
+ #
86
+ # @return [Array<FlatMap::BaseMapper>]
87
+ def nearest_mountings
88
+ mountings.map{ |mount| mount.owned? ? mount.nearest_mountings : mount }.flatten
89
+ end
90
+
91
+ # Return a list of all mountings (mapper objects) associated with +self+.
92
+ #
93
+ # Overridden in {Traits}.
94
+ #
95
+ # @return [Array<FlatMap::BaseMapper>]
96
+ def mountings
97
+ @mountings ||= self.class.mountings.map{ |factory| factory.create(self) }
98
+ end
99
+
100
+ # Return a mapping with the name that corresponds to passed +mounting_name+,
101
+ # if it exists.
102
+ #
103
+ # @return [FlatMap::Mapping, nil]
104
+ def mounting(mounting_name, is_deep = true)
105
+ list = is_deep ? all_mountings : mountings
106
+ list.find{ |mount| mount.name == mounting_name }
107
+ end
108
+
109
+ # Return a list of all mounted mappers. If +self+ is a trait, return a
110
+ # list of all mountings of the owner. This will allow separate traits
111
+ # to share methods via method_missing pattern.
112
+ #
113
+ # @return [Array<FlatMap::BaseMapper>] mounted mappers (including traits)
114
+ def all_mountings
115
+ return all_nested_mountings.unshift(self) unless owned?
116
+ owner.all_mountings
117
+ end
118
+ protected :all_mountings
119
+
120
+ # Return a list of mountings that are accessible by a named mapper.
121
+ #
122
+ # @return [Array<FlatMap::BaseMapper>]
123
+ def all_nested_mountings
124
+ mountings.dup.concat(mountings.map{ |mount| mount.send(:all_nested_mountings) }).flatten
125
+ end
126
+ protected :all_nested_mountings
127
+
128
+ # Return a list of all mappings, i.e. mappings associated to +self+,
129
+ # plus mappings of all deeply mounted mappers. If +self+ is the owner,
130
+ # that means it is a part (a trait) of a host mapper. That means that all
131
+ # mappings of it actually correspond to all mappings of a host mapper.
132
+ # This allows to define things like validation in a trait where access
133
+ # to top-level mappings is required.
134
+ #
135
+ # @return [Array<FlatMap::Mapping>]
136
+ def all_mappings
137
+ return all_nested_mappings unless owned?
138
+ owner.all_mappings
139
+ end
140
+ protected :all_mappings
141
+
142
+ # Return a list of all mappings, i.e. mappings that associated to +self+
143
+ # plus mappings of all deeply mounted mappers.
144
+ #
145
+ # @return [Array<FlatMap::Mapping>]
146
+ def all_nested_mappings
147
+ (mappings + mountings.map{ |mount| mount.send(:all_nested_mappings) }).flatten
148
+ end
149
+ protected :all_nested_mappings
150
+
151
+ # Delegate missing method to any mounted mapping that respond to it,
152
+ # unless those methods are protected methods of FlatMap::BaseMapper.
153
+ #
154
+ # NOTE: :to_ary method is called internally by Ruby 1.9.3 when we call
155
+ # something like [mapper].flatten. And we DO want default behavior
156
+ # for handling this missing method.
157
+ def method_missing(name, *args, &block)
158
+ return super if name == :to_ary ||
159
+ FlatMap::BaseMapper.protected_instance_methods.include?(name)
160
+
161
+ return self[name] if mapping(name).present?
162
+
163
+ mount = all_mountings.find{ |_mount| _mount.respond_to?(name) }
164
+ return super if mount.nil?
165
+ mount.send(name, *args, &block)
166
+ end
167
+ end
168
+ end