light_mapper 1.0.5 → 1.0.6

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: 90664595ffebf1ec50dc86f05c193a044f6deee8bafe3091d18f3aed23d3ec32
4
- data.tar.gz: e4cbe5fa16216ca94cf9c77b0fb2c96fbfc02978acf805b06984dcaedebb9a26
3
+ metadata.gz: a800edac754047d5a624828133bcc37a9c79e9249bf24cb3ccd4442c7b294acc
4
+ data.tar.gz: e4381557b9e15af95471a77c7e201a08acf59a634ce8d8cebff0e7200f582f0e
5
5
  SHA512:
6
- metadata.gz: 4c91780ca138072138e84d7fadc7b8d552009107f1a40c7c96cef076181120a141182894e6d907bd28be11eb85b4db52a60496fc5b162753de42733dd2646aef
7
- data.tar.gz: 5c27ebcc9f91eda5680d9a5105a6bd54c43e90b229d447ca457adc49a4d0504b7b4aabdab4e7b8029f799d66b1b80de32d0ce232f59b1589812f3c0f34571b9c
6
+ metadata.gz: fdfbc28bf90ceb8495578d0109077544ce08b4be8432ab2eba8899acf524c3cff446af6cbe2c18153c9a59dc8e486f896fa5a3041ccbb049c724f0f595ad658e
7
+ data.tar.gz: 297000ae1a8a25d0abf4fc1ecb7256ef6a7e9db066bdd6a370cc44cc674e4bcbc6256c650c582c3949fc0fd38272ea110b2a844bb5f2bab0935b08ee317cafa9
data/README.md CHANGED
@@ -193,7 +193,14 @@ result will be:
193
193
  }
