two-way-mapper 0.0.1 → 0.3.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.
@@ -1,11 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TwoWayMapper
2
4
  module Tools
3
5
  class << self
4
6
  def first_item_from_hash!(hash)
5
7
  raise ArgumentError unless hash.is_a?(Hash)
6
- raise ArgumentError unless first = hash.first
8
+ raise ArgumentError unless (first = hash.first)
7
9
 
8
- hash.delete first[0]
10
+ hash.delete(first[0])
9
11
  first
10
12
  end
11
13
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TwoWayMapper
2
- VERSION = "0.0.1"
4
+ VERSION = '0.3.0'
3
5
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe TwoWayMapper::Map do
2
4
  describe '#register' do
3
- let(:map) { TwoWayMapper::Map.new }
5
+ let(:map) { described_class.new }
4
6
 
5
7
  it 'should register new mapping' do
6
8
  map.register :import
@@ -1,65 +1,125 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe TwoWayMapper::Mapping do
2
- [:left, :right].each do |method|
3
- describe "##{method}" do
4
- let(:mapping) { TwoWayMapper::Mapping.new }
4
+ let(:mapping) { described_class.new }
5
5
 
6
- it "should set #{method} with options" do
7
- mapping.send method, :object, opt1: ''
6
+ described_class::DIRECTIONS.each do |direction|
7
+ describe "##{direction}" do
8
+ it "should set #{direction} with options" do
9
+ mapping.send direction, :object, opt1: ''
8
10
 
9
- expect(mapping.send("#{method}_class")).to eql TwoWayMapper::Node::Object
10
- expect(mapping.send("#{method}_options")).to include opt1: ''
11
+ expect(mapping.send("#{direction}_class")).to eql TwoWayMapper::Node::Object
12
+ expect(mapping.send("#{direction}_options")).to include opt1: ''
13
+ end
14
+ end
15
+ end
16
+
17
+ context 'selectors' do
18
+ before do
19
+ mapping.left :object
20
+ mapping.right :object
21
+ end
22
+
23
+ before do
24
+ mapping.rule 'firstname', 'FirstName'
25
+ mapping.rule 'fullname', 'FullName', from_right_to_left_only: true
26
+ mapping.rule 'lastname', 'LastName'
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']
31
+ end
32
+
33
+ describe '#left_selectors' do
34
+ it 'should get left selectors' do
35
+ expect(mapping.left_selectors).to eql(
36
+ %w(firstname fullname lastname fullname1 field1 field2 field3 field4 field5)
37
+ )
38
+ end
39
+
40
+ it 'should include only mappable selectors if such option is passed' do
41
+ expect(mapping.left_selectors(mappable: true)).to eql(
42
+ %w(firstname fullname lastname field1 field2 field3 field4 field5)
43
+ )
44
+ end
45
+ end
46
+
47
+ describe '#right_selectors' do
48
+ it 'should get right selectors' do
49
+ expect(mapping.right_selectors).to eql(
50
+ %w(FirstName FullName LastName FullName1 Field1 Field2 Field3 Field4 Field5)
51
+ )
52
+ end
53
+
54
+ it 'should include only mappable selectors if such option is passed' do
55
+ expect(mapping.right_selectors(mappable: true)).to eql(
56
+ %w(FirstName LastName FullName1 Field1 Field2 Field3 Field4 Field5)
57
+ )
11
58
  end
12
59
  end
13
60
  end
14
61
 
15
62
  describe '#rule' do
16
- let(:mapping) { TwoWayMapper::Mapping.new }
17
63
  before :each do
18
64
  mapping.left :object
19
65
  mapping.right :hash
20
66
  end
21
67
 
22
68
  context 'left and right validation' do
23
- let(:mapping_without_both) { TwoWayMapper::Mapping.new }
24
- let(:mapping_without_left) { TwoWayMapper::Mapping.new }
25
- let(:mapping_without_right) { TwoWayMapper::Mapping.new }
69
+ let(:mapping_without_both) { described_class.new }
70
+ let(:mapping_without_left) { described_class.new }
71
+ let(:mapping_without_right) { described_class.new }
72
+
26
73
  before :each do
27
74
  mapping_without_left.right :hash
28
75
  mapping_without_right.left :hash
29
76
  end
