two-way-mapper 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 524242de8d10dfa77f277974cd5e1670dcc68b0345b371d847a687cce8998c59
4
- data.tar.gz: e4b0d1784f91ee600a316c161a8bb97b3bccabd6c2d97a1296dbb82596a93c08
3
+ metadata.gz: 7b43b700b85546bce3d9c79425c5d82a20e4e63b24df74219296328c83c5f069
4
+ data.tar.gz: 5582e71aa4fc752c5fcc60709e65a7a58a72c119b529de1973845f768c804075
5
5
  SHA512:
6
- metadata.gz: ce3eb28e41536134ac6c221b41c7bc0c0fecd01092a8f2f26c7076d4d7249a93c26c1e8103a9644a8418e7d93e8570fd273a4eb96023b52d77f92138cfa81be8
7
- data.tar.gz: '09b3e9f0ccf3cc4e9f5ca8d3668b2cd7b253d7c9190f06daceb3afb3f3c0f4d58329d0a12ec394e6675fdd95ec79d1a28c26242037d24b0387193db6957d2c2c'
6
+ metadata.gz: 9f72fb8bfa5c89f4e810c955552d9fd4eed5d2a4d7bd67cc27f5bed42fe33d50c34447f2bf9f7ab16445385733cc3a5e5e0fa71a722ff621f2ab2e591c691945
7
+ data.tar.gz: ae220a4dea0fe3e3c836897822ac77d839a12d368bdd80f2f82aa2851fdebf25b057877aa55d6ab1632011f3de5ad7c4f0610e8407c66419103ba2bd2ccee271
@@ -18,10 +18,12 @@ module TwoWayMapper
18
18
  end
19
19
 
20
20
  def #{from}_selectors(mappable: false)
21
- rules.each_with_object [] do |rule, memo|
22
- next if mappable && rule.from_#{from}_to_#{to}_only?
23
-
24
- memo << rule.#{from}.selector
21
+ rules.flat_map do |rule|
22
+ if mappable && rule.from_#{from}_to_#{to}_only?
23
+ []
24
+ else
25
+ rule.#{from}_nodes.map(&:selector)
26
+ end
25
27
  end
26
28
  end
27
29
 
@@ -32,7 +34,7 @@ module TwoWayMapper
32
34
  CODE
33
35
  end
34
36
 
35
- def rule(left_selector, right_selector = {}, options = {})
37
+ def rule(left_selectors, right_selectors = {}, options = {})
36
38
  raise 'You need to set left before calling rule' unless left_class
37
39
  raise 'You need to set right before calling rule' unless right_class
38
40
 
@@ -40,20 +42,24 @@ module TwoWayMapper
40
42
  left_opt = opt.delete(:left) || {}
41
43
  right_opt = opt.delete(:right) || {}
42
44
 
43
- if left_selector.is_a?(Hash)
44
- raise ArgumentError if left_selector.count < 2
45
+ if left_selectors.is_a?(Hash)
46
+ raise ArgumentError if left_selectors.count < 2
45
47
 
46
- opt = left_selector
47
- left_selector = opt.keys.first
48
- left_opt.merge! opt.delete(left_selector)
49
- right_selector = opt.keys.first
50
- right_opt.merge!(opt.delete(right_selector))
48
+ opt = left_selectors
49
+ left_selectors = opt.keys.first
50
+ left_opt.merge! opt.delete(left_selectors)
51
+ right_selectors = opt.keys.first
52
+ right_opt.merge!(opt.delete(right_selectors))
51
53
  end
52
54
 
53
- left = left_class.new(left_selector, left_options.merge(left_opt))
54
- right = right_class.new(right_selector, right_options.merge(right_opt))
55
+ left_nodes = Array(left_selectors).map do |left_selector|
56
+ left_class.new(left_selector, left_options.merge(left_opt))
57
+ end
58
+ right_nodes = Array(right_selectors).map do |right_selector|
59
+ right_class.new(right_selector, right_options.merge(right_opt))
60
+ end
55
61
 
56
- @rules << Rule.new(left, right, opt)
62
+ @rules << Rule.new(left_nodes, right_nodes, opt)
57
63
  end
