bihash 0.1.0

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