flat_map 0.0.3

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.
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