58
64
 
59
65
  private
@@ -40,11 +40,7 @@ module TwoWayMapper
40
40
 
41
41
  keys[0..to].each do |key|
42
42
  unless rewind_to?(obj, key)
43
- if block_given?
44
- yield(obj, key)
45
- else
46
- create_node(obj, key)
47
- end
43
+ block_given? ? yield(obj, key) : create_node(obj, key)
48
44
  end
49
45
  obj = next_key(obj, key)
50
46
  end
@@ -2,38 +2,28 @@
2
2
 
3
3
  module TwoWayMapper
4
4
  class Rule
5
- attr_reader :left, :right
5
+ attr_reader :left_nodes, :right_nodes
6
6
 
7
- def initialize(left, right, opt = {})
8
- @left = left
9
- @right = right
7
+ def initialize(left_nodes, right_nodes, opt = {})
8
+ @left_nodes = left_nodes
9
+ @right_nodes = right_nodes
10
10
  @options = opt
11
11
  end
12
12
 
13
13
  def from_left_to_right(left_obj, right_obj)
14
14
  return right_obj if from_right_to_left_only?
15
15
 
16
- value = left.read(left_obj)
17
- value = map_value(value, true)
18
- if @options[:on_left_to_right].respond_to?(:call)
19
- value = @options[:on_left_to_right].call(value, left_obj, right_obj)
20
- end
21
- right.write(right_obj, value)
16
+ value = read(left_nodes, [left_obj, right_obj], true)
22
17
 
23
- right_obj
18
+ write(right_nodes, right_obj, value)
24
19
  end
25
20
 
26
21
  def from_right_to_left(left_obj, right_obj)
27
22
  return left_obj if from_left_to_right_only?
28
23
 
29
- value = right.read(right_obj)
30
- value = map_value(value, false)
31
- if @options[:on_right_to_left].respond_to?(:call)
32
- value = @options[:on_right_to_left].call(value, left_obj, right_obj)
33
- end
34
- left.write(left_obj, value)
24
+ value = read(right_nodes, [left_obj, right_obj], false)
35
25
 
36
- left_obj
26
+ write(left_nodes, left_obj, value)
37
27
  end
38
28
 
39
29
  def from_right_to_left_only?
@@ -46,7 +36,32 @@ module TwoWayMapper
46
36
 
47
37
  private
48
38
 
49
- def map_value(value, left_to_right = true)
39
+ def read(nodes, objects, left_to_right)
40
+ callback = left_to_right ? :on_left_to_right : :on_right_to_left
41
+ obj = left_to_right ? objects.first : objects.last
42
+ value = nil
43
+
44
+ nodes.each do |node|
45
+ value = node.read(obj)
46
+ value = map_value(value, left_to_right)
47
+ if @options[callback].respond_to?(:call)
48
+ args = [value] + objects + [node]
49
+ value = @options[callback].call(*args)
50
+ end
51
+
52
+ break if value
53
+ end
54
+
55
+ value
56
+ end
57
+
58
+ def write(nodes, obj, value)
59
+ nodes.each { |node| node.write(obj, value) }
60
+
61
+ obj
62
+ end
63
+
64
+ def map_value(value, left_to_right)
50
65
  map = @options[:map]
51
66
  if map.is_a?(Hash)
52
67
  map = map.invert unless left_to_right
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module TwoWayMapper
4
- VERSION = '0.1.2'
4
+ VERSION = '0.2.0'
5
5
  end
data/spec/mapping_spec.rb CHANGED
@@ -18,30 +18,43 @@ describe TwoWayMapper::Mapping do
18
18
  before do
19
19
  mapping.left :object
20
20
  mapping.right :object
21
+ end
21
22
 
23
+ before do
22
24
  mapping.rule 'firstname', 'FirstName'
23
25
  mapping.rule 'fullname', 'FullName', from_right_to_left_only: true
24
26
  mapping.rule 'lastname', 'LastName'
25
27
  mapping.rule 'fullname1', 'FullName1', from_left_to_right_only: true
