flat_map 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +9 -9
- data/lib/flat_map.rb +8 -3
- data/lib/flat_map/{mapper.rb → model_mapper.rb} +3 -22
- data/lib/flat_map/{mapper/targeting.rb → model_mapper/persistence.rb} +5 -27
- data/lib/flat_map/{mapper → model_mapper}/skipping.rb +4 -4
- data/lib/flat_map/{base_mapper.rb → open_mapper.rb} +28 -10
- data/lib/flat_map/{base_mapper → open_mapper}/attribute_methods.rb +13 -12
- data/lib/flat_map/{base_mapper → open_mapper}/factory.rb +62 -57
- data/lib/flat_map/{base_mapper → open_mapper}/mapping.rb +3 -3
- data/lib/flat_map/{base_mapper → open_mapper}/mounting.rb +16 -16
- data/lib/flat_map/{base_mapper → open_mapper}/persistence.rb +47 -14
- data/lib/flat_map/{base_mapper → open_mapper}/skipping.rb +9 -5
- data/lib/flat_map/{base_mapper → open_mapper}/traits.rb +15 -14
- data/lib/flat_map/version.rb +1 -1
- data/spec/flat_map/mapper/factory_spec.rb +57 -38
- data/spec/flat_map/mapper/persistence_spec.rb +152 -0
- metadata +38 -17
- data/lib/flat_map/empty_mapper.rb +0 -29
- data/spec/flat_map/empty_mapper_spec.rb +0 -36
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NmZiN2RlYjcwY2U5YmIzMzAyYWM3ZGE3NzA4NDQyMDI2OGE0MDFhYw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
7
|
-
|
6
|
+
MTYwMjllODAzY2IwNDQ4ZGNiNTJmZDA2NzgxMDU0N2ZkZDEzN2EwMA==
|
7
|
+
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZDU4ZDYzYzAzYTBhMDkwZDRlODliODc3ODcyMWM3ODE2YzZkYWVhMmMwN2Nj
|
10
|
+
NWJkNDdhOTc1N2RiMTkwZjFlOGYzOGVlZWY4ZjlhZDliZjQ3YmEwYWY2YjU0
|
11
|
+
YTJiMDA0ZjdiMWQ4NGQzYjQwZGEyMzIyNzAwNWYzZjBmMWUxNjQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
N2UyMjkzNmRkNjQ0OWI0YmYyOGEwMDE2ZjFjYzc2MWFkN2MxMjZlZTM4MDQ2
|
14
|
+
YThhYzUyYzkwZDcxMzFmODI5MzQ0MDQ2M2M5MmQzY2UzMzljODYzMmI2NmUw
|
15
|
+
OTQ2MmNkNTljMjgyYmEwN2NhMzJhYTBjZjhlMDgzMDZkOTJmOTA=
|
data/lib/flat_map.rb
CHANGED
@@ -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/
|
8
|
-
require 'flat_map/
|
9
|
-
|
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
|
183
|
+
class ModelMapper < OpenMapper
|
184
184
|
extend ActiveSupport::Autoload
|
185
185
|
|
186
|
-
autoload :
|
186
|
+
autoload :Persistence
|
187
187
|
autoload :Skipping
|
188
188
|
|
189
|
-
include
|
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
|
3
|
-
#
|
4
|
-
|
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
|
75
|
-
base_mapper_index = ancestor_classes.index(::FlatMap::
|
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
|
3
|
-
# {
|
4
|
-
#
|
5
|
-
module
|
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
|
-
#
|
3
|
-
#
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
-
#
|
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
|
-
# @
|
44
|
-
|
45
|
-
|
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
|
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
|
-
|
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
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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 |
|
43
|
-
|
44
|
-
|
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("#{
|
47
|
-
|
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
|
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::
|
49
|
+
# @return [Class] ancestor of {FlatMap::OpenMapper}
|
50
50
|
def mapper_class
|
51
|
-
|
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
|
-
|
54
|
-
|
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
|
73
|
+
# Return +true+ if mapper to me mounted is +ModelMapper+
|
58
74
|
#
|
59
75
|
# @return [Boolean]
|
60
|
-
def
|
61
|
-
mapper_class <
|
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::
|
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
|
-
|
76
|
-
|
77
|
-
|
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
|
-
#
|
81
|
-
# fetched from it and should be specified explicitly.
|
106
|
+
# Return new instance of +target_class+ of the mapper.
|
82
107
|
#
|
83
|
-
# @
|
84
|
-
|
85
|
-
|
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 [
|
117
|
+
# @param [FlatMap::OpenMapper] mapper
|
104
118
|
# @return [Object, nil] target for new mapper.
|
105
|
-
def explicit_target(
|
119
|
+
def explicit_target(mapper)
|
106
120
|
if @options.key?(:target)
|
107
121
|
target = @options[:target]
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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.
|
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
|
161
|
-
reflection
|
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::
|
189
|
+
# @param [FlatMap::OpenMapper] mapper
|
177
190
|
# @return [Symbol]
|
178
191
|
def fetch_save_order(mapper)
|
179
|
-
return :after
|
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::
|
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
|
-
|
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
|
208
|
-
new_one.name
|
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?
|
237
|
+
mapper_class.mountings.any? do |factory|
|
233
238
|
factory.traited? &&
|
234
|
-
|
235
|
-
|
239
|
+
factory.required_for_any_trait?(traits)
|
240
|
+
end
|
236
241
|
end
|
237
242
|
end
|
238
243
|
end
|