light_mapper 1.0.5 → 1.0.6

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: 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