194
194
  ```
195
195
 
196
+ ### Support for pushing values to specific key based on key path
196
197
 
198
+ ```ruby
199
+ {a: 1}.extend(LightMapper).push('b.c', 2, keys: :symbol)
200
+ # result { a: 1, b: { c: 2 } }
201
+ { a: 1, b: { ab: 1}}.extend(LightMapper).push('b.ab', 2, keys: :symbol, override: true)
202
+ # result { a: 1, b: { ab: 2 } }
203
+ ```
197
204
 
198
205
  ### Mappers selection via pattern matching
199
206
 
@@ -1,3 +1,3 @@
1
1
  module LightMapper
2
- VERSION = '1.0.5'
2
+ VERSION = '1.0.6'
3
3
  end
data/lib/light_mapper.rb CHANGED
@@ -76,7 +76,7 @@ module LightMapper
76
76
  end
77
77
  end
78
78
 
79
- def self.push(hash, key, value, keys: :string, build_structure: true)
79
+ def self.push(hash, key, value, keys: :string, build_structure: true, override: false)
80
80
  return hash[key] = value if key.is_a?(Symbol)
81
81
 
82
82
  path = key.to_s.split('.')
@@ -87,11 +87,11 @@ module LightMapper
87
87
 
88
88
  path.each_with_index do |k, idx|
89
89
  last_idx = idx == path.size - 1
90
- raise AlreadyAssignedValue, "Key #{k} already assigned in #{path} for #{hash.inspect} structure" if last_idx && context.key?(k) && !context[k].nil?
91
- next context[k] = value if last_idx
90
+ raise AlreadyAssignedValue, "Key #{k} already assigned in #{path} for #{hash.inspect} structure" if !override && last_idx && context.key?(k) && !context[k].nil?
91
+ next context[k] = value if last_idx && context.is_a?(Hash)
92
92
 
93
93
  context.send(:[]=,k, {}) if build_structure && !context.key?(k)
94
- context = context.send(:[], k) if context.is_a?(Hash)
94
+ context.is_a?(Hash) ? context = context.send(:[], k) : break
95
95
  end
96
96
  end
97
97
 
@@ -110,6 +110,11 @@ module LightMapper
110
110
  LightMapper.mapping(clone, mappings, opts)
111
111
  end
112
112
 
113
+ def push(key, value, keys: :string, build_structure: true, override: false)
114
+ LightMapper::Helper.push(self, key, value, keys: keys, build_structure: build_structure, override: override)
115
+ self
116
+ end
117
+
113
118
  def self.mapping(hash, mappings, opts = {})
114
119
  strict, any_keys, keys = opts.values_at(:strict, :any_keys, :keys)
115
120
 
@@ -18,248 +18,266 @@ describe LightMapper do
18
18
 
19
19
  subject { original_hash.extend(LightMapper) }
20
20
 
21
- describe 'basic data mapping' do
22
- let(:original_hash) do
23
- {
24
- 'A' => 'test',
25
- 'b' => 1
26
- }
27
- end
28
-
29
- let(:mapper) do
30
- {
31
- 'A' => :a,
32
- 'b' => 'z'
33
- }
34
- end
21
+ context '#push' do
22
+ let(:original_hash) { { a: 1, b: { ab: 1}} }
35
23
 
36
- it 'return correct hash' do
37
- expect(subject.mapping(mapper)).to eq(a: original_hash['A'], 'z' => original_hash['b'])
24
+ it 'adds value to hash by key path' do
25
+ expect(subject.push('b.ac', 2, keys: :symbol)).to match({ a: 1, b: { ab: 1, ac: 2 } })
38
26
  end
39
- end
40
27
 
41
- describe 'basic data mapping when keys are required' do
42
- let(:original_hash) do
43
- {
44
- 'A' => 'test',
45
- 'b' => 1
46
- }
28
+ it 'override value to hash by key path' do
29
+ expect(subject.push('b.ab', 2, keys: :symbol, override: true)).to match({ a: 1, b: { ab: 2 } })
47
30
  end
48
31
 
49
- let(:mapper) do
50
- {
51
- 'A' => :a,
52
- 'c' => 'z'
53
- }
54
- end
55
-
56
- it 'raise KeyError when required key missing' do
57
- expect { subject.mapping(mapper, strict: true) }.to raise_error(LightMapper::KeyMissing)
32
+ it 'not pushing values when path is not found for build_structure: false' do
33
+ expect(subject.push('b.abc.c', 2, keys: :symbol, override: true, build_structure: false)).to match({ a: 1, b: { ab: 1 } })
58
34
  end
59
35
  end
60
36
 
61
- describe 'basic data mapping when any keys kind is allowed' do
62
- let(:original_hash) do
63
- {
64
- 'A' => 'test',
65
- 'b' => 1,
66
- c: 10
67
-
68
- }
69
- end
70
-
71
- let(:mapper) do
72
- {
73
- 'A' => :a,
74
- 'c' => :c,
75
- b: 'z'
76
- }
77
- end
78
-
79
- it 'return correct hash' do
80
- expect(subject.mapping(mapper, any_keys: true)).to eq(
81
- a: original_hash['A'],
82
- c: original_hash[:c],
83
- 'z' => original_hash['b']
84
- )
85
- end
37
+ context '#mapping' do
38
+ context 'basic data mapping' do
39
+ let(:original_hash) do
40
+ {
41
+ 'A' => 'test',
42
+ 'b' => 1
43
+ }
44
+ end
86
45
 
87
- describe 'raise KeyError' do
88
46
  let(:mapper) do
89
47
  {
90
48
  'A' => :a,
91
- 'c' => :c,
92
- k: 'z'
49
+ 'b' => 'z'
93
50
  }
94
51
  end
95
52
 
96
- it ' when required key missing' do
97
- expect { subject.mapping(mapper, strict: true, any_keys: true) }.to raise_error(LightMapper::KeyMissing)
53
+ it 'return correct hash' do
54
+ expect(subject.mapping(mapper)).to eq(a: original_hash['A'], 'z' => original_hash['b'])
98
55
  end
99
56
  end
100
- end
101
-
102
- describe 'more advanced mapping' do
103
- let(:source) do
104
- {
105
- 'source' => { 'google' => { 'search_word' => 'ruby' } },
106
- 'user' => User.new(email: 'pawel@example.com', name: 'Pawel'),
107
- 'roles' => %w[admin manager user],
108
- 'mixed' => { users: [User.new(email: 'max@example.com', name: 'Max', manager: true), User.new(email: 'pawel@example.com', name: 'Pawel', manager: false)] },
109
- 'scores' => [10, 2, 5, 1000],
110
- 'last_4_payments' => [
111
- { 'amount' => 100, 'currency' => 'USD' },
112
- { 'amount' => 200, 'currency' => 'USD' },
113
- { 'amount' => 300, 'currency' => 'USD' },
114
- { 'amount' => 400, 'currency' => 'USD' }
115
- ],
116
- 'array' => [
117
- [1, 2, 3],
118
- [4, 5, 6],
119
- [
120
- 7,
121
- 8,
122
- [':D']
123
- ],
124
- ]
125
- }
126
- end
127
57
 
128
- context 'with nested hash mapping' do
129
- let(:mapping) { { 'source.google.search_word' => :word } }
130
-
131
- it 'return correct result' do
132
- expect(source.extend(LightMapper).mapping(mapping)).to eq(word: 'ruby')
58
+ context 'basic data mapping when keys are required' do
59
+ let(:original_hash) do
60
+ {
61
+ 'A' => 'test',
62
+ 'b' => 1
63
+ }
133
64
  end
134
- end
135
-
136
- context 'with nested array mapping' do
137
- let(:mapping) { { 'array.2.2.first' => :result } }
138
65
 
139
- it 'return correct result' do
140
- expect(source.extend(LightMapper).mapping(mapping)).to eq(result: ':D')
66
+ let(:mapper) do
67
+ {
68
+ 'A' => :a,
69
+ 'c' => 'z'
70
+ }
141
71
  end
142
- end
143
72
 
144
- context 'with nested object mapping' do
145
- let(:mapping) { { 'user.email' => :result } }
146
-
147
- it 'return correct result' do
148
- expect(source.extend(LightMapper).mapping(mapping)).to eq(result: 'pawel@example.com')
73
+ it 'raise KeyError when required key missing' do
74
+ expect { subject.mapping(mapper, strict: true) }.to raise_error(LightMapper::KeyMissing)
149
75
  end
150
76
  end
151
77
 
152
- context 'with mapping proc' do
153
- let(:mapping) { { (->(source) { source['last_4_payments'].last['amount'].to_s }) => :result } }
78
+ context 'basic data mapping when any keys kind is allowed' do
79
+ let(:original_hash) do
80
+ {
81
+ 'A' => 'test',
82
+ 'b' => 1,
83
+ c: 10
154
84
 
155
- it 'return correct result' do
156
- expect(source.extend(LightMapper).mapping(mapping)).to eq(result: '400')
85
+ }
157
86
  end
158
- end
159
87
 
160
- context 'with mix of nested object types' do
161
- let(:mapping) do
88
+ let(:mapper) do
162
89
  {
163
- 'source.google.search_word' => :word,
164
- 'user.email' => :email,
165
- 'user.as_json.name' => :name,
166
- 'roles.0' => :first_role,
167
- ['roles', 1] => :middle_role,
168
- 'roles.last' => :last_role,
169
- (->(source) { source['mixed'][:users].find { |user| user.manager }.email }) => :manager_email,
170
- (->(source) { source['mixed'][:users].find { |user| user.manager }.name }) => :manager_name,
171
- 'mixed.users.last.name' => :last_user_name,
172
- (->(source) { source['last_4_payments'].map(&:values).map(&:first).max }) => :quarterly_payment_amount,
173
- 'scores.sum' => :final_score,
174
- 'array.2.2.first' => :smile
90
+ 'A' => :a,
91
+ 'c' => :c,
92
+ b: 'z'
175
93
  }
176
94
  end
177
95
 
178
- it 'return correct result' do
179
- expect(source.extend(LightMapper).mapping(mapping, any_keys: true)).to match({
180
- word: 'ruby',
181
- email: 'pawel@example.com',
182
- name: 'Pawel',
183
- final_score: 1017,
184
- first_role: 'admin',
185
- last_role: 'user',
186
- manager_email: 'max@example.com',
187
- manager_name: 'Max',
188
- last_user_name: 'Pawel',
189
- middle_role: 'manager',
190
- quarterly_payment_amount: 400,
191
- smile: ':D'
192
- })
193
- end
194
-
195
- it 'return correct result base on proper key types' do
196
- expect(source.extend(LightMapper).mapping(mapping)).to match({
197
- word: 'ruby',
198
- email: 'pawel@example.com',
199
- name: 'Pawel',
200
- final_score: 1017,
201
- first_role: 'admin',
202
- last_role: 'user',
203
- manager_email: 'max@example.com',
204
- manager_name: 'Max',
205
- last_user_name: nil,
206
- middle_role: 'manager',
207
- quarterly_payment_amount: 400,
208
- smile: ':D'
209
- })
96
+ it 'return correct hash' do
97
+ expect(subject.mapping(mapper, any_keys: true)).to eq(
98
+ a: original_hash['A'],
99
+ c: original_hash[:c],
100
+ 'z' => original_hash['b']
101
+ )
210
102
  end
211
103
 
212
- it 'raise KeyMissing error' do
213
- expect { source.extend(LightMapper).mapping({ 'user.non_existence_method' => :result }, strict: true) }.to raise_error(LightMapper::KeyMissing)
214
- expect { source.extend(LightMapper).mapping({ 'last_4_payments.4' => :result }, strict: true) }.to raise_error(LightMapper::KeyMissing)
215
- expect { source.extend(LightMapper).mapping({ 'source.google.missing_key' => :result }, strict: true) }.to raise_error(LightMapper::KeyMissing)
104
+ context 'raise KeyError' do
105
+ let(:mapper) do
106
+ {
107
+ 'A' => :a,
108
+ 'c' => :c,
109
+ k: 'z'
110
+ }
111
+ end
112
+
113
+ it ' when required key missing' do
114
+ expect { subject.mapping(mapper, strict: true, any_keys: true) }.to raise_error(LightMapper::KeyMissing)
115
+ end
216
116
  end
217
117
  end
218
118
 
219
- context 'nested input to nested output' do
220
- let(:source) do
119
+ context 'more advanced mapping' do
120
+ let(:original_hash) do
221
121
  {
222
- 'source' => { 'google' => { 'private_pool' => true } },
122
+ 'source' => { 'google' => { 'search_word' => 'ruby' } },
223
123
  'user' => User.new(email: 'pawel@example.com', name: 'Pawel'),
224
124
  'roles' => %w[admin manager user],
125
+ 'mixed' => { users: [User.new(email: 'max@example.com', name: 'Max', manager: true), User.new(email: 'pawel@example.com', name: 'Pawel', manager: false)] },
225
126
  'scores' => [10, 2, 5, 1000],
226
127
  'last_4_payments' => [
227
128
  { 'amount' => 100, 'currency' => 'USD' },
228
129
  { 'amount' => 200, 'currency' => 'USD' },
229
130
  { 'amount' => 300, 'currency' => 'USD' },
230
131
  { 'amount' => 400, 'currency' => 'USD' }
132
+ ],
133
+ 'array' => [
134
+ [1, 2, 3],
135
+ [4, 5, 6],
136
+ [
137
+ 7,
138
+ 8,
139
+ [':D']
140
+ ],
231
141
  ]
232
142
  }
233
143
  end
234
144
 
235
- let(:mapping) do
236
- {
237
- 'source.google.private_pool' => 'private',
238
- 'user.email' => 'user.email',
239
- 'user.name' => 'user.name',
240
- 'roles' => 'user.roles',
241
- 'scores.max' => 'user.best_score',
242
- 'last_4_payments.last' => 'payment.last',
243
- }
145
+ context 'with nested hash mapping' do
146
+ let(:mapping) { { 'source.google.search_word' => :word } }
147
+
148
+ it 'return correct result' do
149
+ expect(subject.mapping(mapping)).to eq(word: 'ruby')
150
+ end
151
+ end
152
+
153
+ context 'with nested array mapping' do
154
+ let(:mapping) { { 'array.2.2.first' => :result } }
155
+
156
+ it 'return correct result' do
157
+ expect(subject.mapping(mapping)).to eq(result: ':D')
158
+ end
159
+ end
160
+
161
+ context 'with nested object mapping' do
162
+ let(:mapping) { { 'user.email' => :result } }
163
+
164
+ it 'return correct result' do
165
+ expect(subject.mapping(mapping)).to eq(result: 'pawel@example.com')
166
+ end
167
+ end
168
+
169
+ context 'with mapping proc' do
170
+ let(:mapping) { { (->(source) { source['last_4_payments'].last['amount'].to_s }) => :result } }
171
+
172
+ it 'return correct result' do
173
+ expect(subject.mapping(mapping)).to eq(result: '400')
174
+ end
244
175
  end
245
176
 
246
- it 'return correct result' do
247
- expect(source.extend(LightMapper).mapping(mapping, keys: :symbol)).to match({
248
- private: true,
249
- user: {
250
- email: 'pawel@example.com',
251
- name: 'Pawel',
252
- roles: %w[admin manager user],
253
- best_score: 1000,
254
- },
255
- payment: {
256
- last: { 'amount' => 400, 'currency' => 'USD' }
257
- }
258
- })
177
+ context 'with mix of nested object types' do
178
+ let(:mapping) do
179
+ {
180
+ 'source.google.search_word' => :word,
181
+ 'user.email' => :email,
182
+ 'user.as_json.name' => :name,
183
+ 'roles.0' => :first_role,
184
+ ['roles', 1] => :middle_role,
185
+ 'roles.last' => :last_role,
186
+ (->(source) { source['mixed'][:users].find { |user| user.manager }.email }) => :manager_email,
187
+ (->(source) { source['mixed'][:users].find { |user| user.manager }.name }) => :manager_name,
188
+ 'mixed.users.last.name' => :last_user_name,
189
+ (->(source) { source['last_4_payments'].map(&:values).map(&:first).max }) => :quarterly_payment_amount,
190
+ 'scores.sum' => :final_score,
191
+ 'array.2.2.first' => :smile
192
+ }
193
+ end
194
+
195
+ it 'return correct result' do
196
+ expect(subject.mapping(mapping, any_keys: true)).to match({
197
+ word: 'ruby',
198
+ email: 'pawel@example.com',
199
+ name: 'Pawel',
200
+ final_score: 1017,
201
+ first_role: 'admin',
202
+ last_role: 'user',
203
+ manager_email: 'max@example.com',
204
+ manager_name: 'Max',
205
+ last_user_name: 'Pawel',
206
+ middle_role: 'manager',
207
+ quarterly_payment_amount: 400,
208
+ smile: ':D'
209
+ })
210
+ end
211
+
212
+ it 'return correct result base on proper key types' do
213
+ expect(subject.mapping(mapping)).to match({
214
+ word: 'ruby',
215
+ email: 'pawel@example.com',
216
+ name: 'Pawel',
217
+ final_score: 1017,
218
+ first_role: 'admin',
219
+ last_role: 'user',
220
+ manager_email: 'max@example.com',
221
+ manager_name: 'Max',
222
+ last_user_name: nil,
223
+ middle_role: 'manager',
224
+ quarterly_payment_amount: 400,
225
+ smile: ':D'
226
+ })
227
+ end
228
+
229
+ it 'raise KeyMissing error' do
230
+ expect { subject.mapping({ 'user.non_existence_method' => :result }, strict: true) }.to raise_error(LightMapper::KeyMissing)
231
+ expect { subject.mapping({ 'last_4_payments.4' => :result }, strict: true) }.to raise_error(LightMapper::KeyMissing)
232
+ expect { subject.mapping({ 'source.google.missing_key' => :result }, strict: true) }.to raise_error(LightMapper::KeyMissing)
233
+ end
259
234
  end
260
235
 
261
- it 'raise AlreadyAssignedValue error when key was allready assigned' do
262
- expect { source.extend(LightMapper).mapping('source.google.private_pool' => 'private', 'source.google' => 'private') }.to raise_error(LightMapper::AlreadyAssignedValue)
236
+ context 'nested input to nested output' do
237
+ let(:original_hash) do
238
+ {
239
+ 'source' => { 'google' => { 'private_pool' => true } },
240
+ 'user' => User.new(email: 'pawel@example.com', name: 'Pawel'),
241
+ 'roles' => %w[admin manager user],
242
+ 'scores' => [10, 2, 5, 1000],
243
+ 'last_4_payments' => [
244
+ { 'amount' => 100, 'currency' => 'USD' },
245
+ { 'amount' => 200, 'currency' => 'USD' },
246
+ { 'amount' => 300, 'currency' => 'USD' },
247
+ { 'amount' => 400, 'currency' => 'USD' }
248
+ ]
249
+ }
250
+ end
251
+
252
+ let(:mapping) do
253
+ {
254
+ 'source.google.private_pool' => 'private',
255
+ 'user.email' => 'user.email',
256
+ 'user.name' => 'user.name',
257
+ 'roles' => 'user.roles',
258
+ 'scores.max' => 'user.best_score',
259
+ 'last_4_payments.last' => 'payment.last',
260
+ }
261
+ end
262
+
263
+ it 'return correct result' do
264
+ expect(subject.mapping(mapping, keys: :symbol)).to match({
265
+ private: true,
266
+ user: {
267
+ email: 'pawel@example.com',
268
+ name: 'Pawel',
269
+ roles: %w[admin manager user],
270
+ best_score: 1000,
271
+ },
272
+ payment: {
273
+ last: { 'amount' => 400, 'currency' => 'USD' }
274
+ }
275
+ })
276
+ end
277
+
278
+ it 'raise AlreadyAssignedValue error when key was allready assigned' do
279
+ expect { subject.mapping('source.google.private_pool' => 'private', 'source.google' => 'private') }.to raise_error(LightMapper::AlreadyAssignedValue)
280
+ end
263
281
  end
264
282
  end
265
283
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: light_mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pawel Niemczyk