bihash 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +25 -0
- data/Rakefile +10 -0
- data/bihash.gemspec +24 -0
- data/bin/console +6 -0
- data/bin/setup +6 -0
- data/lib/bihash.rb +311 -0
- data/lib/bihash/version.rb +3 -0
- data/spec/bihash_spec.rb +782 -0
- data/spec/spec_helper.rb +5 -0
- metadata +114 -0
data/spec/bihash_spec.rb
ADDED
@@ -0,0 +1,782 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bihash do
|
4
|
+
it 'should be enumerable' do
|
5
|
+
Bihash.must_include Enumerable
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '::[]' do
|
9
|
+
it 'should be able to create an empty bihash' do
|
10
|
+
bh = Bihash[]
|
11
|
+
bh.must_be_instance_of Bihash
|
12
|
+
bh.must_be_empty
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should convert a hash to a bihash' do
|
16
|
+
bh = Bihash[:key => 'value']
|
17
|
+
bh.must_be_instance_of Bihash
|
18
|
+
bh[:key].must_equal 'value'
|
19
|
+
bh['value'].must_equal :key
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should not accept a hash with duplicate values' do
|
23
|
+
-> { Bihash[:k1 => 'val', :k2 => 'val'] }.must_raise ArgumentError
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should not accept a hash that would result in ambiguous mappings' do
|
27
|
+
-> { Bihash[1, 2, 2, 3] }.must_raise ArgumentError
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should accept a hash where a key equals its value' do
|
31
|
+
bh = Bihash[:key => :key]
|
32
|
+
bh.must_be_instance_of Bihash
|
33
|
+
bh[:key].must_equal :key
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should always return the value object if key-value pairs are equal' do
|
37
|
+
key, value = [], []
|
38
|
+
bh = Bihash[key => value]
|
39
|
+
bh.must_be_instance_of Bihash
|
40
|
+
bh[key].object_id.must_equal value.object_id
|
41
|
+
bh[value].object_id.must_equal value.object_id
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should accept an even number of arguments' do
|
45
|
+
bh = Bihash[:k1, 1, :k2, 2]
|
46
|
+
bh.must_be_instance_of Bihash
|
47
|
+
bh[:k1].must_equal 1
|
48
|
+
bh[:k2].must_equal 2
|
49
|
+
bh[1].must_equal :k1
|
50
|
+
bh[2].must_equal :k2
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should accept an array of key-value pairs packaged in arrays' do
|
54
|
+
array = [[:k1, 1], [:k2, 2]]
|
55
|
+
bh = Bihash[array]
|
56
|
+
bh.must_be_instance_of Bihash
|
57
|
+
bh[:k1].must_equal 1
|
58
|
+
bh[:k2].must_equal 2
|
59
|
+
bh[1].must_equal :k1
|
60
|
+
bh[2].must_equal :k2
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '::new' do
|
65
|
+
it 'should create an empty bihash with a default of nil if no args' do
|
66
|
+
bh = Bihash.new
|
67
|
+
bh.must_be_instance_of Bihash
|
68
|
+
bh.must_be_empty
|
69
|
+
bh[:not_a_key].must_equal nil
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should create an empty bihash with a default if given an object arg' do
|
73
|
+
bh = Bihash.new('default')
|
74
|
+
bh.must_be_instance_of Bihash
|
75
|
+
bh.must_be_empty
|
76
|
+
bh[:not_a_key].must_equal 'default'
|
77
|
+
bh[:not_a_key].tr!('ealt', '3417')
|
78
|
+
bh[:still_not_a_key].must_equal 'd3f4u17'
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should create an empty bihash with a default if given a block arg' do
|
82
|
+
bh = Bihash.new { 'd3f4u17' }
|
83
|
+
bh.must_be_instance_of Bihash
|
84
|
+
bh.must_be_empty
|
85
|
+
bh[:not_a_key].must_equal 'd3f4u17'
|
86
|
+
bh[:not_a_key].tr!('3417', 'ealt')
|
87
|
+
bh[:not_a_key].must_equal 'd3f4u17'
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should allow assignment of new pairs if given a block arg' do
|
91
|
+
bh = Bihash.new { |bihash, key| bihash[key] = key.to_s }
|
92
|
+
bh[404].must_equal '404'
|
93
|
+
bh.size.must_equal 1
|
94
|
+
bh.must_include 404
|
95
|
+
bh.must_include '404'
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should not accept both an object and a block' do
|
99
|
+
-> { Bihash.new('default 1') { 'default 2' } }.must_raise ArgumentError
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '::try_convert' do
|
104
|
+
it 'should convert an object to a bihash if it responds to #to_hash' do
|
105
|
+
hash = {:k1 => 1, :k2 => 2}
|
106
|
+
bh = Bihash.try_convert(hash)
|
107
|
+
bh.must_be_instance_of Bihash
|
108
|
+
bh[:k1].must_equal 1
|
109
|
+
bh[:k2].must_equal 2
|
110
|
+
bh[1].must_equal :k1
|
111
|
+
bh[2].must_equal :k2
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should return nil if the object does not respond to #to_hash' do
|
115
|
+
Bihash.try_convert(Object.new).must_equal nil
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should not accept a hash with duplicate values' do
|
119
|
+
-> { Bihash.try_convert(:k1 => 1, :k2 => 1) }.must_raise ArgumentError
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe '#==' do
|
124
|
+
it 'should return true when two bihashes have the same pairs' do
|
125
|
+
bh1, bh2 = Bihash[:k1 => 1, :k2 => 2], Bihash[2 => :k2, 1 => :k1]
|
126
|
+
(bh1 == bh2).must_equal true
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should return false when two bihashes do not have the same pairs' do
|
130
|
+
bh1, bh2 = Bihash[:k1 => 1, :k2 => 2], Bihash[:k1 => 1, :k2 => 99]
|
131
|
+
(bh1 == bh2).must_equal false
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should be aliased to #eql?' do
|
135
|
+
bh = Bihash.new
|
136
|
+
bh.method(:eql?).must_equal bh.method(:==)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe '#[]' do
|
141
|
+
it 'should return the other pair' do
|
142
|
+
bh = Bihash[:key => 'value']
|
143
|
+
bh[:key].must_equal 'value'
|
144
|
+
bh['value'].must_equal :key
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'should return falsey values correctly' do
|
148
|
+
bh1 = Bihash[nil => false]
|
149
|
+
bh1[nil].must_equal false
|
150
|
+
bh1[false].must_equal nil
|
151
|
+
|
152
|
+
bh2 = Bihash[false => nil]
|
153
|
+
bh2[false].must_equal nil
|
154
|
+
bh2[nil].must_equal false
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe '#[]=' do
|
159
|
+
it 'should allow assignment of new pairs' do
|
160
|
+
bh = Bihash.new
|
161
|
+
bh[:key] = 'value'
|
162
|
+
bh[:key].must_equal 'value'
|
163
|
+
bh['value'].must_equal :key
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should remove old pairs if old keys are re-assigned' do
|
167
|
+
bh = Bihash[1 => 'one', 2 => 'two']
|
168
|
+
bh[1] = 'uno'
|
169
|
+
bh[1].must_equal 'uno'
|
170
|
+
bh['uno'].must_equal 1
|
171
|
+
bh.wont_include 'one'
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'should always return the value object if key-value pairs are equal' do
|
175
|
+
key, value = [], []
|
176
|
+
bh = Bihash.new
|
177
|
+
bh[key] = value
|
178
|
+
bh[key].object_id.must_equal value.object_id
|
179
|
+
bh[value].object_id.must_equal value.object_id
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'should be aliased to #store' do
|
183
|
+
bh = Bihash.new
|
184
|
+
bh.method(:store).must_equal bh.method(:[]=)
|
185
|
+
bh.store(:key, 'value')
|
186
|
+
bh[:key].must_equal 'value'
|
187
|
+
bh['value'].must_equal :key
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
191
|
+
-> { Bihash.new.freeze[:key] = 'value' }.must_raise RuntimeError
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe '#assoc' do
|
196
|
+
it 'should return the pair if the argument is a key' do
|
197
|
+
bh = Bihash[:k1 => 'v1', :k2 => 'v2']
|
198
|
+
bh.assoc(:k1).must_equal [:k1, 'v1']
|
199
|
+
bh.assoc('v2').must_equal ['v2', :k2]
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'should return nil if the argument is not a key' do
|
203
|
+
bh = Bihash.new(404)
|
204
|
+
bh.assoc(:not_a_key).must_equal nil
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'should find the key using #==' do
|
208
|
+
bh = Bihash[[] => 'array']
|
209
|
+
bh['array'] << 'modified'
|
210
|
+
bh.assoc(['modified']).must_equal [['modified'], 'array']
|
211
|
+
bh.assoc([]).must_equal nil
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe '#clear' do
|
216
|
+
it 'should remove all pairs and return the bihash' do
|
217
|
+
bh = Bihash[:key => 'value']
|
218
|
+
bh.clear.object_id.must_equal bh.object_id
|
219
|
+
bh.must_be_empty
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
223
|
+
-> { Bihash.new.freeze.clear }.must_raise RuntimeError
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
describe '#clone' do
|
228
|
+
it 'should make a copy of the bihash' do
|
229
|
+
bh = Bihash[1 => :one]
|
230
|
+
clone = bh.clone
|
231
|
+
clone[2] = :two
|
232
|
+
bh[2].must_equal nil
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe '#compare_by_identity' do
|
237
|
+
it 'should set bihash to compare by identity instead of equality' do
|
238
|
+
bh = Bihash.new.compare_by_identity
|
239
|
+
key1, key2 = 'key', 'value'
|
240
|
+
bh[key1] = key2
|
241
|
+
bh['key'].must_equal nil
|
242
|
+
bh['value'].must_equal nil
|
243
|
+
bh[key1].must_equal 'value'
|
244
|
+
bh[key2].must_equal 'key'
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
248
|
+
-> { Bihash.new.freeze.compare_by_identity }.must_raise RuntimeError
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
describe '#compare_by_identity?' do
|
253
|
+
it 'should indicate whether bihash is comparing by identity' do
|
254
|
+
Bihash.new.compare_by_identity.compare_by_identity?.must_equal true
|
255
|
+
Bihash.new.compare_by_identity?.must_equal false
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe '#default' do
|
260
|
+
it 'should not accept more than one argument' do
|
261
|
+
-> { Bihash.new.default(1,2) }.must_raise ArgumentError
|
262
|
+
end
|
263
|
+
|
264
|
+
describe 'when there is not a default proc' do
|
265
|
+
it 'should return the default' do
|
266
|
+
bh1 = Bihash[:key => 'value']
|
267
|
+
bh1.default.must_equal nil
|
268
|
+
bh1.default(:not_a_key).must_equal nil
|
269
|
+
bh1.default(:key).must_equal nil
|
270
|
+
|
271
|
+
bh2 = Bihash.new(404)
|
272
|
+
bh2[:key] = 'value'
|
273
|
+
bh2.default.must_equal 404
|
274
|
+
bh2.default(:not_a_key).must_equal 404
|
275
|
+
bh2.default(:key).must_equal 404
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
describe 'when there is a default proc' do
|
280
|
+
it 'should return the default if called with no argument' do
|
281
|
+
Bihash.new { 'proc called' }.default.must_equal nil
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'should call the default proc when called with an argument' do
|
285
|
+
bh = Bihash.new { |bihash, key| bihash[key] = key.to_s }
|
286
|
+
bh[:key] = 'value'
|
287
|
+
|
288
|
+
bh.default(:key).must_equal 'key'
|
289
|
+
bh[:key].must_equal 'key'
|
290
|
+
|
291
|
+
bh.default(404).must_equal '404'
|
292
|
+
bh[404].must_equal '404'
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe '#default=' do
|
298
|
+
it 'should set the default object' do
|
299
|
+
bh = Bihash.new { 'proc called' }
|
300
|
+
bh[:not_a_key].must_equal 'proc called'
|
301
|
+
(bh.default = 404).must_equal 404
|
302
|
+
bh[:not_a_key].must_equal 404
|
303
|
+
end
|
304
|
+
|
305
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
306
|
+
-> { Bihash.new.freeze.default = 404 }.must_raise RuntimeError
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
describe '#default_proc' do
|
311
|
+
it 'should return the default proc if it exists' do
|
312
|
+
bh = Bihash.new { |bihash, key| bihash[key] = key }
|
313
|
+
prc = bh.default_proc
|
314
|
+
array = []
|
315
|
+
prc.call(array, 2)
|
316
|
+
array.must_equal [nil, nil, 2]
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'should return nil if there is no default proc' do
|
320
|
+
Bihash.new.default_proc.must_equal nil
|
321
|
+
Bihash.new(404).default_proc.must_equal nil
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
describe '#default_proc=' do
|
326
|
+
it 'should set the default proc' do
|
327
|
+
bh = Bihash.new(:default_object)
|
328
|
+
bh[:not_a_key].must_equal :default_object
|
329
|
+
(bh.default_proc = ->(bihash, key) { '404' }).must_be_instance_of Proc
|
330
|
+
bh[:not_a_key].must_equal '404'
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'should set the default value to nil if argument is nil' do
|
334
|
+
bh = Bihash.new(:default_object)
|
335
|
+
bh[:not_a_key].must_equal :default_object
|
336
|
+
(bh.default_proc = nil).must_equal nil
|
337
|
+
bh[:not_a_key].must_equal nil
|
338
|
+
end
|
339
|
+
|
340
|
+
it 'should raise TypeError if not given a non-proc (except nil)' do
|
341
|
+
-> { Bihash.new.default_proc = :not_a_proc }.must_raise TypeError
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'should raise TypeError given a lambda without 2 args' do
|
345
|
+
-> { Bihash.new.default_proc = -> { '404' } }.must_raise TypeError
|
346
|
+
end
|
347
|
+
|
348
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
349
|
+
-> { Bihash[].freeze.default_proc = proc { '' } }.must_raise RuntimeError
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
describe '#delete' do
|
354
|
+
it 'should return the other key if the given key is found' do
|
355
|
+
Bihash[:key => 'value'].delete(:key).must_equal 'value'
|
356
|
+
Bihash[:key => 'value'].delete('value').must_equal :key
|
357
|
+
end
|
358
|
+
|
359
|
+
it 'should remove both keys' do
|
360
|
+
bh1 = Bihash[:key => 'value']
|
361
|
+
bh1.delete(:key)
|
362
|
+
bh1.wont_include :key
|
363
|
+
bh1.wont_include 'value'
|
364
|
+
|
365
|
+
bh2 = Bihash[:key => 'value']
|
366
|
+
bh2.delete('value')
|
367
|
+
bh2.wont_include :key
|
368
|
+
bh2.wont_include 'value'
|
369
|
+
end
|
370
|
+
|
371
|
+
it 'should call the block (if given) when the key is not found' do
|
372
|
+
out = Bihash[:key => 'value'].delete(404) { |key| "#{key} not found" }
|
373
|
+
out.must_equal '404 not found'
|
374
|
+
end
|
375
|
+
|
376
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
377
|
+
-> { Bihash.new.freeze.delete(:key) }.must_raise RuntimeError
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
describe '#delete_if' do
|
382
|
+
it 'should delete any pairs for which the block evaluates to true' do
|
383
|
+
bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
384
|
+
bh_id = bh.object_id
|
385
|
+
bh.delete_if { |key1, key2| key1.even? }.object_id.must_equal bh_id
|
386
|
+
bh.must_equal Bihash[1 => :one, 3 => :three]
|
387
|
+
end
|
388
|
+
|
389
|
+
it 'should raise RuntimeError if called on a frozen bihash with a block' do
|
390
|
+
-> { Bihash.new.freeze.delete_if { false } }.must_raise RuntimeError
|
391
|
+
end
|
392
|
+
|
393
|
+
it 'should return an enumerator if not given a block' do
|
394
|
+
enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].delete_if
|
395
|
+
enum.must_be_instance_of Enumerator
|
396
|
+
enum.each { |k1, k2| k1.even? }.must_equal Bihash[1 => :one, 3 => :three]
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
describe '#dup' do
|
401
|
+
it 'should make a copy of the bihash' do
|
402
|
+
bh = Bihash[1 => :one]
|
403
|
+
dup = bh.dup
|
404
|
+
dup[2] = :two
|
405
|
+
bh[2].must_equal nil
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
describe '#each' do
|
410
|
+
it 'should iterate over each pair in the bihash' do
|
411
|
+
array = []
|
412
|
+
Bihash[:k1 => 'v1', :k2 => 'v2'].each { |pair| array << pair }
|
413
|
+
array.must_equal [[:k1, 'v1'], [:k2, 'v2']]
|
414
|
+
end
|
415
|
+
|
416
|
+
it 'should return the bihash if given a block' do
|
417
|
+
bh = Bihash.new
|
418
|
+
bh.each { |p| }.must_be_instance_of Bihash
|
419
|
+
bh.each { |p| }.object_id.must_equal bh.object_id
|
420
|
+
end
|
421
|
+
|
422
|
+
it 'should return an enumerator if not given a block' do
|
423
|
+
enum = Bihash[:k1 => 'v1', :k2 => 'v2'].each
|
424
|
+
enum.must_be_instance_of Enumerator
|
425
|
+
enum.each { |pair| pair }.must_equal Bihash[:k1 => 'v1', :k2 => 'v2']
|
426
|
+
end
|
427
|
+
|
428
|
+
it 'should be aliased to #each_pair' do
|
429
|
+
bh = Bihash.new
|
430
|
+
bh.method(:each_pair).must_equal bh.method(:each)
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
describe '#empty?' do
|
435
|
+
it 'should indicate if the bihash is empty' do
|
436
|
+
Bihash.new.empty?.must_equal true
|
437
|
+
Bihash[:key => 'value'].empty?.must_equal false
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
describe '#fetch' do
|
442
|
+
it 'should return the other pair' do
|
443
|
+
bh = Bihash[:key => 'value']
|
444
|
+
bh.fetch(:key).must_equal 'value'
|
445
|
+
bh.fetch('value').must_equal :key
|
446
|
+
end
|
447
|
+
|
448
|
+
it 'should return falsey values correctly' do
|
449
|
+
bh1 = Bihash[nil => false]
|
450
|
+
bh1.fetch(nil).must_equal false
|
451
|
+
bh1.fetch(false).must_equal nil
|
452
|
+
|
453
|
+
bh2 = Bihash[false => nil]
|
454
|
+
bh2.fetch(false).must_equal nil
|
455
|
+
bh2.fetch(nil).must_equal false
|
456
|
+
end
|
457
|
+
|
458
|
+
describe 'when the key is not found' do
|
459
|
+
it 'should raise KeyError when not supplied any default' do
|
460
|
+
-> { Bihash[].fetch(:not_a_key) }.must_raise KeyError
|
461
|
+
end
|
462
|
+
|
463
|
+
it 'should return the second arg when supplied with one' do
|
464
|
+
Bihash[].fetch(:not_a_key, :second_arg).must_equal :second_arg
|
465
|
+
end
|
466
|
+
|
467
|
+
it 'should call the block if supplied with one' do
|
468
|
+
Bihash[].fetch(404) { |k| "#{k} not found" }.must_equal '404 not found'
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
describe '#flatten' do
|
474
|
+
it 'extract the pairs into an array' do
|
475
|
+
Bihash[:k1 => 'v1', :k2 => 'v2'].flatten.must_equal [:k1, 'v1', :k2, 'v2']
|
476
|
+
end
|
477
|
+
|
478
|
+
it 'should not flatten array keys if no argument is given' do
|
479
|
+
Bihash[:key => ['v1', 'v2']].flatten.must_equal [:key, ['v1', 'v2']]
|
480
|
+
end
|
481
|
+
|
482
|
+
it 'should flatten to the level given as an argument' do
|
483
|
+
Bihash[:key => ['v1', 'v2']].flatten(2).must_equal [:key, 'v1', 'v2']
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
describe '#hash' do
|
488
|
+
it 'should return the same hash code if two bihashes have the same pairs' do
|
489
|
+
bh1, bh2 = Bihash[:k1 => 1, :k2 => 2], Bihash[2 => :k2, 1 => :k1]
|
490
|
+
bh1.hash.must_equal bh2.hash
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
describe '#include?' do
|
495
|
+
it 'should indicate if the bihash contains the argument' do
|
496
|
+
bh = Bihash[:key => 'value']
|
497
|
+
bh.include?(:key).must_equal true
|
498
|
+
bh.include?('value').must_equal true
|
499
|
+
bh.include?(:not_a_key).must_equal false
|
500
|
+
end
|
501
|
+
|
502
|
+
it 'should be aliased to #has_key?, #key?, and #member?' do
|
503
|
+
bh = Bihash.new
|
504
|
+
bh.method(:has_key?).must_equal bh.method(:include?)
|
505
|
+
bh.method(:key?).must_equal bh.method(:include?)
|
506
|
+
bh.method(:member?).must_equal bh.method(:include?)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
describe '#keep_if' do
|
511
|
+
it 'should retain any pairs for which the block evaluates to true' do
|
512
|
+
bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
513
|
+
bh_id = bh.object_id
|
514
|
+
bh.keep_if { |key1, key2| key1.even? }.object_id.must_equal bh_id
|
515
|
+
bh.must_equal Bihash[2 => :two, 4 => :four]
|
516
|
+
end
|
517
|
+
|
518
|
+
it 'should raise RuntimeError if called on a frozen bihash with a block' do
|
519
|
+
-> { Bihash.new.freeze.keep_if { true } }.must_raise RuntimeError
|
520
|
+
end
|
521
|
+
|
522
|
+
it 'should return an enumerator if not given a block' do
|
523
|
+
enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].keep_if
|
524
|
+
enum.must_be_instance_of Enumerator
|
525
|
+
enum.each { |k1, k2| k1.even? }.must_equal Bihash[2 => :two, 4 => :four]
|
526
|
+
end
|
527
|
+
end
|
528
|
+
|
529
|
+
describe '#length' do
|
530
|
+
it 'should return the number of pairs in the bihash' do
|
531
|
+
Bihash[1 => :one, 2 => :two].length.must_equal 2
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
describe '#merge' do
|
536
|
+
it 'should merge bihashes, assigning each arg pair to a copy of reciever' do
|
537
|
+
receiver = Bihash[:chips => :salsa, :milk => :cookies, :fish => :rice]
|
538
|
+
original_receiver = receiver.dup
|
539
|
+
argument = Bihash[:fish => :chips, :soup => :salad]
|
540
|
+
return_value = Bihash[:milk => :cookies, :fish => :chips, :soup => :salad]
|
541
|
+
receiver.merge(argument).must_equal return_value
|
542
|
+
receiver.must_equal original_receiver
|
543
|
+
end
|
544
|
+
|
545
|
+
it 'should raise TypeError if arg is not a bihash' do
|
546
|
+
-> { Bihash.new.merge({:key => 'value'}) }.must_raise TypeError
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
describe '#merge!' do
|
551
|
+
it 'should merge bihashes, assigning each arg pair to the receiver' do
|
552
|
+
receiver = Bihash[:chips => :salsa, :milk => :cookies, :fish => :rice]
|
553
|
+
argument = Bihash[:fish => :chips, :soup => :salad]
|
554
|
+
return_value = Bihash[:milk => :cookies, :fish => :chips, :soup => :salad]
|
555
|
+
receiver.merge!(argument).must_equal return_value
|
556
|
+
receiver.must_equal return_value
|
557
|
+
end
|
558
|
+
|
559
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
560
|
+
-> { Bihash.new.freeze.merge!(Bihash.new) }.must_raise RuntimeError
|
561
|
+
end
|
562
|
+
|
563
|
+
it 'should raise TypeError if arg is not a bihash' do
|
564
|
+
-> { Bihash.new.merge!({:key => 'value'}) }.must_raise TypeError
|
565
|
+
end
|
566
|
+
|
567
|
+
it 'should be aliased to #update' do
|
568
|
+
bh = Bihash.new
|
569
|
+
bh.method(:update).must_equal bh.method(:merge!)
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
describe '#rehash' do
|
574
|
+
it 'should recompute all key hash values and return the bihash' do
|
575
|
+
bh = Bihash[[] => :array]
|
576
|
+
bh[:array] << 1
|
577
|
+
bh[[1]].must_equal nil
|
578
|
+
bh.rehash[[1]].must_equal :array
|
579
|
+
bh[[1]].must_equal :array
|
580
|
+
end
|
581
|
+
|
582
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
583
|
+
-> { Bihash.new.freeze.rehash }.must_raise RuntimeError
|
584
|
+
end
|
585
|
+
|
586
|
+
it 'should raise RuntimeError if called when key duplicated outside pair' do
|
587
|
+
bh = Bihash[[1], [2], [3], [4]]
|
588
|
+
(bh[[4]] << 1).shift
|
589
|
+
-> { bh.rehash }.must_raise RuntimeError
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
describe '#reject' do
|
594
|
+
describe 'should return a bihash with items not rejected by the block' do
|
595
|
+
it 'when some items are rejected' do
|
596
|
+
bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
597
|
+
bh.reject { |k1,k2| k1.even? }.must_equal Bihash[1 => :one, 3 => :three]
|
598
|
+
bh.must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
599
|
+
end
|
600
|
+
|
601
|
+
it 'when no items are rejected' do
|
602
|
+
bh = Bihash[1 => :one, 3 => :three, 5 => :five, 7 => :seven]
|
603
|
+
bh.reject { |k1,k2| k1.even? }.must_equal bh
|
604
|
+
bh.must_equal bh
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
it 'should return an enumerator if not given a block' do
|
609
|
+
enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].reject
|
610
|
+
enum.must_be_instance_of Enumerator
|
611
|
+
enum.each { |k1,k2| k1.even? }.must_equal Bihash[1 => :one, 3 => :three]
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
describe '#reject!' do
|
616
|
+
it 'should delete any pairs for which the block evaluates to true' do
|
617
|
+
bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
618
|
+
bh_id = bh.object_id
|
619
|
+
bh.reject! { |key1, key2| key1.even? }.object_id.must_equal bh_id
|
620
|
+
bh.must_equal Bihash[1 => :one, 3 => :three]
|
621
|
+
end
|
622
|
+
|
623
|
+
it 'should return nil if no changes were made to the bihash' do
|
624
|
+
bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
625
|
+
bh.reject! { |key1, key2| key1 > 5 }.must_equal nil
|
626
|
+
bh.must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
627
|
+
end
|
628
|
+
|
629
|
+
it 'should raise RuntimeError if called on a frozen bihash with a block' do
|
630
|
+
-> { Bihash.new.freeze.reject! { false } }.must_raise RuntimeError
|
631
|
+
end
|
632
|
+
|
633
|
+
it 'should return an enumerator if not given a block' do
|
634
|
+
enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].reject!
|
635
|
+
enum.must_be_instance_of Enumerator
|
636
|
+
enum.each { |k1, k2| k1.even? }.must_equal Bihash[1 => :one, 3 => :three]
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
describe '#replace' do
|
641
|
+
it 'should replace the contents of receiver with the contents of the arg' do
|
642
|
+
receiver = Bihash[]
|
643
|
+
original_id = receiver.object_id
|
644
|
+
arg = Bihash[:key => 'value']
|
645
|
+
receiver.replace(arg).must_equal Bihash[:key => 'value']
|
646
|
+
arg[:another_key] = 'another_value'
|
647
|
+
receiver.object_id.must_equal original_id
|
648
|
+
receiver.must_equal Bihash[:key => 'value']
|
649
|
+
end
|
650
|
+
|
651
|
+
it 'should raise TypeError if arg is not a bihash' do
|
652
|
+
-> { Bihash.new.replace({:key => 'value'}) }.must_raise TypeError
|
653
|
+
end
|
654
|
+
|
655
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
656
|
+
-> { Bihash.new.freeze.replace(Bihash[:k, 'v']) }.must_raise RuntimeError
|
657
|
+
end
|
658
|
+
end
|
659
|
+
|
660
|
+
describe '#select' do
|
661
|
+
describe 'should return a bihash with items selected by the block' do
|
662
|
+
it 'when only some items are selected' do
|
663
|
+
bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
664
|
+
bh.select { |k1,k2| k1.even? }.must_equal Bihash[2 => :two, 4 => :four]
|
665
|
+
bh.must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
666
|
+
end
|
667
|
+
|
668
|
+
it 'when all items are selected' do
|
669
|
+
bh = Bihash[2 => :two, 4 => :four, 6 => :six, 8 => :eight]
|
670
|
+
bh.select { |k1,k2| k1.even? }.must_equal bh
|
671
|
+
bh.must_equal bh
|
672
|
+
end
|
673
|
+
end
|
674
|
+
|
675
|
+
it 'should return an enumerator if not given a block' do
|
676
|
+
enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].select
|
677
|
+
enum.must_be_instance_of Enumerator
|
678
|
+
enum.each { |k1,k2| k1.even? }.must_equal Bihash[2 => :two, 4 => :four]
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
describe '#select!' do
|
683
|
+
it 'should retain any pairs for which the block evaluates to true' do
|
684
|
+
bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
685
|
+
bh_id = bh.object_id
|
686
|
+
bh.select! { |key1, key2| key1.even? }.object_id.must_equal bh_id
|
687
|
+
bh.must_equal Bihash[2 => :two, 4 => :four]
|
688
|
+
end
|
689
|
+
|
690
|
+
it 'should return nil if no changes were made to the bihash' do
|
691
|
+
bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
692
|
+
bh.select! { |key1, key2| key1 < 5 }.must_equal nil
|
693
|
+
bh.must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
|
694
|
+
end
|
695
|
+
|
696
|
+
it 'should raise RuntimeError if called on a frozen bihash with a block' do
|
697
|
+
-> { Bihash.new.freeze.select! { true } }.must_raise RuntimeError
|
698
|
+
end
|
699
|
+
|
700
|
+
it 'should return an enumerator if not given a block' do
|
701
|
+
enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].select!
|
702
|
+
enum.must_be_instance_of Enumerator
|
703
|
+
enum.each { |k1, k2| k1.even? }.must_equal Bihash[2 => :two, 4 => :four]
|
704
|
+
end
|
705
|
+
end
|
706
|
+
|
707
|
+
describe '#shift' do
|
708
|
+
it 'should remove the oldest pair from the bihash and return it' do
|
709
|
+
bh = Bihash[1 => :one, 2 => :two, 3 => :three]
|
710
|
+
bh.shift.must_equal [1, :one]
|
711
|
+
bh.must_equal Bihash[2 => :two, 3 => :three]
|
712
|
+
end
|
713
|
+
|
714
|
+
it 'should return the default value if bihash is empty' do
|
715
|
+
Bihash.new.shift.must_equal nil
|
716
|
+
Bihash.new(404).shift.must_equal 404
|
717
|
+
Bihash.new { 'd3f4u17' }.shift.must_equal 'd3f4u17'
|
718
|
+
end
|
719
|
+
|
720
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
721
|
+
-> { Bihash.new.freeze.shift }.must_raise RuntimeError
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
describe '#size' do
|
726
|
+
it 'should return the number of pairs in the bihash' do
|
727
|
+
Bihash[1 => :one, 2 => :two].size.must_equal 2
|
728
|
+
end
|
729
|
+
end
|
730
|
+
|
731
|
+
describe '#to_h' do
|
732
|
+
it 'should return a copy of the forward hash' do
|
733
|
+
bh = Bihash[:key1 => 'val1', :key2 => 'val2']
|
734
|
+
h = bh.to_h
|
735
|
+
h.must_equal Hash[:key1 => 'val1', :key2 => 'val2']
|
736
|
+
h.delete(:key1)
|
737
|
+
bh.must_include :key1
|
738
|
+
end
|
739
|
+
end
|
740
|
+
|
741
|
+
describe '#to_s' do
|
742
|
+
it 'should return a nice string representing the bihash' do
|
743
|
+
bh = Bihash[:k1 => 'v1', :k2 => [:v2], :k3 => {:k4 => 'v4'}]
|
744
|
+
bh.to_s.must_equal 'Bihash[:k1=>"v1", :k2=>[:v2], :k3=>{:k4=>"v4"}]'
|
745
|
+
end
|
746
|
+
|
747
|
+
it 'should be aliased to #inspect' do
|
748
|
+
bh = Bihash.new
|
749
|
+
bh.method(:inspect).must_equal bh.method(:to_s)
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
753
|
+
describe '#values_at' do
|
754
|
+
it 'should return an array of values corresponding to the given keys' do
|
755
|
+
Bihash[1 => :one, 2 => :two].values_at(1, 2).must_equal [:one, :two]
|
756
|
+
Bihash[1 => :one, 2 => :two].values_at(:one, :two).must_equal [1, 2]
|
757
|
+
Bihash[1 => :one, 2 => :two].values_at(1, :two).must_equal [:one, 2]
|
758
|
+
end
|
759
|
+
|
760
|
+
it 'should use the default if a given key is not found' do
|
761
|
+
bh = Bihash.new(404)
|
762
|
+
bh[1] = :one
|
763
|
+
bh[2] = :two
|
764
|
+
bh.values_at(1, 2, 3).must_equal [:one, :two, 404]
|
765
|
+
bh.values_at(:one, :two, :three).must_equal [1, 2, 404]
|
766
|
+
end
|
767
|
+
|
768
|
+
it 'should not duplicate entries if a key equals its value' do
|
769
|
+
Bihash[:key => :key].values_at(:key).must_equal [:key]
|
770
|
+
end
|
771
|
+
|
772
|
+
it 'should return an empty array with no args' do
|
773
|
+
Bihash[:key => 'value'].values_at.must_equal []
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
describe '#initialize' do
|
778
|
+
it 'should raise RuntimeError if called on a frozen bihash' do
|
779
|
+
-> { Bihash.new.freeze.send(:initialize) }.must_raise RuntimeError
|
780
|
+
end
|
781
|
+
end
|
782
|
+
end
|