two-way-mapper 0.1.1 → 0.1.2
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 +4 -4
- data/lib/two_way_mapper/mapping.rb +23 -21
- data/lib/two_way_mapper/rule.rb +34 -15
- data/lib/two_way_mapper/version.rb +1 -1
- data/spec/mapping_spec.rb +32 -24
- data/spec/rule_spec.rb +149 -122
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 524242de8d10dfa77f277974cd5e1670dcc68b0345b371d847a687cce8998c59
|
4
|
+
data.tar.gz: e4b0d1784f91ee600a316c161a8bb97b3bccabd6c2d97a1296dbb82596a93c08
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce3eb28e41536134ac6c221b41c7bc0c0fecd01092a8f2f26c7076d4d7249a93c26c1e8103a9644a8418e7d93e8570fd273a4eb96023b52d77f92138cfa81be8
|
7
|
+
data.tar.gz: '09b3e9f0ccf3cc4e9f5ca8d3668b2cd7b253d7c9190f06daceb3afb3f3c0f4d58329d0a12ec394e6675fdd95ec79d1a28c26242037d24b0387193db6957d2c2c'
|
@@ -2,31 +2,34 @@
|
|
2
2
|
|
3
3
|
module TwoWayMapper
|
4
4
|
class Mapping
|
5
|
+
DIRECTIONS = [:left, :right].freeze
|
6
|
+
|
5
7
|
attr_reader :rules, :left_class, :left_options, :right_class, :right_options
|
6
8
|
|
7
9
|
def initialize
|
8
10
|
@rules = []
|
9
11
|
end
|
10
12
|
|
11
|
-
[
|
13
|
+
[DIRECTIONS, DIRECTIONS.reverse].each do |from, to|
|
12
14
|
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
13
|
-
def #{
|
14
|
-
@#{
|
15
|
-
@#{
|
15
|
+
def #{from}(plugin, options = {})
|
16
|
+
@#{from}_class = node_class(plugin)
|
17
|
+
@#{from}_options = options
|
16
18
|
end
|
17
|
-
CODE
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
25
|
+
end
|
22
26
|
end
|
23
|
-
CODE
|
24
|
-
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
28
|
+
def from_#{from}_to_#{to}(left_obj, right_obj)
|
29
|
+
rules.each { |rule| rule.from_#{from}_to_#{to}(left_obj, right_obj) }
|
30
|
+
#{to}_obj
|
31
|
+
end
|
32
|
+
CODE
|
30
33
|
end
|
31
34
|
|
32
35
|
def rule(left_selector, right_selector = {}, options = {})
|
@@ -53,13 +56,12 @@ module TwoWayMapper
|
|
53
56
|
@rules << Rule.new(left, right, opt)
|
54
57
|
end
|
55
58
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
CODE
|
59
|
+
private
|
60
|
+
|
61
|
+
def node_class(plugin)
|
62
|
+
TwoWayMapper::Node.const_get(plugin.to_s.camelize)
|
63
|
+
rescue NameError
|
64
|
+
raise NameError, 'Cannot find node'
|
63
65
|
end
|
64
66
|
end
|
65
67
|
end
|
data/lib/two_way_mapper/rule.rb
CHANGED
@@ -10,29 +10,48 @@ module TwoWayMapper
|
|
10
10
|
@options = opt
|
11
11
|
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
13
|
+
def from_left_to_right(left_obj, right_obj)
|
14
|
+
return right_obj if from_right_to_left_only?
|
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)
|
22
|
+
|
23
|
+
right_obj
|
24
|
+
end
|
25
|
+
|
26
|
+
def from_right_to_left(left_obj, right_obj)
|
27
|
+
return left_obj if from_left_to_right_only?
|
28
|
+
|
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)
|
35
|
+
|
36
|
+
left_obj
|
37
|
+
end
|
38
|
+
|
39
|
+
def from_right_to_left_only?
|
40
|
+
@options[:from_right_to_left_only]
|
41
|
+
end
|
42
|
+
|
43
|
+
def from_left_to_right_only?
|
44
|
+
@options[:from_left_to_right_only]
|
26
45
|
end
|
27
46
|
|
28
47
|
private
|
29
48
|
|
30
49
|
def map_value(value, left_to_right = true)
|
31
50
|
map = @options[:map]
|
32
|
-
if map
|
51
|
+
if map.is_a?(Hash)
|
33
52
|
map = map.invert unless left_to_right
|
34
53
|
default_key = "default_#{left_to_right ? 'left' : 'right'}".to_sym
|
35
|
-
|
54
|
+
map[value] || @options[default_key] || @options[:default]
|
36
55
|
else
|
37
56
|
value
|
38
57
|
end
|
data/spec/mapping_spec.rb
CHANGED
@@ -3,39 +3,45 @@
|
|
3
3
|
describe TwoWayMapper::Mapping do
|
4
4
|
let(:mapping) { described_class.new }
|
5
5
|
|
6
|
-
|
7
|
-
describe "##{
|
8
|
-
it "should set #{
|
9
|
-
mapping.send
|
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: ''
|
10
10
|
|
11
|
-
expect(mapping.send("#{
|
12
|
-
expect(mapping.send("#{
|
11
|
+
expect(mapping.send("#{direction}_class")).to eql TwoWayMapper::Node::Object
|
12
|
+
expect(mapping.send("#{direction}_options")).to include opt1: ''
|
13
13
|
end
|
14
14
|
end
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
context 'selectors' do
|
18
|
+
before do
|
19
|
+
mapping.left :object
|
20
|
+
mapping.right :object
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
mapping.rule 'firstname', 'FirstName'
|
23
|
+
mapping.rule 'fullname', 'FullName', from_right_to_left_only: true
|
24
|
+
mapping.rule 'lastname', 'LastName'
|
25
|
+
mapping.rule 'fullname1', 'FullName1', from_left_to_right_only: true
|
26
|
+
end
|
24
27
|
|
25
|
-
|
26
|
-
|
28
|
+
describe '#left_selectors' do
|
29
|
+
it 'should get left selectors' do
|
30
|
+
expect(mapping.left_selectors).to eql %w(firstname fullname lastname fullname1)
|
31
|
+
end
|
27
32
|
|
28
|
-
|
29
|
-
|
30
|
-
end
|
33
|
+
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)
|
31
35
|
end
|
36
|
+
end
|
32
37
|
|
33
|
-
|
34
|
-
|
38
|
+
describe '#right_selectors' do
|
39
|
+
it 'should get right selectors' do
|
40
|
+
expect(mapping.right_selectors).to eql %w(FirstName FullName LastName FullName1)
|
41
|
+
end
|
35
42
|
|
36
|
-
|
37
|
-
|
38
|
-
end
|
43
|
+
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)
|
39
45
|
end
|
40
46
|
end
|
41
47
|
end
|
@@ -110,7 +116,9 @@ describe TwoWayMapper::Mapping do
|
|
110
116
|
allow(mapping).to receive(:rules).and_return [rule1, rule2]
|
111
117
|
end
|
112
118
|
|
113
|
-
[
|
119
|
+
[described_class::DIRECTIONS, described_class::DIRECTIONS.reverse].each do |from, to|
|
120
|
+
method = "from_#{from}_to_#{to}"
|
121
|
+
|
114
122
|
describe "##{method}" do
|
115
123
|
it 'should proxy to all rules' do
|
116
124
|
expect(rule1).to receive(method).with left_obj, right_obj
|
data/spec/rule_spec.rb
CHANGED
@@ -1,151 +1,178 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
describe TwoWayMapper::Rule do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
4
|
+
let(:left_node) { TwoWayMapper::Node::Object.new('key1') }
|
5
|
+
let(:right_node) { TwoWayMapper::Node::Hash.new('Kk.Key1') }
|
6
|
+
let(:left_object) { OpenStruct.new }
|
7
|
+
let(:map) { { 'value' => 'VALUE' } }
|
8
|
+
let(:rule) { described_class.new(left_node, right_node, options) }
|
9
|
+
|
10
|
+
context 'without options' do
|
11
|
+
let(:options) { {} }
|
12
|
+
|
13
|
+
describe '#from_left_to_right' do
|
14
|
+
it 'should read from left node and write to right node' do
|
15
|
+
left_object.key1 = 'value1'
|
16
|
+
right_object = {}
|
17
|
+
rule.from_left_to_right(left_object, right_object)
|
18
|
+
|
19
|
+
expect(right_object).to eql Kk: { Key1: 'value1' }
|
21
20
|
end
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
describe '#from_right_to_left' do
|
24
|
+
it 'should read from right node and write to left node' do
|
25
|
+
left_object.key1 = nil
|
26
|
+
right_object = { Kk: { Key1: 'value1' } }
|
27
|
+
rule.from_right_to_left(left_object, right_object)
|
28
28
|
|
29
|
-
|
30
|
-
end
|
29
|
+
expect(left_object.key1).to eql 'value1'
|
31
30
|
end
|
32
31
|
end
|
32
|
+
end
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
context 'with map option' do
|
35
|
+
let(:options) { { map: map } }
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
describe '#from_left_to_right' do
|
38
|
+
it 'should read from left node and write to right node' do
|
39
|
+
left_object.key1 = 'value'
|
40
|
+
right_object = {}
|
41
|
+
rule.from_left_to_right(left_object, right_object)
|
42
42
|
|
43
|
-
|
44
|
-
end
|
43
|
+
expect(right_object).to eql Kk: { Key1: 'VALUE' }
|
45
44
|
end
|
45
|
+
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
describe '#from_right_to_left' do
|
48
|
+
it 'should read from right node and write to left node' do
|
49
|
+
left_object.key1 = nil
|
50
|
+
right_object = { Kk: { Key1: 'VALUE' } }
|
51
|
+
rule.from_right_to_left(left_object, right_object)
|
52
52
|
|
53
|
-
|
54
|
-
end
|
53
|
+
expect(left_object.key1).to eql 'value'
|
55
54
|
end
|
56
55
|
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with default option' do
|
59
|
+
describe '#from_left_to_right' do
|
60
|
+
it 'should return default value if not found' do
|
61
|
+
rule = described_class.new left_node, right_node, map: map, default: 'not found'
|
62
|
+
|
63
|
+
left_object.key1 = 'value1'
|
64
|
+
right_object = {}
|
65
|
+
rule.from_left_to_right(left_object, right_object)
|
66
|
+
|
67
|
+
expect(right_object).to eql Kk: { Key1: 'not found' }
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should return use default_left if present and value not found' do
|
71
|
+
rule = described_class.new(
|
72
|
+
left_node,
|
73
|
+
right_node,
|
74
|
+
map: map,
|
75
|
+
default: 'not found',
|
76
|
+
default_left: 'not found on left',
|
77
|
+
default_right: 'not found on right'
|
78
|
+
)
|
79
|
+
|
80
|
+
left_object.key1 = 'value1'
|
81
|
+
right_object = {}
|
82
|
+
rule.from_left_to_right(left_object, right_object)
|
83
|
+
|
84
|
+
expect(right_object).to eql Kk: { Key1: 'not found on left' }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#from_right_to_left' 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 = nil
|
93
|
+
right_object = { Kk: { Key1: 'VALUE1' } }
|
94
|
+
rule.from_right_to_left(left_object, right_object)
|
57
95
|
|
58
|
-
|
59
|
-
describe '#from_left_to_right' do
|
60
|
-
it 'should return default value if not found' do
|
61
|
-
rule = described_class.new left_node, right_node, map: map, default: 'not found'
|
62
|
-
|
63
|
-
left_object.key1 = 'value1'
|
64
|
-
right_object = {}
|
65
|
-
rule.from_left_to_right left_object, right_object
|
66
|
-
|
67
|
-
expect(right_object).to eql Kk: { Key1: 'not found' }
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'should return use default_left if present and value not found' do
|
71
|
-
rule = described_class.new(
|
72
|
-
left_node,
|
73
|
-
right_node,
|
74
|
-
map: map,
|
75
|
-
default: 'not found',
|
76
|
-
default_left: 'not found on left',
|
77
|
-
default_right: 'not found on right'
|
78
|
-
)
|
79
|
-
|
80
|
-
left_object.key1 = 'value1'
|
81
|
-
right_object = {}
|
82
|
-
rule.from_left_to_right left_object, right_object
|
83
|
-
|
84
|
-
expect(right_object).to eql Kk: { Key1: 'not found on left' }
|
85
|
-
end
|
96
|
+
expect(left_object.key1).to eql 'not found'
|
86
97
|
end
|
87
98
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
map: map,
|
104
|
-
default: 'not found',
|
105
|
-
default_left: 'not found on left',
|
106
|
-
default_right: 'not found on right'
|
107
|
-
)
|
108
|
-
|
109
|
-
left_object.key1 = nil
|
110
|
-
right_object = { Kk: { Key1: 'VALUE1' } }
|
111
|
-
rule.from_right_to_left left_object, right_object
|
112
|
-
|
113
|
-
expect(left_object.key1).to eql 'not found on right'
|
114
|
-
end
|
99
|
+
it 'should return use default_right 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
|
+
)
|
108
|
+
|
109
|
+
left_object.key1 = nil
|
110
|
+
right_object = { Kk: { Key1: 'VALUE1' } }
|
111
|
+
rule.from_right_to_left(left_object, right_object)
|
112
|
+
|
113
|
+
expect(left_object.key1).to eql 'not found on right'
|
115
114
|
end
|
116
115
|
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'with callback option' do
|
119
|
+
describe 'on_left_to_right' do
|
120
|
+
it 'should transform value if such options passed' do
|
121
|
+
rule = described_class.new(
|
122
|
+
left_node,
|
123
|
+
right_node,
|
124
|
+
on_left_to_right: ->(v, _l, _r) { v.upcase }
|
125
|
+
)
|
126
|
+
|
127
|
+
left_object.key1 = 'value1'
|
128
|
+
right_object = {}
|
129
|
+
rule.from_left_to_right(left_object, right_object)
|
130
|
+
|
131
|
+
expect(right_object).to eql Kk: { Key1: 'VALUE1' }
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should pass left object and right object' do
|
135
|
+
rule = described_class.new(
|
136
|
+
left_node,
|
137
|
+
right_node,
|
138
|
+
on_left_to_right: ->(_v, l, r) { "#{l.object_id}-#{r.object_id}" }
|
139
|
+
)
|
140
|
+
|
141
|
+
left_object.key1 = 'value1'
|
142
|
+
right_object = {}
|
143
|
+
rule.from_left_to_right(left_object, right_object)
|
144
|
+
|
145
|
+
expect(right_object).to eql Kk: { Key1: "#{left_object.object_id}-#{right_object.object_id}" }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe 'on_right_to_left' do
|
150
|
+
it 'should transform value if such options passed' do
|
151
|
+
rule = described_class.new(
|
152
|
+
left_node,
|
153
|
+
right_node,
|
154
|
+
on_right_to_left: ->(v, _l, _r) { v.downcase }
|
155
|
+
)
|
156
|
+
|
157
|
+
left_object.key1 = nil
|
158
|
+
right_object = { Kk: { Key1: 'VALUE1' } }
|
159
|
+
rule.from_right_to_left(left_object, right_object)
|
117
160
|
|
118
|
-
|
119
|
-
describe 'on_left_to_right' do
|
120
|
-
it 'should transform value if such options passed' do
|
121
|
-
rule = described_class.new(
|
122
|
-
left_node,
|
123
|
-
right_node,
|
124
|
-
on_left_to_right: ->(v) { v.upcase }
|
125
|
-
)
|
126
|
-
|
127
|
-
left_object.key1 = 'value1'
|
128
|
-
right_object = {}
|
129
|
-
rule.from_left_to_right left_object, right_object
|
130
|
-
|
131
|
-
expect(right_object).to eql Kk: { Key1: 'VALUE1' }
|
132
|
-
end
|
161
|
+
expect(left_object.key1).to eql 'value1'
|
133
162
|
end
|
134
163
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
)
|
164
|
+
it 'should pass left object and right object' do
|
165
|
+
rule = described_class.new(
|
166
|
+
left_node,
|
167
|
+
right_node,
|
168
|
+
on_right_to_left: ->(_v, l, r) { "#{l.object_id}-#{r.object_id}" }
|
169
|
+
)
|
142
170
|
|
143
|
-
|
144
|
-
|
145
|
-
|
171
|
+
left_object.key1 = nil
|
172
|
+
right_object = { Kk: { Key1: 'VALUE1' } }
|
173
|
+
rule.from_right_to_left(left_object, right_object)
|
146
174
|
|
147
|
-
|
148
|
-
end
|
175
|
+
expect(left_object.key1).to eql "#{left_object.object_id}-#{right_object.object_id}"
|
149
176
|
end
|
150
177
|
end
|
151
178
|
end
|