30
77
 
31
78
  it 'should raise error when no left or right nodes' do
32
- expect{mapping_without_left.rule 'key', 'key'}.to raise_error
33
- expect{mapping_without_right.rule 'key', 'key'}.to raise_error
34
- expect{mapping_without_both.rule 'key', 'key'}.to raise_error
79
+ expect { mapping_without_left.rule 'key', 'key' }.to raise_error StandardError
80
+ expect { mapping_without_right.rule 'key', 'key' }.to raise_error StandardError
81
+ expect { mapping_without_both.rule 'key', 'key' }.to raise_error StandardError
35
82
  end
36
83
  end
37
84
 
38
85
  it 'should add item to rules hash' do
39
- expect{mapping.rule 'key1', 'Key1'}.to change{mapping.rules.count}.from(0).to(1)
86
+ expect { mapping.rule 'key1', 'Key1' }.to change { mapping.rules_list.count }.from(0).to(1)
40
87
 
41
- rule = mapping.rules.first
88
+ rule = mapping.rules_list.first
42
89
  expect(rule).to be_instance_of TwoWayMapper::Rule
43
- expect(rule.left.selector).to eql 'key1'
44
- 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_list.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'
45
105
  end
46
106
 
47
107
  it 'should allow to pass hash' do
48
- expect{mapping.rule 'key1' => { opt1: 'val' }}.to raise_error
108
+ expect { mapping.rule 'key1' => { opt1: 'val' } }.to raise_error StandardError
49
109
 
50
110
  mapping.rule 'key1' => { opt1: 'val' }, 'Key2' => {}
51
- rule = mapping.rules.first
111
+ rule = mapping.rules_list.first
52
112
 
53
- expect(rule.left.selector).to eql 'key1'
54
- 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']
55
115
  end
56
116
 
57
117
  it 'should allow to pass left abd right options ' do
58
118
  mapping.rule 'key1', 'Key2', left: { opt1: 'val' }, right: { opt2: 'val' }
59
- rule = mapping.rules.first
119
+ rule = mapping.rules_list.first
60
120
 
61
- expect(rule.left.options).to include opt1: 'val'
62
- 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'
63
123
  end
64
124
 
65
125
  it 'should work with options copy' do
@@ -71,18 +131,20 @@ describe TwoWayMapper::Mapping do
71
131
  end
72
132
 
73
133
  context 'convertion methods' do
74
- let(:mapping) { TwoWayMapper::Mapping.new }
75
134
  let(:rule1) { double from_left_to_right: nil, from_right_to_left: nil }
76
135
  let(:rule2) { double from_left_to_right: nil, from_right_to_left: nil }
77
- let(:left_obj) { double() }
78
- let(:right_obj) { double() }
136
+ let(:left_obj) { double }
137
+ let(:right_obj) { double }
138
+
79
139
  before :each do
80
140
  mapping.left :object
81
141
  mapping.right :hash
82
- allow(mapping).to receive(:rules).and_return [rule1, rule2]
142
+ allow(mapping).to receive(:rules_list).and_return [rule1, rule2]
83
143
  end
84
144
 
85
- [:from_left_to_right, :from_right_to_left].each do |method|
145
+ [described_class::DIRECTIONS, described_class::DIRECTIONS.reverse].each do |from, to|
146
+ method = "from_#{from}_to_#{to}"
147
+
86
148
  describe "##{method}" do
87
149
  it 'should proxy to all rules' do
88
150
  expect(rule1).to receive(method).with left_obj, right_obj
@@ -93,4 +155,30 @@ describe TwoWayMapper::Mapping do
93
155
  end
94
156
  end
95
157
  end
158
+
159
+ describe '#rules' do
160
+ let(:hash) do
161
+ {
162
+ key1: :Key1,
163
+ key2: :Key2
164
+ }
165
+ end
166
+
167
+ before :each do
168
+ mapping.left :object
169
+ mapping.right :hash
170
+ end
171
+
172
+ it 'should allow to define multiple rules' do
173
+ expect { mapping.rules(hash) }.to change { mapping.rules_list.count }.from(0).to(2)
174
+
175
+ hash.each_with_index do |(left, right), index|
176
+ rule = mapping.rules_list[index]
177
+
178
+ expect(rule).to be_instance_of TwoWayMapper::Rule
179
+ expect(rule.left_nodes.map(&:selector)).to eql [left]
180
+ expect(rule.right_nodes.map(&:selector)).to eql [right]
181
+ end
182
+ end
183
+ end
96
184
  end
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe TwoWayMapper::Node::ActiveRecord do
2
- let(:node) { TwoWayMapper::Node::ActiveRecord.new 'user.email' }
4
+ let(:node) { described_class.new 'user.email' }
3
5
 
