darthjee-core_ext 1.2.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +62 -0
  6. data/LICENSE +22 -0
  7. data/README.md +188 -0
  8. data/Rakefile +7 -0
  9. data/core_ext.gemspec +27 -0
  10. data/lib/array.rb +23 -0
  11. data/lib/array/hash_builder.rb +20 -0
  12. data/lib/darthjee.rb +4 -0
  13. data/lib/darthjee/core_ext.rb +12 -0
  14. data/lib/darthjee/core_ext/version.rb +5 -0
  15. data/lib/enumerable.rb +45 -0
  16. data/lib/hash.rb +195 -0
  17. data/lib/hash/deep_hash_constructor.rb +83 -0
  18. data/lib/hash/key_changer.rb +76 -0
  19. data/lib/hash/value_changer.rb +61 -0
  20. data/lib/numeric.rb +6 -0
  21. data/lib/symbol.rb +9 -0
  22. data/spec/lib/array_spec.rb +229 -0
  23. data/spec/lib/enumerable_spec.rb +31 -0
  24. data/spec/lib/hash/deep_hash_constructor_spec.rb +167 -0
  25. data/spec/lib/hash/key_changer_spec.rb +55 -0
  26. data/spec/lib/hash_spec.rb +347 -0
  27. data/spec/lib/numeric_spec.rb +61 -0
  28. data/spec/lib/symbol_spec.rb +35 -0
  29. data/spec/spec_helper.rb +32 -0
  30. data/spec/support/models/hash/value_changer/dummy.rb +15 -0
  31. data/spec/support/models/hash/value_changer/dummy_iteractor.rb +12 -0
  32. data/spec/support/shared_examples/array_random.rb +16 -0
  33. data/spec/support/shared_examples/chain_fetch.rb +88 -0
  34. data/spec/support/shared_examples/chain_hash_keys_changer.rb +88 -0
  35. data/spec/support/shared_examples/clean.rb +143 -0
  36. data/spec/support/shared_examples/expected.rb +6 -0
  37. data/spec/support/shared_examples/hash_keys_changer.rb +85 -0
  38. data/spec/support/shared_examples/keys_appender.rb +43 -0
  39. data/spec/support/shared_examples/keys_camelizer.rb +285 -0
  40. data/spec/support/shared_examples/keys_underscorer.rb +82 -0
  41. data/spec/support/shared_examples/remap.rb +89 -0
  42. data/spec/support/shared_examples/value_changer.rb +92 -0
  43. metadata +217 -0
