bidu-core_ext 1.0.0

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.
@@ -0,0 +1,287 @@
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 camlize_keys method'
6
+ it_behaves_like 'a class with append_keys method'
7
+ it_behaves_like 'a class with change_kvalues method'
8
+ it_behaves_like 'a class with remap method'
9
+ it_behaves_like 'an object with chain_fetch method'
10
+
11
+ describe :squash do
12
+ let(:hash) { { a: { b: 1, c: { d: 2 } } } }
13
+
14
+ it 'flattens the hash' do
15
+ expect(hash.squash).to eq('a.b' => 1, 'a.c.d' => 2)
16
+ end
17
+
18
+ it { expect { hash.squash }.not_to change { hash } }
19
+
20
+ context 'with array value' do
21
+ let(:hash) { { a: { b: [1, { x: 3, y: { z: 4 } }] } } }
22
+
23
+ it 'flattens the hash' do
24
+ expect(hash.squash).to eq('a.b' => [1, { x: 3, y: { z: 4 } }])
25
+ end
26
+ end
27
+ end
28
+
29
+ describe :sort_keys do
30
+ it 'sorts keys as symbols' do
31
+ expect({ b: 1, a: 2 }.sort_keys).to eq(a: 2, b: 1)
32
+ end
33
+ it 'sorts keys as string' do
34
+ expect({ 'b' => 1, 'a' => 2 }.sort_keys).to eq('a' => 2, 'b' => 1)
35
+ end
36
+ it 'sorts keys recursively' do
37
+ expect({ b: 1, a: { d: 3, c: 4 } }.sort_keys).to eq(a: { c: 4, d: 3 }, b: 1)
38
+ end
39
+ it 'sorts keys recursively when argumen is passed' do
40
+ expect({ b: 1, a: { d: 3, c: 4 } }.sort_keys(recursive: true)).to eq(a: { c: 4, d: 3 }, b: 1)
41
+ end
42
+ it 'does not sorts keys recursively when argumen is passed' do
43
+ expect({ b: 1, a: { d: 3, c: 4 } }.sort_keys(recursive: false)).to eq(a: { d: 3, c: 4 }, b: 1)
44
+ end
45
+ it 'sort recursevely on many levels' do
46
+ hash = { b: 1, a: { d: 2, c: { e: 3, f: 4 } } }
47
+ expected = { a: { c: { f: 4, e: 3 }, d: 2 }, b: 1 }
48
+ expect(hash.sort_keys(recursive: true)).to eq(expected)
49
+ end
50
+ it 'applies to arrays as well' do
51
+ hash = { b: 1, a: { d: 2, c: [{ e: 3, f: 4 }] } }
52
+ expected = { a: { c: [{ f: 4, e: 3 }], d: 2 }, b: 1 }
53
+ expect(hash.sort_keys(recursive: true)).to eq(expected)
54
+ end
55
+ end
56
+
57
+ describe :exclusive_merge do
58
+ let(:subject) { { a: 1, b: 2 } }
59
+ let(:other) { { b: 3, c: 4 } }
60
+
61
+ it 'merge only the common keys' do
62
+ expect(subject.exclusive_merge(other)).to eq(a: 1, b: 3)
63
+ end
64
+
65
+ it 'does not change the original hash' do
66
+ expect { subject.exclusive_merge(other) }.not_to change { subject }
67
+ end
68
+ end
69
+
70
+ describe :exclusive_merge! do
71
+ let(:subject) { { a: 1, b: 2 } }
72
+ let(:other) { { b: 3, c: 4 } }
73
+
74
+ it 'merge only the common keys' do
75
+ expect(subject.exclusive_merge!(other)).to eq(a: 1, b: 3)
76
+ end
77
+
78
+ it 'does not change the original hash' do
79
+ expect { subject.exclusive_merge!(other) }.to change { subject }
80
+ end
81
+ end
82
+
83
+ describe :to_deep_hash do
84
+ let(:subject) do
85
+ {
86
+ 'person.name' => 'Some name',
87
+ 'person.age' => 22,
88
+ 'status' => :success,
89
+ 'vehicle.fuel' => 'GASOLINE',
90
+ 'vehicle.doors' => 4
91
+ }
92
+ end
93
+
94
+ let(:expected) do
95
+ {
96
+ 'person' => { 'name' => 'Some name', 'age' => 22 },
97
+ 'vehicle' => { 'fuel' => 'GASOLINE', 'doors' => 4 },
98
+ 'status' => :success
99
+ }
100
+ end
101
+
102
+ it 'build a deep hash' do
103
+ expect(subject.to_deep_hash).to eq(expected)
104
+ end
105
+
106
+ context 'with indexed keys' do
107
+ let(:subject) do
108
+ {
109
+ 'person[0].name' => 'First person',
110
+ 'person[0].age' => 22,
111
+ 'person[1].name' => 'Second person',
112
+ 'person[1].age' => 27,
113
+ 'device[0]' => 'GEAR_LOCK',
114
+ 'device[1]' => 'GPS',
115
+ 'zipCode' => '122345-123'
116
+ }
117
+ end
118
+
119
+ let(:expected) do
120
+ {
121
+ 'person' => [
122
+ { 'name' => 'First person', 'age' => 22 },
123
+ { 'name' => 'Second person', 'age' => 27 }
124
+ ],
125
+ 'device' => %w(GEAR_LOCK GPS),
126
+ 'zipCode' => '122345-123'
127
+ }
128
+ end
129
+
130
+ it 'build a deep hash with arrays' do
131
+ expect(subject.to_deep_hash).to eq(expected)
132
+ end
133
+ end
134
+
135
+ context 'with a n level hash' do
136
+ let(:subject) do
137
+ {
138
+ 'quote_request.personal.person.name' => 'Some name',
139
+ 'quote_request.personal.person.age' => 22,
140
+ 'quote_request.insurance.vehicle.fuel' => 'GASOLINE',
141
+ 'quote_request.insurance.vehicle.doors' => 4,
142
+ 'request.status' => :success,
143
+ 'trials' => 3
144
+ }
145
+ end
146
+
147
+ let(:expected) do
148
+ {
149
+ 'quote_request' => {
150
+ 'personal' => {
151
+ 'person' => { 'name' => 'Some name', 'age' => 22 }
152
+ },
153
+ 'insurance' => {
154
+ 'vehicle' => { 'fuel' => 'GASOLINE', 'doors' => 4 }
155
+ }
156
+ },
157
+ 'request' => { 'status' => :success },
158
+ 'trials' => 3
159
+ }
160
+ end
161
+
162
+ it 'build a deep hash with arrays' do
163
+ expect(subject.to_deep_hash).to eq(expected)
164
+ end
165
+ end
166
+
167
+ context 'with a n level hash and arrays' do
168
+ let(:subject) do
169
+ {
170
+ 'quote_request.personal.person[0].name' => 'Some name 1',
171
+ 'quote_request.personal.person[0].age' => 22,
172
+ 'quote_request.personal.person[1].name' => 'Some name 2',
173
+ 'quote_request.personal.person[1].age' => 23,
174
+ 'request[0].status.clazz' => String,
175
+ 'request[1].status.clazz' => Fixnum,
176
+ 'request[2].status.clazz' => Date,
177
+ 'trials' => 3
178
+ }
179
+ end
180
+
181
+ let(:expected) do
182
+ {
183
+ 'quote_request' => {
184
+ 'personal' => {
185
+ 'person' => [
186
+ { 'name' => 'Some name 1', 'age' => 22 },
187
+ { 'name' => 'Some name 2', 'age' => 23 }
188
+ ]
189
+ }
190
+ },
191
+ 'request' => [
192
+ { 'status' => { 'clazz' => String } },
193
+ { 'status' => { 'clazz' => Fixnum } },
194
+ { 'status' => { 'clazz' => Date } }
195
+ ],
196
+ 'trials' => 3
197
+ }
198
+ end
199
+
200
+ it 'build a deep hash with arrays' do
201
+ expect(subject.to_deep_hash).to eq(expected)
202
+ end
203
+ end
204
+
205
+ context 'with custom separator' do
206
+ let(:subject) do
207
+ {
208
+ 'person_name' => 'Some name',
209
+ 'person_age' => 22,
210
+ 'status' => :success,
211
+ 'vehicle_fuel' => 'GASOLINE',
212
+ 'vehicle_doors' => 4
213
+ }
214
+ end
215
+
216
+ it 'build a deep hash with arrays' do
217
+ expect(subject.to_deep_hash('_')).to eq(expected)
218
+ end
219
+ end
220
+
221
+ context 'with custom separator on n level deep hash' do
222
+ let(:subject) do
223
+ {
224
+ 'person_name_clazz' => String
225
+ }
226
+ end
227
+
228
+ let(:expected) do
229
+ {
230
+ 'person' => {
231
+ 'name' => { 'clazz' => String }
232
+ }
233
+ }
234
+ end
235
+
236
+ it 'build a deep hash with arrays' do
237
+ expect(subject.to_deep_hash('_')).to eq(expected)
238
+ end
239
+ end
240
+ end
241
+
242
+ describe '#map_and_find' do
243
+ let(:hash) { { a: 1, b: 2, c: 3, d: 4 } }
244
+ let(:value) { hash.map_and_find(&block) }
245
+
246
+ context 'when block returns nil' do
247
+ let(:block) { proc {} }
248
+ it { expect(value).to be_nil }
249
+ end
250
+
251
+ context 'when block returns false' do
252
+ let(:block) { proc { false } }
253
+ it { expect(value).to be_nil }
254
+ end
255
+
256
+ context 'when block returns a true evaluated value' do
257
+ let(:block) { proc { |_, v| v.to_s } }
258
+
259
+ it { expect(value).to eq('1') }
260
+
261
+ context 'but not for the first value' do
262
+ let(:transformer) { double(:transformer) }
263
+ let(:block) { proc { |_, v| transformer.transform(v) } }
264
+
265
+ before do
266
+ allow(transformer).to receive(:transform) do |v|
267
+ v.to_s if v > 1
268
+ end
269
+ value
270
+ end
271
+
272
+ it { expect(value).to eq('2') }
273
+ it 'calls the mapping only until it returns a valid value' do
274
+ expect(transformer).to have_received(:transform).exactly(2)
275
+ end
276
+ end
277
+ end
278
+
279
+ context 'when the block accepts one argument' do
280
+ let(:block) { proc { |v| v } }
281
+
282
+ it do
283
+ expect(value).to eq([:a, 1])
284
+ end
285
+ end
286
+ end
287
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Numeric do
4
+ describe '#percent_of' do
5
+ context 'when number is float' do
6
+ let(:number) { 120.0 }
7
+
8
+ it 'converts to percentage of number' do
9
+ expect(number.percent_of(240)).to eq(50)
10
+ end
11
+
12
+ it 'converts to percentage of number' do
13
+ expect(number.percent_of(60)).to eq(200)
14
+ end
15
+
16
+ it 'do not raise an error when divisor is 0' do
17
+ expect(100.0.percent_of(0)).to eq(Float::INFINITY)
18
+ end
19
+
20
+ it 'do not raise an error when divisor is 0.0' do
21
+ expect(100.0.percent_of(0.0)).to eq(Float::INFINITY)
22
+ end
23
+ end
24
+
25
+ context 'when number is integer' do
26
+ it 'converts to percentage of number' do
27
+ expect(500.percent_of(50)).to eq(1000)
28
+ end
29
+
30
+ it 'converts to percentage of number' do
31
+ expect(0.percent_of(50)).to eq(0)
32
+ end
33
+
34
+ it 'converts to percentage of number' do
35
+ expect(10.percent_of(100)).to eq(10)
36
+ end
37
+
38
+ it 'do not raise an error when divisor is 0' do
39
+ expect(100.percent_of(0)).to eq(Float::INFINITY)
40
+ end
41
+ end
42
+
43
+ context 'when a number is 0' do
44
+ context 'when divident is 0' do
45
+ it { expect(0.percent_of(100)).to eq(0) }
46
+ end
47
+
48
+ context 'when divisor is 0' do
49
+ it { expect(100.percent_of(0)).to eq(Float::INFINITY) }
50
+ end
51
+
52
+ context 'both are 0' do
53
+ it { expect(0.percent_of(0)).to eq(Float::INFINITY) }
54
+ end
55
+ end
56
+
57
+ context 'when divisor is nil' do
58
+ it { expect(100.percent_of(nil)).to eq(Float::INFINITY) }
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe Symbol do
4
+ describe '#camelize' do
5
+ it { expect(:sym.camelize).to be_kind_of(Symbol) }
6
+
7
+ context 'when called with upper option' do
8
+ it 'camelize the symbol' do
9
+ expect(:underscore_sym.camelize(:upper)).to eq(:UnderscoreSym)
10
+ end
11
+ end
12
+
13
+ context 'when called with lower option' do
14
+ it 'snake case the symbol' do
15
+ expect(:underscore_sym.camelize(:lower)).to eq(:underscoreSym)
16
+ end
17
+ end
18
+
19
+ context 'when called without option' do
20
+ it 'camelize the symbol' do
21
+ expect(:underscore_sym.camelize).to eq(:UnderscoreSym)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.profiles.define 'gem' do
4
+ add_filter '/spec/'
5
+ end
6
+
7
+ SimpleCov.start 'gem'
8
+ require 'bidu/core_ext'
9
+
10
+ # This file was generated by the `rspec --init` command. Conventionally, all
11
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
12
+ # Require this file using `require "spec_helper"` to ensure that it is only
13
+ # loaded once.
14
+ #
15
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
+
17
+ # Requires supporting ruby files with custom matchers and macros, etc,
18
+ # in spec/support/ and its subdirectories.
19
+ Dir['./spec/support/**/*.rb'].each { |f| require f }
20
+
21
+ RSpec.configure do |config|
22
+ config.treat_symbols_as_metadata_keys_with_true_values = true
23
+ config.run_all_when_everything_filtered = true
24
+ config.filter_run :focus
25
+
26
+ # Run specs in random order to surface order dependencies. If you find an
27
+ # order dependency and want to debug it, you can fix the order by providing
28
+ # the seed, which is printed after each run.
29
+ # --seed 1234
30
+ config.order = 'random'
31
+ end
@@ -0,0 +1,88 @@
1
+ shared_examples 'an object with chain_fetch method' do
2
+ describe :chain_fetch do
3
+ let(:value) { 10 }
4
+ let(:hash) do
5
+ {
6
+ b: 1, c: 2, d: 3, a: {
7
+ c: 3, d: 5, b: {
8
+ d: 6, a: 1, b: 2, c: {
9
+ d: value
10
+ }
11
+ }
12
+ }
13
+ }
14
+ end
15
+ let(:keys) { [:a, :b, :c, :d] }
16
+ let(:result) { hash.chain_fetch(*keys) }
17
+
18
+ context 'when fetching existing keys' do
19
+ it 'returns the value' do
20
+ expect(result).to eq(value)
21
+ end
22
+ end
23
+
24
+ context 'when fetching non existing keys keys' do
25
+ let(:keys) { [:a, :x, :y] }
26
+
27
+ context 'when there is no default value' do
28
+ it 'raises fetch error' do
29
+ expect { result }.to raise_error(KeyError)
30
+ end
31
+
32
+ context 'when the hash has no keys' do
33
+ let(:hash) { {} }
34
+ let(:keys) { [:a] }
35
+
36
+ it 'raises fetch error' do
37
+ expect { result }.to raise_error(KeyError)
38
+ end
39
+ end
40
+
41
+ context 'with a simple level hash' do
42
+ let(:hash) { { a: 1 } }
43
+ let(:keys) { [:c] }
44
+
45
+ it 'raises fetch error' do
46
+ expect { result }.to raise_error(KeyError)
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'but a default value block is given' do
52
+ let(:default_value) { 100 }
53
+ let(:result) { hash.chain_fetch(*keys) { default_value } }
54
+
55
+ it 'returns the default_value' do
56
+ expect(result).to eq(default_value)
57
+ end
58
+
59
+ context 'and the block logs the missing keys' do
60
+ it 'hnadles the missing keys' do
61
+ missing_keys = nil
62
+ hash.chain_fetch(*keys) do |_, keys|
63
+ missing_keys = keys
64
+ end
65
+
66
+ expect(missing_keys).to eq([:y])
67
+ end
68
+ end
69
+
70
+ context 'and the block uses the key for the return' do
71
+ let(:result) { hash.chain_fetch(*keys) { |k| "returned #{k}" } }
72
+ it 'hnadles the missing keys' do
73
+ expect(result).to eq('returned x')
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ context 'when mixing key types' do
80
+ let(:hash) { { a: { 'b' => { 100 => { true => value } } } } }
81
+ let(:keys) { [:a, 'b', 100, true] }
82
+
83
+ it 'returns the value' do
84
+ expect(result).to eq(value)
85
+ end
86
+ end
87
+ end
88
+ end