4
6
  describe '#write' do
5
7
  it 'should try to build before write' do
6
8
  user = double(email: '')
7
- obj = double()
9
+ obj = double
10
+
8
11
  allow(obj).to receive :build_user do
9
12
  allow(obj).to receive(:user).and_return user
10
13
  end
@@ -18,6 +21,7 @@ describe TwoWayMapper::Node::ActiveRecord do
18
21
  it 'should try to build even if respond_to but obj itself is nil' do
19
22
  user = double(email: '')
20
23
  obj = double(user: nil)
24
+
21
25
  allow(obj).to receive :build_user do
22
26
  allow(obj).to receive(:user).and_return user
23
27
  end
@@ -1,19 +1,24 @@
1
- describe TwoWayMapper::Node::Abstract do
1
+ # frozen_string_literal: true
2
+
3
+ describe TwoWayMapper::Node::Base do
2
4
  describe '#keys' do
3
- subject { TwoWayMapper::Node::Abstract.new 'key1.key11.key111' }
5
+ subject { described_class.new('key1.key11.key111') }
4
6
 
5
- its(:keys) { should eql [:key1, :key11, :key111] }
7
+ its(:keys) { should eql %i[key1 key11 key111] }
6
8
  end
7
9
 
8
10
  describe 'writable?' do
9
11
  it 'should be truthy if write_if options not set' do
10
- node = TwoWayMapper::Node::Abstract.new 'key1.key11.key111'
12
+ node = described_class.new('key1.key11.key111')
11
13
 
12
14
  expect(node).to be_writable 'current', 'new'
13
15
  end
14
16
 
15
17
  it 'should be truthy if write_if option set' do
16
- node = TwoWayMapper::Node::Abstract.new 'key1.key11.key111', write_if: ->(c, n) { c == 'current' || n == 'new' }
18
+ node = described_class.new(
19
+ 'key1.key11.key111',
20
+ write_if: ->(c, n) { c == 'current' || n == 'new' }
21
+ )
17
22
 
18
23
  expect(node).to be_writable 'current', 'new1'
19
24
  expect(node).to be_writable 'current1', 'new'
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe TwoWayMapper::Node::Hash do
2
4
  context 'normal keys' do
3
- let(:node) { TwoWayMapper::Node::Hash.new 'key1.key11.key111' }
5
+ let(:node) { described_class.new('key1.key11.key111') }
4
6
 
5
7
  describe '#read' do
6
8
  it 'should return nil when path is not avaiable' do
@@ -25,7 +27,9 @@ describe TwoWayMapper::Node::Hash do
25
27
  end
26
28
 
27
29
  context 'string keys' do
28
- let(:node) { TwoWayMapper::Node::Hash.new 'key1.key11.key111', stringify_keys: true }
30
+ let(:node) do
31
+ described_class.new('key1.key11.key111', stringify_keys: true)
32
+ end
29
33
 
30
34
  describe '#read' do
31
35
  it 'should return nil when path is not avaiable' do
@@ -50,7 +54,7 @@ describe TwoWayMapper::Node::Hash do
50
54
  end
51
55
 
52
56
  context 'write_if option' do
53
- let(:node) { TwoWayMapper::Node::Hash.new 'key1.key11', write_if: ->(c, n) { c.empty? || n == 'value1' } }
57
+ let(:node) { described_class.new 'key1.key11', write_if: ->(c, n) { c.empty? || n == 'value1' } }
54
58
  let(:writable_obj1) { { key1: { key11: '' } } }
55
59
  let(:writable_obj2) { { key1: { key11: 'smth' } } }
56
60
  let(:not_writable_obj) { { key1: { key11: 'smth' } } }
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe TwoWayMapper::Node::Object do
2
- let(:node) { TwoWayMapper::Node::Object.new 'key1.key11.key111' }
4
+ let(:node) { described_class.new 'key1.key11.key111' }
3
5
 