28
+ mapping.rule ['field1', 'field2'], 'Field1'
29
+ mapping.rule 'field3', ['Field2', 'Field3']
30
+ mapping.rule ['field4', 'field5'], ['Field4', 'Field5']
26
31
  end
27
32
 
28
33
  describe '#left_selectors' do
29
34
  it 'should get left selectors' do
30
- expect(mapping.left_selectors).to eql %w(firstname fullname lastname fullname1)
35
+ expect(mapping.left_selectors).to eql(
36
+ %w(firstname fullname lastname fullname1 field1 field2 field3 field4 field5)
37
+ )
31
38
  end
32
39
 
33
40
  it 'should include only mappable selectors if such option is passed' do
34
- expect(mapping.left_selectors(mappable: true)).to eql %w(firstname fullname lastname)
41
+ expect(mapping.left_selectors(mappable: true)).to eql(
42
+ %w(firstname fullname lastname field1 field2 field3 field4 field5)
43
+ )
35
44
  end
36
45
  end
37
46
 
38
47
  describe '#right_selectors' do
39
48
  it 'should get right selectors' do
40
- expect(mapping.right_selectors).to eql %w(FirstName FullName LastName FullName1)
49
+ expect(mapping.right_selectors).to eql(
50
+ %w(FirstName FullName LastName FullName1 Field1 Field2 Field3 Field4 Field5)
51
+ )
41
52
  end
42
53
 
43
54
  it 'should include only mappable selectors if such option is passed' do
44
- expect(mapping.right_selectors(mappable: true)).to eql %w(FirstName LastName FullName1)
55
+ expect(mapping.right_selectors(mappable: true)).to eql(
56
+ %w(FirstName LastName FullName1 Field1 Field2 Field3 Field4 Field5)
57
+ )
45
58
  end
46
59
  end
47
60
  end
@@ -74,8 +87,21 @@ describe TwoWayMapper::Mapping do
74
87
 
75
88
  rule = mapping.rules.first
76
89
  expect(rule).to be_instance_of TwoWayMapper::Rule
77
- expect(rule.left.selector).to eql 'key1'
78
- expect(rule.right.selector).to eql 'Key1'
90
+ expect(rule.left_nodes.map(&:selector)).to eql ['key1']
91
+ expect(rule.right_nodes.map(&:selector)).to eql ['Key1']
92
+ end
93
+
94
+ it 'should support multiple selectors' do
95
+ mapping.rule ['key1', 'key2'], ['Key1', 'Key2']
96
+
97
+ rule = mapping.rules.first
98
+
99
+ expect(rule.left_nodes.size).to eql 2
100
+ expect(rule.left_nodes[0].selector).to eql 'key1'
101
+ expect(rule.left_nodes[1].selector).to eql 'key2'
102
+ expect(rule.right_nodes.size).to eql 2
103
+ expect(rule.right_nodes[0].selector).to eql 'Key1'
104
+ expect(rule.right_nodes[1].selector).to eql 'Key2'
79
105
  end
80
106
 
81
107
  it 'should allow to pass hash' do
@@ -84,16 +110,16 @@ describe TwoWayMapper::Mapping do
84
110
  mapping.rule 'key1' => { opt1: 'val' }, 'Key2' => {}
85
111
  rule = mapping.rules.first
86
112
 
87
- expect(rule.left.selector).to eql 'key1'
88
- expect(rule.right.selector).to eql 'Key2'
113
+ expect(rule.left_nodes.map(&:selector)).to eql ['key1']
114
+ expect(rule.right_nodes.map(&:selector)).to eql ['Key2']
89
115
  end
90
116
 
91
117
  it 'should allow to pass left abd right options ' do
92
118
  mapping.rule 'key1', 'Key2', left: { opt1: 'val' }, right: { opt2: 'val' }
93
119
  rule = mapping.rules.first
94
120
 
95
- expect(rule.left.options).to include opt1: 'val'
96
- expect(rule.right.options).to include opt2: 'val'
121
+ expect(rule.left_nodes.first.options).to include opt1: 'val'
122
+ expect(rule.right_nodes.first.options).to include opt2: 'val'
97
123
  end