@@ -0,0 +1,31 @@
1
+ describe Enumerable do
2
+ describe '#clean!' do
3
+ it_behaves_like 'an array clean method', :clean!
4
+ it_behaves_like 'a hash clean method', :clean!
5
+
6
+ it 'changes the original hash' do
7
+ hash = { a: nil}
8
+ expect { hash.clean! }.to change { hash }
9
+ end
10
+
11
+ it 'changes original array' do
12
+ array = [{ a: nil}]
13
+ expect { array.clean! }.to change { array }
14
+ end
15
+ end
16
+
17
+ describe '#clean' do
18
+ it_behaves_like 'an array clean method', :clean
19
+ it_behaves_like 'a hash clean method', :clean
20
+
21
+ it 'does not change the original hash' do
22
+ hash = { a: nil}
23
+ expect { hash.clean }.not_to change { hash }
24
+ end
25
+
26
+ it 'does not change the original array' do
27
+ array = [{ a: nil}]
28
+ expect { array.clean }.not_to change { array }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,167 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hash::DeepHashConstructor do
4
+ let(:subject) { described_class.new('.') }
5
+ let(:deep_hash) { subject.deep_hash(hash) }
6
+
7
+ describe '.deep_hash' do
8
+ let(:hash) do
9
+ {
10
+ 'person.name' => 'Some name',
11
+ 'person.age' => 22,
12
+ 'status' => :success,
13
+ 'vehicle.fuel' => 'GASOLINE',
14
+ 'vehicle.doors' => 4
15
+ }
16
+ end
17
+
18
+ let(:expected) do
19
+ {
20
+ 'person' => { 'name' => 'Some name', 'age' => 22 },
21
+ 'vehicle' => { 'fuel' => 'GASOLINE', 'doors' => 4 },
22
+ 'status' => :success
23
+ }
24
+ end
25
+
26
+ it 'build a deep hash' do
27
+ expect(deep_hash).to eq(expected)
28
+ end
29
+
30
+ context 'with indexed keys' do
31
+ let(:hash) do
32
+ {
33
+ 'person[0].name' => 'First person',
34
+ 'person[0].age' => 22,
35
+ 'person[1].name' => 'Second person',
36
+ 'person[1].age' => 27,
37
+ 'device[0]' => 'GEAR_LOCK',
38
+ 'device[1]' => 'GPS',
39
+ 'zipCode' => '122345-123'
40
+ }
41
+ end
42
+
43
+ let(:expected) do
44
+ {
45
+ 'person' => [
46
+ { 'name' => 'First person', 'age' => 22 },
47
+ { 'name' => 'Second person', 'age' => 27 }
48
+ ],
49
+ 'device' => %w(GEAR_LOCK GPS),
50
+ 'zipCode' => '122345-123'
51
+ }
52
+ end
53
+
54
+ it 'build a deep hash with arrays' do
55
+ expect(deep_hash).to eq(expected)
56
+ end
57
+ end
58
+
59
+ context 'with a n level hash' do
60
+ let(:hash) do
61
+ {
62
+ 'quote_request.personal.person.name' => 'Some name',
63
+ 'quote_request.personal.person.age' => 22,
64
+ 'quote_request.insurance.vehicle.fuel' => 'GASOLINE',
65
+ 'quote_request.insurance.vehicle.doors' => 4,
66
+ 'request.status' => :success,
67
+ 'trials' => 3
68
+ }
69
+ end
70
+
71
+ let(:expected) do
72
+ {
73
+ 'quote_request' => {
74
+ 'personal' => {
75
+ 'person' => { 'name' => 'Some name', 'age' => 22 }
76
+ },
77
+ 'insurance' => {
78
+ 'vehicle' => { 'fuel' => 'GASOLINE', 'doors' => 4 }
79
+ }
80
+ },
81
+ 'request' => { 'status' => :success },
82
+ 'trials' => 3
83
+ }
84
+ end
85
+
86
+ it 'build a deep hash with arrays' do
87
+ expect(deep_hash).to eq(expected)
88
+ end
89
+ end
90
+
91
+ context 'with a n level hash and arrays' do
92
+ let(:hash) do
93
+ {
94
+ 'quote_request.personal.person[0].name' => 'Some name 1',
95
+ 'quote_request.personal.person[0].age' => 22,
96
+ 'quote_request.personal.person[1].name' => 'Some name 2',
97
+ 'quote_request.personal.person[1].age' => 23,
98
+ 'request[0].status.clazz' => String,
99
+ 'request[1].status.clazz' => Integer,
100
+ 'request[2].status.clazz' => Date,
101
+ 'trials' => 3
102
+ }
103
+ end
104
+
105
+ let(:expected) do
106
+ {
107
+ 'quote_request' => {
108
+ 'personal' => {
109
+ 'person' => [
110
+ { 'name' => 'Some name 1', 'age' => 22 },
111
+ { 'name' => 'Some name 2', 'age' => 23 }
112
+ ]
113
+ }
114
+ },
115
+ 'request' => [
116
+ { 'status' => { 'clazz' => String } },
117
+ { 'status' => { 'clazz' => Integer } },
118
+ { 'status' => { 'clazz' => Date } }
119
+ ],
120
+ 'trials' => 3
121
+ }
122
+ end
123
+
124
+ it 'build a deep hash with arrays' do
125
+ expect(deep_hash).to eq(expected)
126
+ end
127
+ end
128
+
129
+ context 'with custom separator' do
130
+ let(:subject) { described_class.new('_') }
131
+ let(:hash) do
132
+ {
133
+ 'person_name' => 'Some name',
134
+ 'person_age' => 22,
135
+ 'status' => :success,
136
+ 'vehicle_fuel' => 'GASOLINE',
137
+ 'vehicle_doors' => 4
138
+ }
139
+ end
140
+
141
+ it 'build a deep hash with arrays' do
142
+ expect(deep_hash).to eq(expected)
143
+ end
144
+ end
145
+
146
+ context 'with custom separator on n level deep hash' do
147
+ let(:subject) { described_class.new('_') }
148
+ let(:hash) do
149
+ {
150
+ 'person_name_clazz' => String
151
+ }
152
+ end
153
+
154
+ let(:expected) do
155
+ {
156
+ 'person' => {
157
+ 'name' => { 'clazz' => String }
158
+ }
159
+ }
160
+ end
161
+
162
+ it 'build a deep hash with arrays' do
163
+ expect(deep_hash).to eq(expected)
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hash::KeyChanger do
4
+ let(:subject) { described_class.new(hash) }
5
+
6
+ describe '#underscore_keys' do
7
+ let(:hash) { { keyUnderscore: 1 } }
8
+
9
+ it 'underscore all the keys' do
10
+ expect(subject.underscore_keys).to eq(key_underscore: 1)
11
+ end
12
+
13
+ context 'when hash is a many level hash' do
14
+ let(:hash) { { keyUnderscore: { anotherKey: 1 } } }
15
+
16
+ it 'underscore all the keys' do
17
+ expect(subject.underscore_keys).to eq(key_underscore: { another_key: 1 })
18
+ end
19
+ end
20
+
21
+ context 'when hash has an array' do
22
+ let(:hash) { { keyUnderscore: [{ anotherKey: 1 }] } }
23
+
24
+ it 'underscore all the keys' do
25
+ expect(subject.underscore_keys).to eq(key_underscore: [{ another_key: 1 }])
26
+ end
27
+ end
28
+
29
+ context 'changes the hash' do
30
+ it 'underscore all the keys' do
31
+ expect do
32
+ subject.underscore_keys
33
+ end.to change { hash }
34
+ end
35
+ end
36
+
37
+ context 'when giving non recursive options' do
38
+ context 'when hash is a many level hash' do
39
+ let(:hash) { { keyUnderscore: { anotherKey: 1 } } }
40
+
41
+ it 'underscore all the keys' do
42
+ expect(subject.underscore_keys(recursive: false)).to eq(key_underscore: { anotherKey: 1 })
43
+ end
44
+ end
45
+
46
+ context 'when hash has an array' do
47
+ let(:hash) { { keyUnderscore: [{ anotherKey: 1 }] } }
48
+
49
+ it 'underscore all the keys' do
50
+ expect(subject.underscore_keys(recursive: false)).to eq(key_underscore: [{ anotherKey: 1 }])
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,347 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hash do
4
+ it_behaves_like 'a class with change_key method'
5
+ it_behaves_like 'a class with chain_change_key method'
6
+ it_behaves_like 'a class with camlize_keys method'
7
+ it_behaves_like 'a class with underscore_keys method'
8
+ it_behaves_like 'a class with append_keys method'
9
+ it_behaves_like 'a class with change_values method'
10
+ it_behaves_like 'a class with remap method'
11
+ it_behaves_like 'an object with chain_fetch method'
12
+
13
+ describe :squash do
14
+ let(:hash) { { a: { b: 1, c: { d: 2 } } } }
15
+
16
+ it 'flattens the hash' do
17
+ expect(hash.squash).to eq('a.b' => 1, 'a.c.d' => 2)
18
+ end
19
+
20
+ it { expect { hash.squash }.not_to change { hash } }
21
+
22
+ context 'with array value' do
23
+ let(:hash) { { a: { b: [1, { x: 3, y: { z: 4 } }] } } }
24
+
25
+ it 'flattens the hash' do
26
+ expect(hash.squash).to eq('a.b' => [1, { x: 3, y: { z: 4 } }])
27
+ end
28
+ end
29
+ end
30
+
31
+ describe :sort_keys do
32
+ it 'sorts keys as symbols' do
33
+ expect({ b: 1, a: 2 }.sort_keys).to eq(a: 2, b: 1)
34
+ end
35
+ it 'sorts keys as string' do
36
+ expect({ 'b' => 1, 'a' => 2 }.sort_keys).to eq('a' => 2, 'b' => 1)
37
+ end
38
+ it 'sorts keys recursively' do
39
+ expect({ b: 1, a: { d: 3, c: 4 } }.sort_keys).to eq(a: { c: 4, d: 3 }, b: 1)
40
+ end
41
+ it 'sorts keys recursively when argumen is passed' do
42
+ expect({ b: 1, a: { d: 3, c: 4 } }.sort_keys(recursive: true)).to eq(a: { c: 4, d: 3 }, b: 1)
43
+ end
44
+ it 'does not sorts keys recursively when argumen is passed' do
45
+ expect({ b: 1, a: { d: 3, c: 4 } }.sort_keys(recursive: false)).to eq(a: { d: 3, c: 4 }, b: 1)
46
+ end
47
+ it 'sort recursevely on many levels' do
48
+ hash = { b: 1, a: { d: 2, c: { e: 3, f: 4 } } }
49
+ expected = { a: { c: { f: 4, e: 3 }, d: 2 }, b: 1 }
50
+ expect(hash.sort_keys(recursive: true)).to eq(expected)
51
+ end
52
+ it 'applies to arrays as well' do
53
+ hash = { b: 1, a: { d: 2, c: [{ e: 3, f: 4 }] } }
54
+ expected = { a: { c: [{ f: 4, e: 3 }], d: 2 }, b: 1 }
55
+ expect(hash.sort_keys(recursive: true)).to eq(expected)
56
+ end
57
+ end
58
+
59
+ describe :exclusive_merge do
60
+ let(:subject) { { a: 1, b: 2 } }
61
+ let(:other) { { b: 3, c: 4 } }
62
+
63
+ it 'merge only the common keys' do
64
+ expect(subject.exclusive_merge(other)).to eq(a: 1, b: 3)
65
+ end
66
+
67
+ it 'does not change the original hash' do
68
+ expect { subject.exclusive_merge(other) }.not_to change { subject }
69
+ end
70
+ end
71
+
72
+ describe :exclusive_merge! do
73
+ let(:subject) { { a: 1, b: 2 } }
74
+ let(:other) { { b: 3, c: 4 } }
75
+
76
+ it 'merge only the common keys' do
77
+ expect(subject.exclusive_merge!(other)).to eq(a: 1, b: 3)
78
+ end
79
+
80
+ it 'does not change the original hash' do
81
+ expect { subject.exclusive_merge!(other) }.to change { subject }
82
+ end
83
+ end
84
+
85
+ describe :to_deep_hash do
86
+ let(:subject) do
87
+ {
88
+ 'person.name' => 'Some name',
89
+ 'person.age' => 22,
90
+ 'status' => :success,
91
+ 'vehicle.fuel' => 'GASOLINE',
92
+ 'vehicle.doors' => 4
93
+ }
94
+ end
95
+
96
+ let(:expected) do
97
+ {
98
+ 'person' => { 'name' => 'Some name', 'age' => 22 },
99
+ 'vehicle' => { 'fuel' => 'GASOLINE', 'doors' => 4 },
100
+ 'status' => :success
101
+ }
102
+ end
103
+
104
+ it 'build a deep hash' do
105
+ expect(subject.to_deep_hash).to eq(expected)
106
+ end
107
+
108
+ context 'with indexed keys' do
109
+ let(:subject) do
110
+ {
111
+ 'person[0].name' => 'First person',
112
+ 'person[0].age' => 22,
113
+ 'person[1].name' => 'Second person',
114
+ 'person[1].age' => 27,
115
+ 'device[0]' => 'GEAR_LOCK',
116
+ 'device[1]' => 'GPS',
117
+ 'zipCode' => '122345-123'
118
+ }
119
+ end
120
+
121
+ let(:expected) do
122
+ {
123
+ 'person' => [
124
+ { 'name' => 'First person', 'age' => 22 },
125
+ { 'name' => 'Second person', 'age' => 27 }
126
+ ],
127
+ 'device' => %w(GEAR_LOCK GPS),
128
+ 'zipCode' => '122345-123'
129
+ }
130
+ end
131
+
132
+ it 'build a deep hash with arrays' do
133
+ expect(subject.to_deep_hash).to eq(expected)
134
+ end
135
+ end
136
+
137
+ context 'with a n level hash' do
138
+ let(:subject) do
139
+ {
140
+ 'quote_request.personal.person.name' => 'Some name',
141
+ 'quote_request.personal.person.age' => 22,
142
+ 'quote_request.insurance.vehicle.fuel' => 'GASOLINE',
143
+ 'quote_request.insurance.vehicle.doors' => 4,
144
+ 'request.status' => :success,
145
+ 'trials' => 3
146
+ }
147
+ end
148
+
149
+ let(:expected) do
150
+ {
151
+ 'quote_request' => {
152
+ 'personal' => {
153
+ 'person' => { 'name' => 'Some name', 'age' => 22 }
154
+ },
155
+ 'insurance' => {
156
+ 'vehicle' => { 'fuel' => 'GASOLINE', 'doors' => 4 }
157
+ }
158
+ },
159
+ 'request' => { 'status' => :success },
160
+ 'trials' => 3
161
+ }
162
+ end
163
+
164
+ it 'build a deep hash with arrays' do
165
+ expect(subject.to_deep_hash).to eq(expected)
166
+ end
167
+ end
168
+
169
+ context 'with a n level hash and arrays' do
170
+ let(:subject) do
171
+ {
172
+ 'quote_request.personal.person[0].name' => 'Some name 1',
173
+ 'quote_request.personal.person[0].age' => 22,
174
+ 'quote_request.personal.person[1].name' => 'Some name 2',
175
+ 'quote_request.personal.person[1].age' => 23,
176
+ 'request[0].status.clazz' => String,
177
+ 'request[1].status.clazz' => Integer,
178
+ 'request[2].status.clazz' => Date,
179
+ 'trials' => 3
180
+ }
181
+ end
182
+
183
+ let(:expected) do
184
+ {
185
+ 'quote_request' => {
186
+ 'personal' => {
187
+ 'person' => [
188
+ { 'name' => 'Some name 1', 'age' => 22 },
189
+ { 'name' => 'Some name 2', 'age' => 23 }
190
+ ]
191
+ }
192
+ },
193
+ 'request' => [
194
+ { 'status' => { 'clazz' => String } },
195
+ { 'status' => { 'clazz' => Integer } },
196
+ { 'status' => { 'clazz' => Date } }
197
+ ],
198
+ 'trials' => 3
199
+ }
200
+ end
201
+
202
+ it 'build a deep hash with arrays' do
203
+ expect(subject.to_deep_hash).to eq(expected)
204
+ end
205
+ end
206
+
207
+ context 'with custom separator' do
208
+ let(:subject) do
209
+ {
210
+ 'person_name' => 'Some name',
211
+ 'person_age' => 22,
212
+ 'status' => :success,
213
+ 'vehicle_fuel' => 'GASOLINE',
214
+ 'vehicle_doors' => 4
215
+ }
216
+ end
217
+
218
+ it 'build a deep hash with arrays' do
219
+ expect(subject.to_deep_hash('_')).to eq(expected)
220
+ end
221
+ end
222
+
223
+ context 'with custom separator on n level deep hash' do
224
+ let(:subject) do
225
+ {
226
+ 'person_name_clazz' => String
227
+ }
228
+ end
229
+
230
+ let(:expected) do
231
+ {
232
+ 'person' => {
233
+ 'name' => { 'clazz' => String }
234
+ }
235
+ }
236
+ end
237
+
238
+ it 'build a deep hash with arrays' do
239
+ expect(subject.to_deep_hash('_')).to eq(expected)
240
+ end
241
+ end
242
+ end
243
+
244
+ describe '#map_and_find' do
245
+ let(:hash) { { a: 1, b: 2, c: 3, d: 4 } }
246
+ let(:value) { hash.map_and_find(&block) }
247
+
248
+ context 'when block returns nil' do
249
+ let(:block) { proc {} }
250
+ it { expect(value).to be_nil }
251
+ end
252
+
253
+ context 'when block returns false' do
254
+ let(:block) { proc { false } }
255
+ it { expect(value).to be_nil }
256
+ end
257
+
258
+ context 'when block returns a true evaluated value' do
259
+ let(:block) { proc { |_, v| v.to_s } }
260
+
261
+ it { expect(value).to eq('1') }
262
+
263
+ context 'when block returns the key and not the value' do
264
+ let(:block) { proc { |k, v| v > 1 && k } }
265
+
266
+ it { expect(value).to eq(:b) }
267
+ end
268
+
269
+ context 'but not for the first value' do
270
+ let(:transformer) { double(:transformer) }
271
+ let(:block) { proc { |_, v| transformer.transform(v) } }
272
+
273
+ before do
274
+ allow(transformer).to receive(:transform) do |v|
275
+ v.to_s if v > 1
276
+ end
277
+ value
278
+ end
279
+
280
+ it { expect(value).to eq('2') }
281
+ it 'calls the mapping only until it returns a valid value' do
282
+ expect(transformer).to have_received(:transform).exactly(2)
283
+ end
284
+ end
285
+ end
286
+
287
+ context 'when the block accepts one argument' do
288
+ let(:block) { proc { |v| v } }
289
+
290
+ it do
291
+ expect(value).to eq([:a, 1])
292
+ end
293
+ end
294
+ end
295
+
296
+ describe '#map_and_select' do
297
+ let(:hash) { { a: 1, b: 2, c: 3, d: 4 } }
298
+ let(:list) { hash.map_and_select(&block) }
299
+
300
+ context 'when block returns nil' do
301
+ let(:block) { proc {} }
302
+ it { expect(list).to be_empty }
303
+ end
304
+
305
+ context 'when block returns false' do
306
+ let(:block) { proc { false } }
307
+ it { expect(list).to be_empty }
308
+ end
309
+
310
+ context 'when block returns a true evaluated value' do
311
+ let(:block) { proc { |_, v| v.to_s } }
312
+
313
+ it { expect(list).to eq((1..4).map(&:to_s)) }
314
+
315
+ context 'when block returns the key and not the value' do
316
+ let(:block) { proc { |k, v| v > 1 && k } }
317
+
318
+ it { expect(list).to eq([:b, :c, :d]) }
319
+ end
320
+
321
+ context 'but not for the first value' do
322
+ let(:transformer) { double(:transformer) }
323
+ let(:block) { proc { |_, v| transformer.transform(v) } }
324
+
325
+ before do
326
+ allow(transformer).to receive(:transform) do |v|
327
+ v.to_s if v > 1
328
+ end
329
+ list
330
+ end
331
+
332
+ it { expect(list).to eq(hash.values[1..-1].map(&:to_s)) }
333
+ it 'calls the mapping only once for each value' do
334
+ expect(transformer).to have_received(:transform).exactly(4)
335
+ end
336
+ end
337
+ end
338
+
339
+ context 'when the block accepts one argument' do
340
+ let(:block) { proc { |v| v } }
341
+
342
+ it do
343
+ expect(list).to eq([[:a, 1], [:b, 2], [:c, 3], [:d, 4]])
344
+ end
345
+ end
346
+ end
347
+ end