4
6
  describe '#read' do
5
7
  it 'should return nil when path is not avaiable' do
@@ -25,7 +27,7 @@ describe TwoWayMapper::Node::Object do
25
27
  end
26
28
 
27
29
  context 'write_if option' do
28
- let(:node) { TwoWayMapper::Node::Object.new 'key', write_if: ->(c, n) { c.empty? || n == 'value1'} }
30
+ let(:node) { described_class.new 'key', write_if: ->(c, n) { c.empty? || n == 'value1'} }
29
31
  let(:writable_obj1) { OpenStruct.new key: '' }
30
32
  let(:writable_obj2) { OpenStruct.new key: 'smth' }
31
33
  let(:not_writable_obj) { OpenStruct.new key: 'smth' }
@@ -1,127 +1,305 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe TwoWayMapper::Rule do
2
- context 'transformation methods' do
3
- let(:left_node) { TwoWayMapper::Node::Object.new 'key1' }
4
- let(:right_node) { TwoWayMapper::Node::Hash.new 'Kk.Key1' }
5
- let(:left_object) { OpenStruct.new }
6
- let(:map) { { 'value' => 'VALUE' } }
4
+ let(:left_node) { TwoWayMapper::Node::Object.new('key1') }
5
+ let(:left_nodes) { [left_node] }
6
+ let(:right_node) { TwoWayMapper::Node::Hash.new('Kk.Key1') }
7
+ let(:right_nodes) { [right_node] }
8
+ let(:left_object) { OpenStruct.new }
9
+ let(:map) { { 'value' => 'VALUE' } }
10
+ let(:options) { {} }
11
+ let(:rule) { described_class.new(left_nodes, right_nodes, options) }
7
12
 
8
- context 'without options' do
9
- let(:rule) { TwoWayMapper::Rule.new left_node, right_node }
13
+ context 'without options' do
14
+ let(:options) { {} }
10
15
 
11
- describe '#from_left_to_right' do
12
- it 'should read from left node and write to right node' do
13
- left_object.key1 = 'value1'
14
- right_object = {}
15
- rule.from_left_to_right left_object, right_object
16
+ describe '#from_left_to_right' do
17
+ it 'should read from the left node and write to the right node' do
18
+ left_object.key1 = 'value1'
19
+ right_object = {}
20
+ rule.from_left_to_right(left_object, right_object)
16
21
 
17
- expect(right_object).to eql Kk: { Key1: 'value1' }
18
- end
22
+ expect(right_object).to eql Kk: { Key1: 'value1' }
19
23
  end
24
+ end
20
25
 
21
- describe '#from_right_to_left' do
22
- it 'should read from right node and write to left node' do
23
- left_object.key1 = nil
24
- right_object = { Kk: { Key1: 'value1' } }
25
- rule.from_right_to_left left_object, right_object
26
+ describe '#from_right_to_left' do
27
+ it 'should read from the right node and write to the left node' do
28
+ left_object.key1 = nil
29
+ right_object = { Kk: { Key1: 'value1' } }
30
+ rule.from_right_to_left(left_object, right_object)
26
31
 
27
- expect(left_object.key1).to eql 'value1'
28
- end
32
+ expect(left_object.key1).to eql 'value1'
29
33
  end
30
34
  end
35
+ end
31
36
 
32
- context 'with map option' do
33
- let(:rule) { TwoWayMapper::Rule.new left_node, right_node, map: map, default: 'not found' }
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 } }
34
40
 
35
- describe '#from_left_to_right' do
36
- it 'should read from left node and write to right node' do
37
- left_object.key1 = 'value'
38
- right_object = {}
39
- rule.from_left_to_right left_object, right_object
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)
40
45
 
41
- expect(right_object).to eql Kk: { Key1: 'VALUE' }
42
- end
46
+ expect(right_object).to eql({})
43
47
  end
48
+ end
49
+
50
+ describe '#from_right_to_left' do
51
+ let(:options) { { from_left_to_right_only: true } }
44
52
 