98
124
 
99
125
  it 'should work with options copy' do
data/spec/rule_spec.rb CHANGED
@@ -2,16 +2,19 @@
2
2
 
3
3
  describe TwoWayMapper::Rule do
4
4
  let(:left_node) { TwoWayMapper::Node::Object.new('key1') }
5
+ let(:left_nodes) { [left_node] }
5
6
  let(:right_node) { TwoWayMapper::Node::Hash.new('Kk.Key1') }
7
+ let(:right_nodes) { [right_node] }
6
8
  let(:left_object) { OpenStruct.new }
7
9
  let(:map) { { 'value' => 'VALUE' } }
8
- let(:rule) { described_class.new(left_node, right_node, options) }
10
+ let(:options) { {} }
11
+ let(:rule) { described_class.new(left_nodes, right_nodes, options) }
9
12
 
10
13
  context 'without options' do
11
14
  let(:options) { {} }
12
15
 
13
16
  describe '#from_left_to_right' do
14
- it 'should read from left node and write to right node' do
17
+ it 'should read from the left node and write to the right node' do
15
18
  left_object.key1 = 'value1'
16
19
  right_object = {}
17
20
  rule.from_left_to_right(left_object, right_object)
@@ -21,7 +24,7 @@ describe TwoWayMapper::Rule do
21
24
  end
22
25
 
23
26
  describe '#from_right_to_left' do
24
- it 'should read from right node and write to left node' do
27
+ it 'should read from the right node and write to the left node' do
25
28
  left_object.key1 = nil
26
29
  right_object = { Kk: { Key1: 'value1' } }
27
30
  rule.from_right_to_left(left_object, right_object)
@@ -31,6 +34,32 @@ describe TwoWayMapper::Rule do
31
34
  end
32
35
  end
33
36
 
37
+ context 'with from_left_to_right_only/from_right_to_left_only options' do
38
+ describe '#from_left_to_right' do
39
+ let(:options) { { from_right_to_left_only: true } }
40
+
41
+ it 'should do nothing if from_right_to_left_only is set to true' do
42
+ left_object.key1 = 'value1'
43
+ right_object = {}
44
+ rule.from_left_to_right(left_object, right_object)
45
+
46
+ expect(right_object).to eql({})
47
+ end
48
+ end
49
+
50
+ describe '#from_right_to_left' do
51
+ let(:options) { { from_left_to_right_only: true } }
52
+
53
+ it 'should do nothing if from_left_to_right_only is set to true' do
54
+ left_object.key1 = nil
55
+ right_object = { Kk: { Key1: 'value1' } }
56
+ rule.from_right_to_left(left_object, right_object)
57
+
58
+ expect(left_object.key1).to be_nil
59
+ end
60
+ end
61
+ end
62
+
34
63
  context 'with map option' do
35
64
  let(:options) { { map: map } }
36
65
 
@@ -55,10 +84,10 @@ describe TwoWayMapper::Rule do
55
84
  end
56
85
  end
57
86
 
58
- context 'with default option' do
87
+ context 'with map and default option' do
59
88
  describe '#from_left_to_right' do
60
89
  it 'should return default value if not found' do
61
- rule = described_class.new left_node, right_node, map: map, default: 'not found'
90
+ rule = described_class.new [left_node], [right_node], map: map, default: 'not found'
62
91
 
63
92
  left_object.key1 = 'value1'
64
93
  right_object = {}
@@ -69,8 +98,8 @@ describe TwoWayMapper::Rule do
69
98
 
70
99
  it 'should return use default_left if present and value not found' do
