flat_map 0.0.3 → 0.1.0
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 +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
|