45
- describe '#from_right_to_left' do
46
- it 'should read from right node and write to left node' do
47
- left_object.key1 = nil
48
- right_object = { Kk: { Key1: 'VALUE' } }
49
- rule.from_right_to_left left_object, right_object
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)
50
57
 
51
- expect(left_object.key1).to eql 'value'
52
- end
58
+ expect(left_object.key1).to be_nil
53
59
  end
54
60
  end
61
+ end
55
62
 
56
- context 'with default option' do
57
- describe '#from_left_to_right' do
58
- it 'should return default value if not found' do
59
- rule = TwoWayMapper::Rule.new left_node, right_node, map: map, default: 'not found'
63
+ context 'with map option' do
64
+ let(:options) { { map: map } }
60
65
 
61
- left_object.key1 = 'value1'
62
- right_object = {}
63
- rule.from_left_to_right left_object, right_object
66
+ describe '#from_left_to_right' do
67
+ it 'should read from left node and write to right node' do
68
+ left_object.key1 = 'value'
69
+ right_object = {}
70
+ rule.from_left_to_right(left_object, right_object)
64
71
 
65
- expect(right_object).to eql Kk: { Key1: 'not found' }
66
- end
72
+ expect(right_object).to eql Kk: { Key1: 'VALUE' }
73
+ end
74
+ end
67
75
 
68
- it 'should return use default_left if present and value not found' do
69
- rule = TwoWayMapper::Rule.new left_node, right_node, map: map, default: 'not found', default_left: 'not found on left', default_right: 'not found on right'
76
+ describe '#from_right_to_left' do
77
+ it 'should read from right node and write to left node' do
78
+ left_object.key1 = nil
79
+ right_object = { Kk: { Key1: 'VALUE' } }
80
+ rule.from_right_to_left(left_object, right_object)
70
81
 
71
- left_object.key1 = 'value1'
72
- right_object = {}
73
- rule.from_left_to_right left_object, right_object
82
+ expect(left_object.key1).to eql 'value'
83
+ end
84
+ end
85
+ end
86
+
87
+ context 'with map and default option' do
88
+ describe '#from_left_to_right' do
89
+ it 'should return default value if not found' do
90
+ rule = described_class.new [left_node], [right_node], map: map, default: 'not found'
91
+
92
+ left_object.key1 = 'value1'
93
+ right_object = {}
94
+ rule.from_left_to_right(left_object, right_object)
74
95
 
75
- expect(right_object).to eql Kk: { Key1: 'not found on left' }
76
- end
96
+ expect(right_object).to eql Kk: { Key1: 'not found' }
77
97
  end
78
98
 
79
- describe '#from_right_to_left' do
80
- it 'should return default value if not found' do
81
- rule = TwoWayMapper::Rule.new left_node, right_node, map: map, default: 'not found'
99
+ it 'should return use default_left if present and value not found' do
100
+ rule = described_class.new(
101
+ [left_node],
102
+ [right_node],
103
+ map: map,
104
+ default: 'not found',
105
+ default_left: 'not found on left',
106
+ default_right: 'not found on right'
107
+ )
82
108
 
83
- left_object.key1 = nil
84
- right_object = { Kk: { Key1: 'VALUE1' } }
85
- rule.from_right_to_left left_object, right_object
109
+ left_object.key1 = 'value1'
110
+ right_object = {}
111
+ rule.from_left_to_right(left_object, right_object)
86
112
 
87
- expect(left_object.key1).to eql 'not found'
88
- end
113
+ expect(right_object).to eql Kk: { Key1: 'not found on left' }
114
+ end
115
+ end
89
116
 
90
- it 'should return use default_right if present and value not found' do
91
- rule = TwoWayMapper::Rule.new left_node, right_node, map: map, default: 'not found', default_left: 'not found on left', default_right: 'not found on right'
117
+ describe '#from_right_to_left' do
118
+ it 'should return default value if not found' do
119
+ rule = described_class.new [left_node], [right_node], map: map, default: 'not found'
92
120
 
93
- left_object.key1 = nil
94
- right_object = { Kk: { Key1: 'VALUE1' } }
95
- rule.from_right_to_left left_object, right_object
121
+ left_object.key1 = nil
122
+ right_object = { Kk: { Key1: 'VALUE1' } }
123
+ rule.from_right_to_left(left_object, right_object)
96
124
 
