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.
@@ -0,0 +1,3 @@
1
+ class Bihash
2
+ VERSION = '0.1.0'
3
+ end
@@ -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