71
100
  rule = described_class.new(
72
- left_node,
73
- right_node,
101
+ [left_node],
102
+ [right_node],
74
103
  map: map,
75
104
  default: 'not found',
76
105
  default_left: 'not found on left',
@@ -87,7 +116,7 @@ describe TwoWayMapper::Rule do
87
116
 
88
117
  describe '#from_right_to_left' do
89
118
  it 'should return default value if not found' do
90
- rule = described_class.new left_node, right_node, map: map, default: 'not found'
119
+ rule = described_class.new [left_node], [right_node], map: map, default: 'not found'
91
120
 
92
121
  left_object.key1 = nil
93
122
  right_object = { Kk: { Key1: 'VALUE1' } }
@@ -98,8 +127,8 @@ describe TwoWayMapper::Rule do
98
127
 
99
128
  it 'should return use default_right if present and value not found' do
100
129
  rule = described_class.new(
101
- left_node,
102
- right_node,
130
+ [left_node],
131
+ [right_node],
103
132
  map: map,
104
133
  default: 'not found',
105
134
  default_left: 'not found on left',
@@ -115,13 +144,13 @@ describe TwoWayMapper::Rule do
115
144
  end
116
145
  end
117
146
 
118
- describe 'with callback option' do
119
- describe 'on_left_to_right' do
147
+ context 'with callback option' do
148
+ describe '#on_left_to_right' do
120
149
  it 'should transform value if such options passed' do
121
150
  rule = described_class.new(
122
- left_node,
123
- right_node,
124
- on_left_to_right: ->(v, _l, _r) { v.upcase }
151
+ [left_node],
152
+ [right_node],
153
+ on_left_to_right: ->(v, _l, _r, _n) { v.upcase }
125
154
  )
126
155
 
127
156
  left_object.key1 = 'value1'
@@ -133,25 +162,25 @@ describe TwoWayMapper::Rule do
133
162
 
134
163
  it 'should pass left object and right object' do
135
164
  rule = described_class.new(
136
- left_node,
137
- right_node,
138
- on_left_to_right: ->(_v, l, r) { "#{l.object_id}-#{r.object_id}" }
165
+ [left_node],
166
+ [right_node],
167
+ on_left_to_right: ->(_v, l, r, n) { "#{l.object_id}-#{r.object_id}-#{n.selector}" }
139
168
  )
140
169
 
141
170
  left_object.key1 = 'value1'
142
171
  right_object = {}
143
172
  rule.from_left_to_right(left_object, right_object)
144
173
 
145
- expect(right_object).to eql Kk: { Key1: "#{left_object.object_id}-#{right_object.object_id}" }
174
+ expect(right_object).to eql Kk: { Key1: "#{left_object.object_id}-#{right_object.object_id}-#{left_node.selector}" }
146
175
  end
147
176
  end
148
177
 
149
- describe 'on_right_to_left' do
178
+ describe '#on_right_to_left' do
150
179
  it 'should transform value if such options passed' do
151
180
  rule = described_class.new(
152
- left_node,
153
- right_node,
154
- on_right_to_left: ->(v, _l, _r) { v.downcase }
181
+ [left_node],
182
+ [right_node],
183
+ on_right_to_left: ->(v, _l, _r, _n) { v.downcase }
155
184
  )
156
185
 
157
186
  left_object.key1 = nil
@@ -163,16 +192,114 @@ describe TwoWayMapper::Rule do
163
192
 
164
193
  it 'should pass left object and right object' do
165
194
  rule = described_class.new(
166
- left_node,
167
- right_node,
168
- on_right_to_left: ->(_v, l, r) { "#{l.object_id}-#{r.object_id}" }
195
+ [left_node],
196
+ [right_node],
197
+ on_right_to_left: ->(_v, l, r, n) { "#{l.object_id}-#{r.object_id}-#{n.selector}" }
169
198
  )
170
199
 
171
200
  left_object.key1 = nil
172
201
  right_object = { Kk: { Key1: 'VALUE1' } }
173
202
  rule.from_right_to_left(left_object, right_object)
174
203
 
175
- expect(left_object.key1).to eql "#{left_object.object_id}-#{right_object.object_id}"
204
+ expect(left_object.key1).to eql "#{left_object.object_id}-#{right_object.object_id}-#{right_node.selector}"
205
+ end
206
+ end
207
+ end
208
+
209
+ context 'with multiple nodes on the left side' do
210
+ let(:another_left_node) { TwoWayMapper::Node::Object.new('key2') }
211
+ let(:left_nodes) { [left_node, another_left_node] }
212
+
213
+ describe '#from_left_to_right' do
214
+ it 'should get first non nil value from left nodes and write to the right node' do
215
+ left_object.key2 = 'value2'
216
+ right_object = {}
217
+ rule.from_left_to_right(left_object, right_object)
218
+
219
+ expect(right_object).to eql Kk: { Key1: 'value2' }
220
+ end
221
+ end
222
+
223
+ describe '#from_right_to_left' do
224
+ it 'should read from right node and write to all left nodes' do
225
+ left_object.key1 = nil
226
+ left_object.key2 = nil
227
+ right_object = { Kk: { Key1: 'value1' } }
228
+ rule.from_right_to_left(left_object, right_object)
229
+
230
+ expect(left_object.key1).to eql 'value1'
231
+ expect(left_object.key2).to eql 'value1'
232
+ end
233
+ end
234
+ end
235
+
236
+ context 'with multiple nodes on the right side' do
237
+ let(:another_right_node) { TwoWayMapper::Node::Hash.new('Kk1.Key') }
238
+ let(:right_nodes) { [right_node, another_right_node] }
239
+
240
+ describe '#from_left_to_right' do
241
+ it 'should read from the left node and write to all right nodes' do
242
+ left_object.key1 = 'value'
243
+ right_object = {}
244
+ rule.from_left_to_right(left_object, right_object)
245
+
246
+ expect(right_object).to eql Kk: { Key1: 'value' }, Kk1: { Key: 'value' }
247
+ end
248
+ end
249
+
250
+ describe '#from_right_to_left' do
251
+ it 'should get first non nil value from right nodes and write to the left node' do
252
+ left_object.key1 = nil
253
+ right_object = { Kk1: { Key: 'value' } }
254
+ rule.from_right_to_left(left_object, right_object)
255
+
256
+ expect(left_object.key1).to eql 'value'
257
+ end
258
+ end
259
+ end
260
+
261
+ context 'with multiple nodes on both sides' do
262
+ let(:another_left_node) { TwoWayMapper::Node::Object.new('key2') }
263
+ let(:left_nodes) { [left_node, another_left_node] }
264
+ let(:another_right_node) { TwoWayMapper::Node::Hash.new('Kk1.Key') }
265
+ let(:right_nodes) { [right_node, another_right_node] }
266
+
267
+ describe '#from_left_to_right' do
268
+ it 'should get first non nil value from left nodes and write to all right nodes' do
269
+ left_object.key2 = 'value'
270
+ right_object = {}
271
+ rule.from_left_to_right(left_object, right_object)
272
+
273
+ expect(right_object).to eql Kk: { Key1: 'value' }, Kk1: { Key: 'value' }
274
+ end
275
+ end
276
+
277
+ describe '#from_right_to_left' do
278
+ it 'should get first non nil value from right nodes and write to all left nodes' do
279
+ left_object.key1 = nil
280
+ left_object.key2 = nil
281
+ right_object = { Kk1: { Key: 'value' } }
282
+ rule.from_right_to_left(left_object, right_object)
283
+
284
+ expect(left_object.key1).to eql 'value'
285
+ end
286
+ end
287
+ end
288
+
289
+ describe 'multiple nodes with callback' do
290
+ let(:another_left_node) { TwoWayMapper::Node::Object.new('key2') }
291
+ let(:left_nodes) { [left_node, another_left_node] }
292
+ # return false on empty string to emulate a situation when we need to skip blank values
293
+ let(:options) { { on_left_to_right: proc { |v| v != '' && v } } }
294
+
295
+ describe '#from_left_to_right' do
296
+ it 'should involve the callback' do
297
+ left_object.key1 = ''
298
+ left_object.key2 = 'value'
299
+ right_object = {}
300
+ rule.from_left_to_right(left_object, right_object)
301
+
302
+ expect(right_object).to eql Kk: { Key1: 'value' }
176
303
  end
177
304
  end
178
305
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: two-way-mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Masliuchenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-31 00:00:00.000000000 Z
11
+ date: 2020-01-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport