two-way-mapper 0.1.2 → 0.2.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 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