darthjee-core_ext 1.2.6

Sign up to get free protection for your applications and to get access to all the features.
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