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.
- checksums.yaml +15 -0
- data/.gitignore +31 -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/LICENSE +20 -0
- data/README.markdown +211 -0
- data/Rakefile +15 -0
- data/flat_map.gemspec +30 -0
- data/lib/flat_map.rb +9 -0
- data/lib/flat_map/base_mapper.rb +95 -0
- data/lib/flat_map/base_mapper/attribute_methods.rb +54 -0
- data/lib/flat_map/base_mapper/factory.rb +238 -0
- data/lib/flat_map/base_mapper/mapping.rb +123 -0
- data/lib/flat_map/base_mapper/mounting.rb +168 -0
- data/lib/flat_map/base_mapper/persistence.rb +145 -0
- data/lib/flat_map/base_mapper/skipping.rb +62 -0
- data/lib/flat_map/base_mapper/traits.rb +94 -0
- data/lib/flat_map/empty_mapper.rb +29 -0
- data/lib/flat_map/errors.rb +57 -0
- data/lib/flat_map/mapper.rb +213 -0
- data/lib/flat_map/mapper/skipping.rb +45 -0
- data/lib/flat_map/mapper/targeting.rb +130 -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/version.rb +3 -0
- data/spec/flat_map/empty_mapper_spec.rb +36 -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 +258 -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/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/spec_helper.rb +7 -0
- data/tmp/metric_fu/_data/20131218.yml +6902 -0
- data/tmp/metric_fu/_data/20131219.yml +6726 -0
- metadata +184 -0
@@ -0,0 +1,45 @@
|
|
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
|
6
|
+
# Extend original #skip! method for Rails-models-based targets
|
7
|
+
#
|
8
|
+
# Note that this will mark the target record as
|
9
|
+
# destroyed if it is a new record. Thus, this
|
10
|
+
# record will not be a subject of Rails associated
|
11
|
+
# validation procedures, and will not be saved as an
|
12
|
+
# associated record.
|
13
|
+
#
|
14
|
+
# @return [Object]
|
15
|
+
def skip!
|
16
|
+
super
|
17
|
+
if target.is_a?(ActiveRecord::Base)
|
18
|
+
if target.new_record?
|
19
|
+
# Using the instance variable directly as {ActiveRecord::Base#delete}
|
20
|
+
# will freeze the record.
|
21
|
+
target.instance_variable_set('@destroyed', true)
|
22
|
+
else
|
23
|
+
# Using reload instead of reset_changes! to reset associated nested
|
24
|
+
# model changes also
|
25
|
+
target.reload
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Extend original #use! method for Rails-models-based targets, as
|
31
|
+
# acoompanied to #skip! method.
|
32
|
+
#
|
33
|
+
# @return [Object]
|
34
|
+
def use!
|
35
|
+
super
|
36
|
+
if target.is_a?(ActiveRecord::Base)
|
37
|
+
if target.new_record?
|
38
|
+
target.instance_variable_set('@destroyed', false)
|
39
|
+
else
|
40
|
+
all_nested_mountings.each(&:use!)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,130 @@
|
|
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
|
+
|
16
|
+
extend ActiveSupport::Concern
|
17
|
+
|
18
|
+
included do
|
19
|
+
define_callbacks :save
|
20
|
+
|
21
|
+
# Writer of the target class name. Allows manual control over target
|
22
|
+
# class of the mapper, for example:
|
23
|
+
#
|
24
|
+
# class CustomerMapper
|
25
|
+
# self.target_class_name = 'Customer::Active'
|
26
|
+
# end
|
27
|
+
class_attribute :target_class_name
|
28
|
+
end
|
29
|
+
|
30
|
+
# ModelMethods class macros
|
31
|
+
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
|
+
# Find a record of the +target_class+ by +id+ and use it as a
|
42
|
+
# target for a new mapper, with a list of passed +traits+ applied
|
43
|
+
# to it.
|
44
|
+
#
|
45
|
+
# @param [#to_i] id of the record
|
46
|
+
# @param [*Symbol] traits
|
47
|
+
# @return [FlatMap::Mapper] mapper
|
48
|
+
def find(id, *traits, &block)
|
49
|
+
new(target_class.find(id), *traits, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Fetch a class for the target of the mapper.
|
53
|
+
#
|
54
|
+
# @return [Class] class
|
55
|
+
def target_class
|
56
|
+
(target_class_name || default_target_class_name).constantize
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return target class name based on name of the ancestor mapper
|
60
|
+
# that is closest to {FlatMap::Mapper}, which may be +self+.
|
61
|
+
#
|
62
|
+
# class VehicleMapper
|
63
|
+
# # some definitions
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# class CarMapper < VehicleMapper
|
67
|
+
# # some more definitions
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# CarMapper.target_class # => Vehicle
|
71
|
+
#
|
72
|
+
# @return [String]
|
73
|
+
def default_target_class_name
|
74
|
+
ancestor_classes = ancestors.select{ |ancestor| ancestor.is_a? Class }
|
75
|
+
base_mapper_index = ancestor_classes.index(::FlatMap::Mapper)
|
76
|
+
ancestor_classes[base_mapper_index - 1].name[/^([\w:]+)Mapper.*$/, 1]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Return a 'mapper' string as a model_name. Used by Rails FormBuilder.
|
81
|
+
#
|
82
|
+
# @return [String]
|
83
|
+
def model_name
|
84
|
+
'mapper'
|
85
|
+
end
|
86
|
+
|
87
|
+
# Delegate to the target's #to_key method.
|
88
|
+
# @return [String]
|
89
|
+
def to_key
|
90
|
+
target.to_key
|
91
|
+
end
|
92
|
+
|
93
|
+
# Write a passed set of +params+. Then try to save the model if +self+
|
94
|
+
# passes validation. Saving is performed in a transaction.
|
95
|
+
#
|
96
|
+
# @param [Hash] params
|
97
|
+
# @return [Boolean]
|
98
|
+
def apply(params)
|
99
|
+
write(params)
|
100
|
+
res = if valid?
|
101
|
+
ActiveRecord::Base.transaction do
|
102
|
+
save
|
103
|
+
end
|
104
|
+
end
|
105
|
+
!!res
|
106
|
+
end
|
107
|
+
|
108
|
+
# Save +target+
|
109
|
+
#
|
110
|
+
# @return [Boolean]
|
111
|
+
def save_target
|
112
|
+
return true if owned?
|
113
|
+
target.respond_to?(:save) ? target.save(:validate => false) : true
|
114
|
+
end
|
115
|
+
|
116
|
+
# Delegate persistence to target.
|
117
|
+
#
|
118
|
+
# @return [Boolean]
|
119
|
+
def persisted?
|
120
|
+
target.respond_to?(:persisted?) ? target.persisted? : false
|
121
|
+
end
|
122
|
+
|
123
|
+
# Delegate #id to target, if possible.
|
124
|
+
#
|
125
|
+
# @return [Fixnum, nil]
|
126
|
+
def id
|
127
|
+
target.id if target.respond_to?(:id)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module FlatMap
|
2
|
+
# Encapsulates mapping concept used by mappers. Each mapping belongs to
|
3
|
+
# a particular mapper and has its own reader and writer objects.
|
4
|
+
class Mapping
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
|
7
|
+
autoload :Reader
|
8
|
+
autoload :Writer
|
9
|
+
autoload :Factory
|
10
|
+
|
11
|
+
attr_reader :mapper, :name, :full_name, :target_attribute
|
12
|
+
attr_reader :reader, :writer
|
13
|
+
attr_reader :multiparam
|
14
|
+
|
15
|
+
delegate :target, :to => :mapper
|
16
|
+
delegate :write, :to => :writer, :allow_nil => true
|
17
|
+
delegate :read, :to => :reader, :allow_nil => true
|
18
|
+
|
19
|
+
# Initialize a mapping, passing to it a +mapper+, which is
|
20
|
+
# a gateway to actual +target+, +name+, which is an external
|
21
|
+
# identifier, +target_attribute+, which is used to access
|
22
|
+
# actual information of the +target+, and +options+.
|
23
|
+
#
|
24
|
+
# @param [FlatMap::Mapper] mapper
|
25
|
+
# @param [Symbol] name
|
26
|
+
# @param [Symbol] target_attribute
|
27
|
+
# @param [Hash] options
|
28
|
+
# @option [Symbol, Proc] :reader specifies how value will
|
29
|
+
# be read from the +target+
|
30
|
+
# @option [Symbol] :format specifies additional processing
|
31
|
+
# of the value on reading
|
32
|
+
# @option [Symbol, Proc] :writer specifies how value will
|
33
|
+
# be written to the +target+
|
34
|
+
# @option [Class] :multiparam specifies multiparam Class,
|
35
|
+
# object of which will be instantiated on writing
|
36
|
+
# multiparam attribute passed from the Rails form
|
37
|
+
def initialize(mapper, name, target_attribute, options = {})
|
38
|
+
@mapper = mapper
|
39
|
+
@name = name
|
40
|
+
@target_attribute = target_attribute
|
41
|
+
|
42
|
+
@full_name = mapper.suffixed? ? :"#{@name}_#{mapper.suffix}" : name
|
43
|
+
|
44
|
+
@multiparam = options[:multiparam]
|
45
|
+
|
46
|
+
fetch_reader(options)
|
47
|
+
fetch_writer(options)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return +true+ if the mapping was created with the <tt>:multiparam</tt>
|
51
|
+
# option set.
|
52
|
+
#
|
53
|
+
# @return [Boolean]
|
54
|
+
def multiparam?
|
55
|
+
!!@multiparam
|
56
|
+
end
|
57
|
+
|
58
|
+
# Lookup the passed hash of params for the key that corresponds
|
59
|
+
# to the +full_name+ of +self+, and write it if it is present.
|
60
|
+
#
|
61
|
+
# @param [Hash] params
|
62
|
+
# @return [Object] value assigned
|
63
|
+
def write_from_params(params)
|
64
|
+
write(params[full_name]) if params.key?(full_name) && writer.present?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return a hash of a single key => value pair, where key
|
68
|
+
# corresponds to +full_name+ and +value+ to value read from
|
69
|
+
# +target+. If +reader+ is not set, return an empty hash.
|
70
|
+
#
|
71
|
+
# @return [Hash]
|
72
|
+
def read_as_params
|
73
|
+
reader ? {full_name => read} : {}
|
74
|
+
end
|
75
|
+
|
76
|
+
# Instantiate a +reader+ object based on the <tt>:reader</tt>
|
77
|
+
# and <tt>:format</tt> values of +options+.
|
78
|
+
#
|
79
|
+
# @param [Hash] options
|
80
|
+
# @return [FlatMap::Mapping::Reader::Basic]
|
81
|
+
def fetch_reader(options)
|
82
|
+
options_reader = options[:reader]
|
83
|
+
|
84
|
+
@reader =
|
85
|
+
case options_reader
|
86
|
+
when Symbol, String
|
87
|
+
Reader::Method.new(self, options_reader)
|
88
|
+
when Proc
|
89
|
+
Reader::Proc.new(self, options_reader)
|
90
|
+
when false
|
91
|
+
nil
|
92
|
+
else
|
93
|
+
if options.key?(:format) then
|
94
|
+
Reader::Formatted.new(self, options[:format])
|
95
|
+
else
|
96
|
+
Reader::Basic.new(self)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
private :fetch_reader
|
101
|
+
|
102
|
+
# Instantiate a +writer+ object based on the <tt>:writer</tt>
|
103
|
+
# value of +options+.
|
104
|
+
#
|
105
|
+
# @param [Hash] options
|
106
|
+
# @return [FlatMap::Mapping::Writer::Basic]
|
107
|
+
def fetch_writer(options)
|
108
|
+
options_writer = options[:writer]
|
109
|
+
|
110
|
+
@writer =
|
111
|
+
case options_writer
|
112
|
+
when Symbol, String
|
113
|
+
Writer::Method.new(self, options_writer)
|
114
|
+
when Proc
|
115
|
+
Writer::Proc.new(self, options_writer)
|
116
|
+
when false
|
117
|
+
nil
|
118
|
+
else
|
119
|
+
Writer::Basic.new(self)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
private :fetch_writer
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FlatMap
|
2
|
+
# Factory objects store mapping definitions within mapper class and are
|
3
|
+
# used eventually to generate mapping objects for a particular mapper.
|
4
|
+
class Mapping::Factory
|
5
|
+
# Simply store all arguments necessary to create a new mapping for
|
6
|
+
# a specific mapper.
|
7
|
+
#
|
8
|
+
# @param [*Object] args
|
9
|
+
def initialize(*args)
|
10
|
+
@args = args
|
11
|
+
end
|
12
|
+
|
13
|
+
# Return a new mapping, initialized by +mapper+ and <tt>@args</tt>.
|
14
|
+
#
|
15
|
+
# @param [FlatMap::Mapper] mapper
|
16
|
+
# @return [FlatMap::Mapping]
|
17
|
+
def create(mapper)
|
18
|
+
Mapping.new(mapper, *@args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module FlatMap
|
2
|
+
# Reader module hosts various readers that are used by
|
3
|
+
# mappings for reading and returning values.
|
4
|
+
module Mapping::Reader
|
5
|
+
extend ActiveSupport::Autoload
|
6
|
+
|
7
|
+
autoload :Basic
|
8
|
+
autoload :Method
|
9
|
+
autoload :Proc
|
10
|
+
autoload :Formatted
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module FlatMap
|
2
|
+
module Mapping::Reader
|
3
|
+
# Basic reader simply sends a mapped attribute to the target
|
4
|
+
# and returns the result value.
|
5
|
+
class Basic
|
6
|
+
attr_reader :mapping
|
7
|
+
|
8
|
+
delegate :target, :target_attribute, :to => :mapping
|
9
|
+
|
10
|
+
# Initialize the reader with a mapping.
|
11
|
+
#
|
12
|
+
# @param [FlatMap::Mapping] mapping
|
13
|
+
def initialize(mapping)
|
14
|
+
@mapping = mapping
|
15
|
+
end
|
16
|
+
|
17
|
+
# Send the attribute method to the target and return its value.
|
18
|
+
# As a base class for readers, it allows to pass additional
|
19
|
+
# arguments when reading value (for example, used by :enum
|
20
|
+
# format of {Formatted} reader)
|
21
|
+
#
|
22
|
+
# @return [Object] value returned by reading
|
23
|
+
def read(*)
|
24
|
+
target.send(target_attribute)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module FlatMap
|
2
|
+
module Mapping::Reader
|
3
|
+
# Formatted reader reads the value the same as Basic reader does, but
|
4
|
+
# additionally performs value postprocessing. All processing methods
|
5
|
+
# are defined within {Formatted::Formats} module. The method is chosen
|
6
|
+
# based on the :format option when the mapping is defined.
|
7
|
+
class Formatted < Basic
|
8
|
+
extend ActiveSupport::Autoload
|
9
|
+
autoload :Formats
|
10
|
+
|
11
|
+
include Formats
|
12
|
+
|
13
|
+
# Initialize the reader with a +mapping+ and a +format+.
|
14
|
+
#
|
15
|
+
# @param [FlatMap::Mapping] mapping
|
16
|
+
# @param [Symbol] format
|
17
|
+
def initialize(mapping, format)
|
18
|
+
@mapping, @format = mapping, format
|
19
|
+
end
|
20
|
+
|
21
|
+
# Read the value just like the {Basic} reader does, but
|
22
|
+
# additionally send the returned value to its format method.
|
23
|
+
#
|
24
|
+
# Additional arguments will be passed to formatting function
|
25
|
+
# of the mapping's format.
|
26
|
+
#
|
27
|
+
# @return [Object] formatted value
|
28
|
+
def read(*args)
|
29
|
+
format_value(super, *args)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Send the +value+ to the format method, defined in the {Format}
|
33
|
+
# module and specified upon reader initialization.
|
34
|
+
#
|
35
|
+
# Additional optional arguments are passed as well.
|
36
|
+
#
|
37
|
+
# @param [Object] value
|
38
|
+
# @return [Object] formatted value
|
39
|
+
def format_value(value, *args)
|
40
|
+
send(@format, value, *args)
|
41
|
+
end
|
42
|
+
private :format_value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module FlatMap
|
2
|
+
module Mapping::Reader
|
3
|
+
# Hosts various formats that can be applied to values read by mappings
|
4
|
+
# for post-processing.
|
5
|
+
module Formatted::Formats
|
6
|
+
if defined? I18n
|
7
|
+
# Pass +value+ to <tt>I18n::l</tt> method.
|
8
|
+
def i18n_l(value)
|
9
|
+
I18n::l(value) if value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
if defined? PowerEnum
|
14
|
+
# Return the specified +property+ of a +value+ which
|
15
|
+
# is supposed to be an +enum+ record. By default,
|
16
|
+
# uses <tt>:name</tt>. However, <tt>:description</tt>
|
17
|
+
# might be also useful for UI purposes.
|
18
|
+
#
|
19
|
+
# @param [Object] value
|
20
|
+
# @param [Symbol] property
|
21
|
+
# @return [Object]
|
22
|
+
def enum(value, property = :name)
|
23
|
+
value.send(property) if value
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module FlatMap
|
2
|
+
module Mapping::Reader
|
3
|
+
# Method mapper calls a method, defined by the mapper, sending
|
4
|
+
# the mapping object to it as an argument.
|
5
|
+
class Method < Basic
|
6
|
+
delegate :mapper, :to => :mapping
|
7
|
+
|
8
|
+
# Initialize the reader with a +mapping+ and a +method+.
|
9
|
+
#
|
10
|
+
# @param [FlatMap::Mapping] mapping
|
11
|
+
# @param [Symbol] method name
|
12
|
+
def initialize(mapping, method)
|
13
|
+
@mapping, @method = mapping, method
|
14
|
+
end
|
15
|
+
|
16
|
+
# Send the <tt>@method</tt> to the mapping's mapper, passing
|
17
|
+
# the mapping itself to it.
|
18
|
+
#
|
19
|
+
# @return [Object] value returned by reader
|
20
|
+
def read
|
21
|
+
mapper.send(@method, mapping)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|