97
- expect(left_object.key1).to eql 'not found on right'
98
- end
125
+ expect(left_object.key1).to eql 'not found'
126
+ end
127
+
128
+ it 'should return use default_right if present and value not found' do
129
+ rule = described_class.new(
130
+ [left_node],
131
+ [right_node],
132
+ map: map,
133
+ default: 'not found',
134
+ default_left: 'not found on left',
135
+ default_right: 'not found on right'
136
+ )
137
+
138
+ left_object.key1 = nil
139
+ right_object = { Kk: { Key1: 'VALUE1' } }
140
+ rule.from_right_to_left(left_object, right_object)
141
+
142
+ expect(left_object.key1).to eql 'not found on right'
143
+ end
144
+ end
145
+ end
146
+
147
+ context 'with callback option' do
148
+ describe '#on_left_to_right' do
149
+ it 'should transform value if such options passed' do
150
+ rule = described_class.new(
151
+ [left_node],
152
+ [right_node],
153
+ on_left_to_right: ->(v, _l, _r, _n) { v.upcase }
154
+ )
155
+
156
+ left_object.key1 = 'value1'
157
+ right_object = {}
158
+ rule.from_left_to_right(left_object, right_object)
159
+
160
+ expect(right_object).to eql Kk: { Key1: 'VALUE1' }
161
+ end
162
+
163
+ it 'should pass left object and right object' do
164
+ rule = described_class.new(
165
+ [left_node],
166
+ [right_node],
167
+ on_left_to_right: ->(_v, l, r, n) { "#{l.object_id}-#{r.object_id}-#{n.selector}" }
168
+ )
169
+
170
+ left_object.key1 = 'value1'
171
+ right_object = {}
172
+ rule.from_left_to_right(left_object, right_object)
173
+
174
+ expect(right_object).to eql Kk: { Key1: "#{left_object.object_id}-#{right_object.object_id}-#{left_node.selector}" }
99
175
  end
100
176
  end
101
177
 
102
- describe 'with callback option' do
103
- describe 'on_left_to_right' do
104
- it 'should transform value if such options passed' do
105
- rule = TwoWayMapper::Rule.new left_node, right_node, on_left_to_right: ->(v) { v.upcase }
178
+ describe '#on_right_to_left' do
179
+ it 'should transform value if such options passed' do
180
+ rule = described_class.new(
181
+ [left_node],
182
+ [right_node],
183
+ on_right_to_left: ->(v, _l, _r, _n) { v.downcase }
184
+ )
185
+
186
+ left_object.key1 = nil
187
+ right_object = { Kk: { Key1: 'VALUE1' } }
188
+ rule.from_right_to_left(left_object, right_object)
189
+
190
+ expect(left_object.key1).to eql 'value1'
191
+ end
192
+
193
+ it 'should pass left object and right object' do
194
+ rule = described_class.new(
195
+ [left_node],
196
+ [right_node],
197
+ on_right_to_left: ->(_v, l, r, n) { "#{l.object_id}-#{r.object_id}-#{n.selector}" }
198
+ )
106
199
 
107
- left_object.key1 = 'value1'
108
- right_object = {}
109
- rule.from_left_to_right left_object, right_object
200
+ left_object.key1 = nil
201
+ right_object = { Kk: { Key1: 'VALUE1' } }
202
+ rule.from_right_to_left(left_object, right_object)
110
203
 
111
- expect(right_object).to eql Kk: { Key1: 'VALUE1' }
112
- end
204
+ expect(left_object.key1).to eql "#{left_object.object_id}-#{right_object.object_id}-#{right_node.selector}"
113
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
114
288
 
115
- describe 'on_right_to_left' do
116
- it 'should transform value if such options passed' do
117
- rule = TwoWayMapper::Rule.new left_node, right_node, on_right_to_left: ->(v) { v.downcase }
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 } } }
118
294
 
119
- left_object.key1 = nil
120
- right_object = { Kk: { Key1: 'VALUE1' } }
121
- rule.from_right_to_left left_object, right_object
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)
122
301
 
123
- expect(left_object.key1).to eql 'value1'
124
- end
302
+ expect(right_object).to eql Kk: { Key1: 'value' }
125
303
  end
126
304
  end
127
305
  end