HornsAndHooves-flat_map 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/.metrics +17 -0
- data/.rspec +4 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +9 -0
- data/Gemfile +20 -0
- data/HornsAndHooves-flat_map.gemspec +29 -0
- data/LICENSE +21 -0
- data/README.markdown +214 -0
- data/Rakefile +15 -0
- data/lib/flat_map.rb +14 -0
- data/lib/flat_map/errors.rb +57 -0
- data/lib/flat_map/mapping.rb +124 -0
- data/lib/flat_map/mapping/factory.rb +21 -0
- data/lib/flat_map/mapping/reader.rb +12 -0
- data/lib/flat_map/mapping/reader/basic.rb +28 -0
- data/lib/flat_map/mapping/reader/formatted.rb +45 -0
- data/lib/flat_map/mapping/reader/formatted/formats.rb +28 -0
- data/lib/flat_map/mapping/reader/method.rb +25 -0
- data/lib/flat_map/mapping/reader/proc.rb +15 -0
- data/lib/flat_map/mapping/writer.rb +11 -0
- data/lib/flat_map/mapping/writer/basic.rb +25 -0
- data/lib/flat_map/mapping/writer/method.rb +28 -0
- data/lib/flat_map/mapping/writer/proc.rb +18 -0
- data/lib/flat_map/model_mapper.rb +195 -0
- data/lib/flat_map/model_mapper/persistence.rb +108 -0
- data/lib/flat_map/model_mapper/skipping.rb +45 -0
- data/lib/flat_map/open_mapper.rb +113 -0
- data/lib/flat_map/open_mapper/attribute_methods.rb +55 -0
- data/lib/flat_map/open_mapper/factory.rb +244 -0
- data/lib/flat_map/open_mapper/mapping.rb +123 -0
- data/lib/flat_map/open_mapper/mounting.rb +168 -0
- data/lib/flat_map/open_mapper/persistence.rb +178 -0
- data/lib/flat_map/open_mapper/skipping.rb +66 -0
- data/lib/flat_map/open_mapper/traits.rb +95 -0
- data/lib/flat_map/version.rb +3 -0
- data/spec/flat_map/errors_spec.rb +23 -0
- data/spec/flat_map/mapper/attribute_methods_spec.rb +36 -0
- data/spec/flat_map/mapper/callbacks_spec.rb +76 -0
- data/spec/flat_map/mapper/factory_spec.rb +285 -0
- data/spec/flat_map/mapper/mapping_spec.rb +98 -0
- data/spec/flat_map/mapper/mounting_spec.rb +142 -0
- data/spec/flat_map/mapper/persistence_spec.rb +152 -0
- data/spec/flat_map/mapper/skipping_spec.rb +91 -0
- data/spec/flat_map/mapper/targeting_spec.rb +156 -0
- data/spec/flat_map/mapper/traits_spec.rb +172 -0
- data/spec/flat_map/mapper/validations_spec.rb +72 -0
- data/spec/flat_map/mapper_spec.rb +9 -0
- data/spec/flat_map/mapping/factory_spec.rb +12 -0
- data/spec/flat_map/mapping/reader/basic_spec.rb +15 -0
- data/spec/flat_map/mapping/reader/formatted_spec.rb +62 -0
- data/spec/flat_map/mapping/reader/method_spec.rb +13 -0
- data/spec/flat_map/mapping/reader/proc_spec.rb +13 -0
- data/spec/flat_map/mapping/writer/basic_spec.rb +15 -0
- data/spec/flat_map/mapping/writer/method_spec.rb +13 -0
- data/spec/flat_map/mapping/writer/proc_spec.rb +13 -0
- data/spec/flat_map/mapping_spec.rb +123 -0
- data/spec/flat_map/open_mapper_spec.rb +19 -0
- data/spec/spec_helper.rb +7 -0
- data/tmp/metric_fu/_data/20131218.yml +6902 -0
- data/tmp/metric_fu/_data/20131219.yml +6726 -0
- metadata +220 -0
@@ -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 OpenMapper::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 {OpenMapper::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 OpenMapper::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::OpenMapper::Factory>]
|
25
|
+
def mount(*args, &block)
|
26
|
+
mountings << FlatMap::OpenMapper::Factory.new(*args, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
# List of mountings (factories) of a class.
|
30
|
+
#
|
31
|
+
# @return [Array<FlatMap::OpenMapper>]
|
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::OpenMapper>]
|
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::OpenMapper>]
|
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::OpenMapper>]
|
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}. Left here for consistency.
|
94
|
+
#
|
95
|
+
# @return [Array<FlatMap::OpenMapper>]
|
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::OpenMapper>] 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::OpenMapper>]
|
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::OpenMapper.
|
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
|
+
self.class.protected_instance_methods.include?(name)
|
160
|
+
|
161
|
+
return self[name] if mapping(name).present?
|
162
|
+
|
163
|
+
mounting = all_mountings.find{ |mount| mount.respond_to?(name) }
|
164
|
+
return super if mounting.nil?
|
165
|
+
mounting.send(name, *args, &block)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,178 @@
|
|
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
|
+
#
|
6
|
+
# In particular, validation and save methods are defined here. And
|
7
|
+
# the <tt>save</tt> method itself is defined as a callback. Also, Rails
|
8
|
+
# multiparam attributes extraction is defined within this module.
|
9
|
+
module OpenMapper::Persistence
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
define_callbacks :save
|
14
|
+
end
|
15
|
+
|
16
|
+
# ModelMethods class macros
|
17
|
+
module ClassMethods
|
18
|
+
# Create a new mapper object wrapped around new instance of its
|
19
|
+
# +target_class+, with a list of passed +traits+ applied to it.
|
20
|
+
#
|
21
|
+
# @param [*Symbol] traits
|
22
|
+
# @return [FlatMap::OpenMapper] mapper
|
23
|
+
def build(*traits, &block)
|
24
|
+
new(target_class.new, *traits, &block)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Default target class for OpenMapper is OpenStruct.
|
28
|
+
#
|
29
|
+
# @return [Class] class
|
30
|
+
def target_class
|
31
|
+
OpenStruct
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Write a passed set of +params+. Then try to save the model if +self+
|
36
|
+
# passes validation. Saving is performed in a transaction.
|
37
|
+
#
|
38
|
+
# @param [Hash] params
|
39
|
+
# @return [Boolean]
|
40
|
+
def apply(params)
|
41
|
+
write(params)
|
42
|
+
valid? && save
|
43
|
+
end
|
44
|
+
|
45
|
+
# Extract the multiparam values from the passed +params+. Then use the
|
46
|
+
# resulting hash to assign values to the target. Assignment is performed
|
47
|
+
# by sending writer methods to +self+ that correspond to keys in the
|
48
|
+
# resulting +params+ hash.
|
49
|
+
#
|
50
|
+
# @param [Hash] params
|
51
|
+
# @return [Hash] params
|
52
|
+
def write(params)
|
53
|
+
extract_multiparams!(params)
|
54
|
+
|
55
|
+
params.each do |name, value|
|
56
|
+
self.send("#{name}=", value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Try to save the target and send a +save+ method to all mounted mappers.
|
61
|
+
#
|
62
|
+
# The order in which mappings are saved is important, since we save
|
63
|
+
# records with :validate => false option. Since Rails will perform
|
64
|
+
# auto-saving on associations (and it in its turn will try to save associated
|
65
|
+
# record with :validate => true option. To be more precise, with
|
66
|
+
# :validate => !autosave option, where autosave corresponds to that option
|
67
|
+
# of reflection, which is usually not specified, i.e. nil), we might come to
|
68
|
+
# a situation of saving a record with nil foreign key for belongs_to association,
|
69
|
+
# which will raise exception. Thus, we want to explicitly save records in
|
70
|
+
# order which will allow them to be saved.
|
71
|
+
# Return +false+ if that chain of +save+ calls returns +true+ on any of
|
72
|
+
# its elements. Return +true+ otherwise.
|
73
|
+
#
|
74
|
+
# Saving is performed as a callback.
|
75
|
+
#
|
76
|
+
# @return [Boolean]
|
77
|
+
def save
|
78
|
+
before_res = save_mountings(before_save_mountings)
|
79
|
+
target_res = self_mountings.map{ |mount| mount.shallow_save }.all?
|
80
|
+
after_res = save_mountings(after_save_mountings)
|
81
|
+
|
82
|
+
before_res && target_res && after_res
|
83
|
+
end
|
84
|
+
|
85
|
+
# Return +true+ since OpenStruct is always 'saved'.
|
86
|
+
#
|
87
|
+
# @return [true]
|
88
|
+
def save_target
|
89
|
+
true
|
90
|
+
end
|
91
|
+
|
92
|
+
# Perform target save with callbacks call
|
93
|
+
#
|
94
|
+
# @return [Boolean]
|
95
|
+
def shallow_save
|
96
|
+
run_callbacks(:save){ save_target }
|
97
|
+
end
|
98
|
+
|
99
|
+
# Return +true+ if target was updated.
|
100
|
+
#
|
101
|
+
# @return [Boolean]
|
102
|
+
def persisted?
|
103
|
+
target != OpenStruct.new
|
104
|
+
end
|
105
|
+
|
106
|
+
# Send <tt>:save</tt> method to all mountings in list. Will return +true+
|
107
|
+
# only if all savings are positive.
|
108
|
+
#
|
109
|
+
# @param [Array<FlatMap::OpenMapper>] mountings
|
110
|
+
# @return [Boolean]
|
111
|
+
def save_mountings(mountings)
|
112
|
+
mountings.map{ |mount| mount.save }.all?
|
113
|
+
end
|
114
|
+
private :save_mountings
|
115
|
+
|
116
|
+
# Return +true+ if the mapper is valid, i.e. if it is valid itself, and if
|
117
|
+
# all mounted mappers (traits and other mappers) are also valid.
|
118
|
+
#
|
119
|
+
# Accepts any parameters, but doesn't use them to be compatible with
|
120
|
+
# ActiveRecord calls.
|
121
|
+
#
|
122
|
+
# @return [Boolean]
|
123
|
+
def valid?(*)
|
124
|
+
res = trait_mountings.map(&:valid?).all?
|
125
|
+
res = super && res # we do want to call super
|
126
|
+
mounting_res = mapper_mountings.map(&:valid?).all?
|
127
|
+
consolidate_errors!
|
128
|
+
res && mounting_res
|
129
|
+
end
|
130
|
+
|
131
|
+
# Consolidate the errors of all mounted mappers to a set of errors of +self+.
|
132
|
+
#
|
133
|
+
# @return [Array<ActiveModel::Errors>]
|
134
|
+
def consolidate_errors!
|
135
|
+
mountings.map(&:errors).each do |errs|
|
136
|
+
errors.messages.merge!(errs.to_hash){ |key, old, new| old.concat(new) }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
private :consolidate_errors!
|
140
|
+
|
141
|
+
# Overridden to use {FlatMap::Errors} instead of ActiveModel ones.
|
142
|
+
#
|
143
|
+
# @return [FlatMap::Errors]
|
144
|
+
def errors
|
145
|
+
@errors ||= FlatMap::Errors.new(self)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Extract Rails multiparam parameters from the +params+, modifying
|
149
|
+
# original hash. Behaves somewhat like
|
150
|
+
# {ActiveRecord::AttributeAssignment#extract_callstack_for_multiparameter_attributes}
|
151
|
+
# See this method for more details.
|
152
|
+
#
|
153
|
+
# @param [Hash] params
|
154
|
+
# @return [Array<FlatMap::Mapping>] return value is not used, original
|
155
|
+
# +params+ hash is modified instead and used later on.
|
156
|
+
def extract_multiparams!(params)
|
157
|
+
all_mappings.select(&:multiparam?).each do |mapping|
|
158
|
+
param_keys = params.keys.
|
159
|
+
select { |key| key.to_s =~ /#{mapping.full_name}\(\d+[isf]\)/ }.
|
160
|
+
sort_by{ |key| key.to_s[/\((\d+)\w*\)/, 1].to_i }
|
161
|
+
|
162
|
+
next if param_keys.empty?
|
163
|
+
|
164
|
+
args = param_keys.inject([]) do |values, key|
|
165
|
+
value = params.delete key
|
166
|
+
type = key[/\(\d+(\w*)\)/, 1]
|
167
|
+
value = value.send("to_#{type}") unless type.blank?
|
168
|
+
|
169
|
+
values.push value
|
170
|
+
values
|
171
|
+
end
|
172
|
+
|
173
|
+
params[mapping.name] = mapping.multiparam.new(*args) rescue nil
|
174
|
+
end
|
175
|
+
end
|
176
|
+
private :extract_multiparams!
|
177
|
+
end
|
178
|
+
end
|