bihash 1.2.0 → 2.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.
data/spec/bihash_spec.rb CHANGED
@@ -2,244 +2,380 @@ require 'spec_helper'
2
2
 
3
3
  describe Bihash do
4
4
  it 'should be enumerable' do
5
- Bihash.must_include Enumerable
5
+ _(Bihash).must_include Enumerable
6
+ end
7
+
8
+ Bihash::UNIMPLEMENTED_CLASS_METHODS.each do |method|
9
+ it "should report that it does not respond to ::#{method}" do
10
+ _(Bihash.respond_to?(method)).must_equal false
11
+ end
12
+
13
+ it "should raise NoMethodError if ::#{method} is called" do
14
+ error = _(-> { Bihash.send(method) }).must_raise NoMethodError
15
+ _(error.message).must_equal "Bihash::#{method} not implemented"
16
+ end
6
17
  end
7
18
 
8
19
  Bihash::UNIMPLEMENTED_METHODS.each do |method|
9
20
  it "should report that it does not respond to ##{method}" do
10
- Bihash.new.respond_to?(method).must_equal false
21
+ _(Bihash.new.respond_to?(method)).must_equal false
11
22
  end
12
23
 
13
24
  it "should raise NoMethodError if ##{method} is called" do
14
- error = -> { Bihash.new.send(method) }.must_raise NoMethodError
15
- error.message.must_equal "Bihash##{method} not implemented"
25
+ error = _(-> { Bihash.new.send(method) }).must_raise NoMethodError
26
+ _(error.message).must_equal "Bihash##{method} not implemented"
16
27
  end
17
28
  end
18
29
 
19
30
  describe '::[]' do
20
31
  it 'should be able to create an empty bihash' do
21
32
  bh = Bihash[]
22
- bh.must_be_instance_of Bihash
23
- bh.must_be_empty
33
+ _(bh).must_be_instance_of Bihash
34
+ _(bh).must_be_empty
24
35
  end
25
36
 
26
37
  it 'should convert a hash to a bihash' do
27
38
  bh = Bihash[:key => 'value']
28
- bh.must_be_instance_of Bihash
29
- bh[:key].must_equal 'value'
30
- bh['value'].must_equal :key
39
+ _(bh).must_be_instance_of Bihash
40
+ _(bh[:key]).must_equal 'value'
41
+ _(bh['value']).must_equal :key
42
+ end
43
+
44
+ it 'should not carry over the default value' do
45
+ h = Hash.new(404)
46
+ bh = Bihash[h]
47
+ _(bh.default).must_be_nil
48
+ _(bh[:not_a_key]).must_be_nil
49
+ end
50
+
51
+ it 'should not carry over the default_proc' do
52
+ h = Hash.new { "#{_2} not found" }
53
+ bh = Bihash[h]
54
+ _(bh.default_proc).must_be_nil
55
+ _(bh[:not_a_key]).must_be_nil
56
+ end
57
+
58
+ it 'should not carry over compare_by_identity' do
59
+ h = Hash.new.compare_by_identity
60
+ h[Array.new] = :array
61
+ bh = Bihash[h]
62
+ _(bh.compare_by_identity?).must_equal false
63
+ _(bh[Array.new]).must_equal :array
31
64
  end
32
65
 
33
66
  it 'should not accept a hash with duplicate values' do
34
- -> { Bihash[:k1 => 'val', :k2 => 'val'] }.must_raise ArgumentError
67
+ _(-> { Bihash[:k1 => 'val', :k2 => 'val'] }).must_raise ArgumentError
35
68
  end
36
69
 
37
70
  it 'should not accept a hash that would result in ambiguous mappings' do
38
- -> { Bihash[1, 2, 2, 3] }.must_raise ArgumentError
71
+ _(-> { Bihash[1, 2, 2, 3] }).must_raise ArgumentError
72
+ end
73
+
74
+ it 'should not accept a hash that has ambiguous mappings that are ==' do
75
+ _(-> { Bihash[1, 1.0, 1.0, :anything]}).must_raise ArgumentError
39
76
  end
40
77
 
41
78
  it 'should accept a hash where a key equals its value' do
42
79
  bh = Bihash[:key => :key]
43
- bh.must_be_instance_of Bihash
44
- bh[:key].must_equal :key
80
+ _(bh).must_be_instance_of Bihash
81
+ _(bh[:key]).must_equal :key
45
82
  end
46
83
 
47
84
  it 'should always return the value object if key-value pairs are equal' do
48
85
  key, value = [], []
49
86
  bh = Bihash[key => value]
50
- bh.must_be_instance_of Bihash
51
- bh[key].object_id.must_equal value.object_id
52
- bh[value].object_id.must_equal value.object_id
87
+ _(bh).must_be_instance_of Bihash
88
+ _(bh[key].object_id).must_equal value.object_id
89
+ _(bh[value].object_id).must_equal value.object_id
53
90
  end
54
91
 
55
92
  it 'should accept an even number of arguments' do
56
93
  bh = Bihash[:k1, 1, :k2, 2]
57
- bh.must_be_instance_of Bihash
58
- bh[:k1].must_equal 1
59
- bh[:k2].must_equal 2
60
- bh[1].must_equal :k1
61
- bh[2].must_equal :k2
94
+ _(bh).must_be_instance_of Bihash
95
+ _(bh[:k1]).must_equal 1
96
+ _(bh[:k2]).must_equal 2
97
+ _(bh[1]).must_equal :k1
98
+ _(bh[2]).must_equal :k2
62
99
  end
63
100
 
64
101
  it 'should accept an array of key-value pairs packaged in arrays' do
65
102
  array = [[:k1, 1], [:k2, 2]]
66
103
  bh = Bihash[array]
67
- bh.must_be_instance_of Bihash
68
- bh[:k1].must_equal 1
69
- bh[:k2].must_equal 2
70
- bh[1].must_equal :k1
71
- bh[2].must_equal :k2
104
+ _(bh).must_be_instance_of Bihash
105
+ _(bh[:k1]).must_equal 1
106
+ _(bh[:k2]).must_equal 2
107
+ _(bh[1]).must_equal :k1
108
+ _(bh[2]).must_equal :k2
72
109
  end
73
110
  end
74
111
 
75
112
  describe '::new' do
76
113
  it 'should create an empty bihash with a default of nil if no args' do
77
114
  bh = Bihash.new
78
- bh.must_be_instance_of Bihash
79
- bh.must_be_empty
80
- bh[:not_a_key].must_be_nil
115
+ _(bh).must_be_instance_of Bihash
116
+ _(bh).must_be_empty
117
+ _(bh[:not_a_key]).must_be_nil
81
118
  end
82
119
 
83
120
  it 'should create an empty bihash with a default if given an object arg' do
84
- bh = Bihash.new('default')
85
- bh.must_be_instance_of Bihash
86
- bh.must_be_empty
87
- bh[:not_a_key].must_equal 'default'
88
- bh[:not_a_key].tr!('ealt', '3417')
89
- bh[:still_not_a_key].must_equal 'd3f4u17'
121
+ bh = Bihash.new(['default'])
122
+ _(bh).must_be_instance_of Bihash
123
+ _(bh).must_be_empty
124
+ _(bh[:not_a_key]).must_equal ['default']
125
+ bh[:not_a_key] << 'pwned'
126
+ _(bh[:still_not_a_key]).must_equal ['default', 'pwned']
90
127
  end
91
128
 
92
129
  it 'should create an empty bihash with a default if given a block arg' do
93
- bh = Bihash.new { 'd3f4u17' }
94
- bh.must_be_instance_of Bihash
95
- bh.must_be_empty
96
- bh[:not_a_key].must_equal 'd3f4u17'
97
- bh[:not_a_key].tr!('3417', 'ealt')
98
- bh[:not_a_key].must_equal 'd3f4u17'
130
+ bh = Bihash.new { ['default'] }
131
+ _(bh).must_be_instance_of Bihash
132
+ _(bh).must_be_empty
133
+ _(bh[:not_a_key]).must_equal ['default']
134
+ bh[:not_a_key] << '(not) pwned'
135
+ _(bh[:not_a_key]).must_equal ['default']
99
136
  end
100
137
 
101
138
  it 'should allow assignment of new pairs if given a block arg' do
102
139
  bh = Bihash.new { |bihash, key| bihash[key] = key.to_s }
103
- bh[404].must_equal '404'
104
- bh.size.must_equal 1
105
- bh.must_include 404
106
- bh.must_include '404'
140
+ _(bh[404]).must_equal '404'
141
+ _(bh.size).must_equal 1
142
+ _(bh).must_include 404
143
+ _(bh).must_include '404'
144
+ end
145
+
146
+ it 'should accept capacity as an optimization with and without defaults' do
147
+ bh1 = Bihash.new(capacity: 42)
148
+ _(bh1[:not_a_key]).must_be_nil
149
+
150
+ bh2 = Bihash.new(404, capacity: 42)
151
+ _(bh2[:not_a_key]).must_equal 404
152
+
153
+ bh3 = Bihash.new(capacity: 42) { "#{_2} not found" }
154
+ _(bh3[404]).must_equal '404 not found'
155
+ end
156
+
157
+ it 'should not accept arbitrary keyword arguments' do
158
+ _(-> { Bihash.new(keywords: true) }).must_raise ArgumentError
159
+ end
160
+
161
+ it 'should accept a default that is a hash' do
162
+ bh = Bihash.new({hash: true})
163
+ _(bh[:not_a_key]).must_equal({hash: true})
107
164
  end
108
165
 
109
166
  it 'should not accept both an object and a block' do
110
- -> { Bihash.new('default 1') { 'default 2' } }.must_raise ArgumentError
167
+ _(-> { Bihash.new('default 1') { 'default 2' } }).must_raise ArgumentError
111
168
  end
112
169
  end
113
170
 
114
171
  describe '::try_convert' do
172
+ it 'should return the bihash if given a bihash' do
173
+ original_bh = Bihash[:key => 'value']
174
+ returned_bh = Bihash.try_convert(original_bh)
175
+ _(original_bh.object_id).must_equal returned_bh.object_id
176
+ end
177
+
115
178
  it 'should convert an object to a bihash if it responds to #to_hash' do
116
179
  hash = {:k1 => 1, :k2 => 2}
117
180
  bh = Bihash.try_convert(hash)
118
- bh.must_be_instance_of Bihash
119
- bh[:k1].must_equal 1
120
- bh[:k2].must_equal 2
121
- bh[1].must_equal :k1
122
- bh[2].must_equal :k2
181
+ _(bh).must_be_instance_of Bihash
182
+ _(bh[:k1]).must_equal 1
183
+ _(bh[:k2]).must_equal 2
184
+ _(bh[1]).must_equal :k1
185
+ _(bh[2]).must_equal :k2
123
186
  end
124
187
 
125
- it 'should convert a bihash to a bihash' do
126
- bh = Bihash[:key => 'value']
127
- Bihash.try_convert(bh).must_equal bh
188
+ it 'should return nil if the object does not respond to #to_hash' do
189
+ _(Bihash.try_convert(Object.new)).must_be_nil
128
190
  end
129
191
 
130
- it 'should return nil if the object does not respond to #to_hash' do
131
- Bihash.try_convert(Object.new).must_be_nil
192
+ it 'should protect against mutations the original object' do
193
+ hash = {:k1 => 1, :k2 => 2}
194
+ bh = Bihash.try_convert(hash)
195
+ hash[:k3] = 3
196
+ _(bh).must_equal Bihash[:k1 => 1, :k2 => 2]
197
+ _(bh.include?(:k3)).must_equal false
198
+ _(bh.include?(3)).must_equal false
199
+ end
200
+
201
+ it 'should not carry over the default value' do
202
+ h = Hash.new(404)
203
+ bh = Bihash.try_convert(h)
204
+ _(bh.default).must_be_nil
205
+ _(bh[:not_a_key]).must_be_nil
206
+ end
207
+
208
+ it 'should not carry over the default_proc' do
209
+ h = Hash.new { "#{_2} not found" }
210
+ bh = Bihash.try_convert(h)
211
+ _(bh.default_proc).must_be_nil
212
+ _(bh[:not_a_key]).must_be_nil
213
+ end
214
+
215
+ it 'should not carry over compare_by_identity' do
216
+ h = Hash.new.compare_by_identity
217
+ h[Array.new] = :array
218
+ bh = Bihash.try_convert(h)
219
+ _(bh.compare_by_identity?).must_equal false
220
+ _(bh[Array.new]).must_equal :array
132
221
  end
133
222
 
134
223
  it 'should not accept a hash with duplicate values' do
135
- -> { Bihash.try_convert(:k1 => 1, :k2 => 1) }.must_raise ArgumentError
224
+ _(-> { Bihash.try_convert(:k1 => 1, :k2 => 1) }).must_raise ArgumentError
136
225
  end
137
226
  end
138
227
 
139
228
  describe '#<' do
140
229
  it 'should raise an error if the right hand side is not a bihash' do
141
- -> { Bihash[a: 1, b: 2] < {a: 1, b: 2, c: 3} }.must_raise TypeError
230
+ _(-> { Bihash[a: 1, b: 2] < {a: 1, b: 2, c: 3} }).must_raise TypeError
142
231
  end
143
232
 
144
233
  it 'should return true when the argument is a strict subset of self' do
145
- (Bihash[a: 1, b: 2] < Bihash[a: 1, b: 2, c: 3]).must_equal true
234
+ _(Bihash[a: 1, b: 2] < Bihash[a: 1, b: 2, c: 3]).must_equal true
146
235
  end
147
236
 
148
237
  it 'should return false when the argument is equal to self' do
149
- (Bihash[a: 1, b: 2] < Bihash[a: 1, b: 2]).must_equal false
238
+ _(Bihash[a: 1, b: 2] < Bihash[a: 1, b: 2]).must_equal false
150
239
  end
151
240
 
152
241
  it 'should return false when the argument is not a subset of self' do
153
- (Bihash[a: 1, b: 2, c: 3] < Bihash[a: 1, b: 2]).must_equal false
242
+ _(Bihash[a: 1, b: 2, c: 3] < Bihash[a: 1, b: 2]).must_equal false
154
243
  end
155
244
  end
156
245
 
157
246
  describe '#<=' do
158
247
  it 'should raise an error if the right hand side is not a bihash' do
159
- -> { Bihash[a: 1, b: 2] <= {a: 1, b: 2, c: 3} }.must_raise TypeError
248
+ _(-> { Bihash[a: 1, b: 2] <= {a: 1, b: 2, c: 3} }).must_raise TypeError
160
249
  end
161
250
 
162
251
  it 'should return true when the argument is a strict subset of self' do
163
- (Bihash[a: 1, b: 2] <= Bihash[a: 1, b: 2, c: 3]).must_equal true
252
+ _(Bihash[a: 1, b: 2] <= Bihash[a: 1, b: 2, c: 3]).must_equal true
164
253
  end
165
254
 
166
255
  it 'should return true when the argument is equal to self' do
167
- (Bihash[a: 1, b: 2] <= Bihash[a: 1, b: 2]).must_equal true
256
+ _(Bihash[a: 1, b: 2] <= Bihash[a: 1, b: 2]).must_equal true
168
257
  end
169
258
 
170
259
  it 'should return false when the argument is not a subset of self' do
171
- (Bihash[a: 1, b: 2, c: 3] <= Bihash[a: 1, b: 2]).must_equal false
260
+ _(Bihash[a: 1, b: 2, c: 3] <= Bihash[a: 1, b: 2]).must_equal false
261
+ end
262
+
263
+ it 'should not treat == values as eql? when comparing pairs' do
264
+ bh1 = Bihash[1.to_i => 1.to_r, 1.to_f => 1.to_c]
265
+ bh2 = Bihash[1.to_i => 1.to_c, 1.to_f => 1.to_r]
266
+ _(bh1 <= bh2).must_equal false
172
267
  end
173
268
  end
174
269
 
175
270
  describe '#==' do
176
271
  it 'should return true when two bihashes have the same pairs' do
177
272
  bh1, bh2 = Bihash[:k1 => 1, :k2 => 2], Bihash[2 => :k2, 1 => :k1]
178
- (bh1 == bh2).must_equal true
273
+ _(bh1 == bh2).must_equal true
179
274
  end
180
275
 
181
276
  it 'should return false when two bihashes do not have the same pairs' do
182
277
  bh1, bh2 = Bihash[:k1 => 1, :k2 => 2], Bihash[:k1 => 1, :k2 => 99]
183
- (bh1 == bh2).must_equal false
278
+ _(bh1 == bh2).must_equal false
279
+ end
280
+
281
+ it 'should compare pairs using #eql? (since all values are also keys)' do
282
+ bh1 = Bihash[1.to_i => 1.to_r, 1.to_f => 1.to_c]
283
+ bh2 = Bihash[1.to_i => 1.to_c, 1.to_f => 1.to_r]
284
+ _(bh1 == bh2).must_equal false
285
+ end
286
+
287
+ describe 'when the bihashes differ on compare_by_identity' do
288
+ it 'should return true when both bihashes are empty' do
289
+ bh1 = Bihash[].compare_by_identity
290
+ bh2 = Bihash[]
291
+ _(bh1 == bh2).must_equal true
292
+ end
293
+
294
+ it 'should return false for non-empty bihashes' do
295
+ bh1 = Bihash[one: 1].compare_by_identity
296
+ bh2 = Bihash[one: 1]
297
+ _(bh1 == bh2).must_equal false
298
+ end
299
+ end
300
+
301
+ describe 'when compare_by_identity is set on both bihashes' do
302
+ it 'should return true when all pairs are equal?' do
303
+ i, r, f, c = 1.to_i, 1.to_r, 1.to_f, 1.to_c
304
+ bh1 = Bihash[i => r, f => c].compare_by_identity
305
+ bh2 = Bihash[i => r, f => c].compare_by_identity
306
+ _(bh1 == bh2).must_equal true
307
+ end
308
+
309
+ it 'should return false when all pairs are eql? but not equal?' do
310
+ bh1 = Bihash[1.to_i => 1.to_r, 1.to_f => 1.to_c].compare_by_identity
311
+ bh2 = Bihash[1.to_i => 1.to_r, 1.to_f => 1.to_c].compare_by_identity
312
+ _(bh1 == bh2).must_equal false
313
+ end
184
314
  end
185
315
 
186
316
  it 'should be aliased to #eql?' do
187
317
  bh = Bihash.new
188
- bh.method(:eql?).must_equal bh.method(:==)
318
+ _(bh.method(:eql?)).must_equal bh.method(:==)
189
319
  end
190
320
  end
191
321
 
192
322
  describe '#>' do
193
323
  it 'should raise an error if the right hand side is not a bihash' do
194
- -> { Bihash[a: 1, b: 2] > {a: 1, b: 2, c: 3} }.must_raise TypeError
324
+ _(-> { Bihash[a: 1, b: 2] > {a: 1, b: 2, c: 3} }).must_raise TypeError
195
325
  end
196
326
 
197
327
  it 'should return true when the argument is a strict superset of self' do
198
- (Bihash[a: 1, b: 2, c: 3] > Bihash[a: 1, b: 2]).must_equal true
328
+ _(Bihash[a: 1, b: 2, c: 3] > Bihash[a: 1, b: 2]).must_equal true
199
329
  end
200
330
 
201
331
  it 'should return false when the argument is equal to self' do
202
- (Bihash[a: 1, b: 2] > Bihash[a: 1, b: 2]).must_equal false
332
+ _(Bihash[a: 1, b: 2] > Bihash[a: 1, b: 2]).must_equal false
203
333
  end
204
334
 
205
335
  it 'should return false when the argument is not a superset of self' do
206
- (Bihash[a: 1, b: 2] > Bihash[a: 1, b: 2, c: 3]).must_equal false
336
+ _(Bihash[a: 1, b: 2] > Bihash[a: 1, b: 2, c: 3]).must_equal false
207
337
  end
208
338
  end
209
339
 
210
340
  describe '#>=' do
211
341
  it 'should raise an error if the right hand side is not a bihash' do
212
- -> { Bihash[a: 1, b: 2] >= {a: 1, b: 2, c: 3} }.must_raise TypeError
342
+ _(-> { Bihash[a: 1, b: 2] >= {a: 1, b: 2, c: 3} }).must_raise TypeError
213
343
  end
214
344
 
215
345
  it 'should return true when the argument is a strict superset of self' do
216
- (Bihash[a: 1, b: 2, c: 3] >= Bihash[a: 1, b: 2]).must_equal true
346
+ _(Bihash[a: 1, b: 2, c: 3] >= Bihash[a: 1, b: 2]).must_equal true
217
347
  end
218
348
 
219
349
  it 'should return true when the argument is equal to self' do
220
- (Bihash[a: 1, b: 2] >= Bihash[a: 1, b: 2]).must_equal true
350
+ _(Bihash[a: 1, b: 2] >= Bihash[a: 1, b: 2]).must_equal true
221
351
  end
222
352
 
223
353
  it 'should return false when the argument is not a superset of self' do
224
- (Bihash[a: 1, b: 2] >= Bihash[a: 1, b: 2, c: 3]).must_equal false
354
+ _(Bihash[a: 1, b: 2] >= Bihash[a: 1, b: 2, c: 3]).must_equal false
355
+ end
356
+
357
+ it 'should not treat == values as eql? when comparing pairs' do
358
+ bh1 = Bihash[1.to_i => 1.to_r, 1.to_f => 1.to_c]
359
+ bh2 = Bihash[1.to_i => 1.to_c, 1.to_f => 1.to_r]
360
+ _(bh1 >= bh2).must_equal false
225
361
  end
226
362
  end
227
363
 
228
364
  describe '#[]' do
229
365
  it 'should return the other pair' do
230
366
  bh = Bihash[:key => 'value']
231
- bh[:key].must_equal 'value'
232
- bh['value'].must_equal :key
367
+ _(bh[:key]).must_equal 'value'
368
+ _(bh['value']).must_equal :key
233
369
  end
234
370
 
235
371
  it 'should return falsey values correctly' do
236
372
  bh1 = Bihash[nil => false]
237
- bh1[nil].must_equal false
238
- bh1[false].must_be_nil
373
+ _(bh1[nil]).must_equal false
374
+ _(bh1[false]).must_be_nil
239
375
 
240
376
  bh2 = Bihash[false => nil]
241
- bh2[false].must_be_nil
242
- bh2[nil].must_equal false
377
+ _(bh2[false]).must_be_nil
378
+ _(bh2[nil]).must_equal false
243
379
  end
244
380
  end
245
381
 
@@ -247,68 +383,125 @@ describe Bihash do
247
383
  it 'should allow assignment of new pairs' do
248
384
  bh = Bihash.new
249
385
  bh[:key] = 'value'
250
- bh[:key].must_equal 'value'
251
- bh['value'].must_equal :key
386
+ _(bh[:key]).must_equal 'value'
387
+ _(bh['value']).must_equal :key
252
388
  end
253
389
 
254
390
  it 'should remove old pairs if old keys are re-assigned' do
255
391
  bh = Bihash[1 => 'one', 2 => 'two']
256
392
  bh[1] = 'uno'
257
- bh[1].must_equal 'uno'
258
- bh['uno'].must_equal 1
259
- bh.wont_include 'one'
393
+ _(bh[1]).must_equal 'uno'
394
+ _(bh['uno']).must_equal 1
395
+ _(bh).wont_include 'one'
260
396
  end
261
397
 
262
398
  it 'should always return the value object if key-value pairs are equal' do
263
399
  key, value = [], []
264
400
  bh = Bihash.new
265
401
  bh[key] = value
266
- bh[key].object_id.must_equal value.object_id
267
- bh[value].object_id.must_equal value.object_id
402
+ _(bh[key].object_id).must_equal value.object_id
403
+ _(bh[value].object_id).must_equal value.object_id
268
404
  end
269
405
 
270
406
  it 'should be aliased to #store' do
271
407
  bh = Bihash.new
272
- bh.method(:store).must_equal bh.method(:[]=)
408
+ _(bh.method(:store)).must_equal bh.method(:[]=)
273
409
  bh.store(:key, 'value')
274
- bh[:key].must_equal 'value'
275
- bh['value'].must_equal :key
410
+ _(bh[:key]).must_equal 'value'
411
+ _(bh['value']).must_equal :key
276
412
  end
277
413
 
278
- it 'should raise RuntimeError if called on a frozen bihash' do
279
- -> { Bihash.new.freeze[:key] = 'value' }.must_raise RuntimeError
414
+ it 'should raise FrozenError if called on a frozen bihash' do
415
+ _(-> { Bihash.new.freeze[:key] = 'value' }).must_raise FrozenError
280
416
  end
281
417
  end
282
418
 
283
419
  describe '#assoc' do
284
420
  it 'should return the pair if the argument is a key' do
285
421
  bh = Bihash[:k1 => 'v1', :k2 => 'v2']
286
- bh.assoc(:k1).must_equal [:k1, 'v1']
287
- bh.assoc('v2').must_equal ['v2', :k2]
422
+ _(bh.assoc(:k1)).must_equal [:k1, 'v1']
423
+ _(bh.assoc('v2')).must_equal ['v2', :k2]
288
424
  end
289
425
 
290
426
  it 'should return nil if the argument is not a key' do
291
427
  bh = Bihash.new(404)
292
- bh.assoc(:not_a_key).must_be_nil
428
+ _(bh.assoc(:not_a_key)).must_be_nil
293
429
  end
294
430
 
295
431
  it 'should find the key using #==' do
296
432
  bh = Bihash[[] => 'array']
297
433
  bh['array'] << 'modified'
298
- bh.assoc(['modified']).must_equal [['modified'], 'array']
299
- bh.assoc([]).must_be_nil
434
+ _(bh.assoc(['modified'])).must_equal [['modified'], 'array']
435
+ _(bh.assoc([])).must_be_nil
436
+ end
437
+ end
438
+
439
+ describe '#compact' do
440
+ describe 'when any pairs contain a nil key' do
441
+ it 'should return a new bihash with any pairs containing nil removed' do
442
+ bh = Bihash[1 => :one, 2 => nil, 3 => :three]
443
+ _(bh.compact).must_equal Bihash[1 => :one, 3 => :three]
444
+ _(bh).must_equal Bihash[1 => :one, 2 => nil, 3 => :three]
445
+ end
446
+ end
447
+
448
+ describe 'no pairs contain a nil key' do
449
+ it 'should return a copy of the original bihash' do
450
+ bh = Bihash[1 => :one, 2 => :two, 3 => :three]
451
+ compacted_bh = bh.compact
452
+ _(compacted_bh).must_equal Bihash[1 => :one, 2=> :two, 3 => :three]
453
+ _(compacted_bh.object_id).wont_equal bh.object_id
454
+ end
455
+ end
456
+
457
+ it 'should return a new bihash with defaults copied' do
458
+ bh_default = Bihash.new(404)
459
+ _(bh_default.compact.default).must_equal 404
460
+ bh_default_proc = Bihash.new { "hello #{_2}" }
461
+ _(bh_default_proc.compact.default(:world)).must_equal "hello world"
462
+ end
463
+
464
+ it 'should return a new bihash with compare_by_identity copied' do
465
+ bh = Bihash[]
466
+ _(bh.compact.compare_by_identity?).must_equal false
467
+ bh.compare_by_identity
468
+ _(bh.compact.compare_by_identity?).must_equal true
469
+ end
470
+ end
471
+
472
+ describe '#compact!' do
473
+ it 'should delete any pairs containing nil' do
474
+ bh1 = Bihash[1 => :one, 2 => nil, 3 => :three]
475
+ bh1_id = bh1.object_id
476
+ _(bh1.compact!.object_id).must_equal bh1_id
477
+ _(bh1).must_equal Bihash[1 => :one, 3 => :three]
478
+
479
+ bh2 = Bihash[1 => :one, 2 => nil, 3 => :three]
480
+ bh2_id = bh2.object_id
481
+ _(bh2.compact!.object_id).must_equal bh2_id
482
+ _(bh2).must_equal Bihash[1 => :one, 3 => :three]
483
+ end
484
+
485
+ it 'should return nil if no changes were made to the bihash' do
486
+ bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
487
+ _(bh.compact!).must_be_nil
488
+ _(bh).must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
489
+ end
490
+
491
+ it 'should raise FrozenError if called on a frozen bihash' do
492
+ _(-> { Bihash.new.freeze.compact! }).must_raise FrozenError
300
493
  end
301
494
  end
302
495
 
303
496
  describe '#clear' do
304
497
  it 'should remove all pairs and return the bihash' do
305
498
  bh = Bihash[:key => 'value']
306
- bh.clear.object_id.must_equal bh.object_id
307
- bh.must_be_empty
499
+ _(bh.clear.object_id).must_equal bh.object_id
500
+ _(bh).must_be_empty
308
501
  end
309
502
 
310
- it 'should raise RuntimeError if called on a frozen bihash' do
311
- -> { Bihash.new.freeze.clear }.must_raise RuntimeError
503
+ it 'should raise FrozenError if called on a frozen bihash' do
504
+ _(-> { Bihash.new.freeze.clear }).must_raise FrozenError
312
505
  end
313
506
  end
314
507
 
@@ -317,7 +510,30 @@ describe Bihash do
317
510
  bh = Bihash[1 => :one]
318
511
  clone = bh.clone
319
512
  clone[2] = :two
320
- bh[2].must_be_nil
513
+ _(bh[2]).must_be_nil
514
+ end
515
+
516
+ it 'should copy the default value' do
517
+ bh = Bihash.new(404)
518
+ clone = bh.clone
519
+ _(clone.default).must_equal 404
520
+ end
521
+
522
+ it 'should copy the default proc' do
523
+ bh = Bihash.new { |_,k| k.to_s }
524
+ clone = bh.clone
525
+ _(clone.default(:not_a_key)).must_equal 'not_a_key'
526
+ end
527
+
528
+ it 'should copy compare_by_identity' do
529
+ bh = Bihash[]
530
+ _(bh.clone.compare_by_identity?).must_equal false
531
+ bh.compare_by_identity
532
+ _(bh.clone.compare_by_identity?).must_equal true
533
+ end
534
+
535
+ it 'should copy the frozen state' do
536
+ _(Bihash.new.freeze.clone.frozen?).must_equal true
321
537
  end
322
538
  end
323
539
 
@@ -326,58 +542,76 @@ describe Bihash do
326
542
  bh = Bihash.new.compare_by_identity
327
543
  key1, key2 = 'key', 'value'
328
544
  bh[key1] = key2
329
- bh['key'].must_be_nil
330
- bh['value'].must_be_nil
331
- bh[key1].must_equal 'value'
332
- bh[key2].must_equal 'key'
545
+ _(bh['key']).must_be_nil
546
+ _(bh['value']).must_be_nil
547
+ _(bh[key1]).must_equal 'value'
548
+ _(bh[key2]).must_equal 'key'
549
+ end
550
+
551
+ it 'should raise FrozenError if called on a frozen bihash' do
552
+ _(-> { Bihash.new.freeze.compare_by_identity }).must_raise FrozenError
333
553
  end
334
554
 
335
- it 'should raise RuntimeError if called on a frozen bihash' do
336
- -> { Bihash.new.freeze.compare_by_identity }.must_raise RuntimeError
555
+ it 'should raise if called when key duplicated (equal?) outside pair' do
556
+ a, b, c, d = [:a], [:b], [:c], [:d]
557
+ bh = Bihash[a => b, c => d]
558
+ d.replace([:a])
559
+ bh[d] = :anything
560
+ _(-> { bh.compare_by_identity }).must_raise RuntimeError
337
561
  end
338
562
  end
339
563
 
340
564
  describe '#compare_by_identity?' do
341
565
  it 'should indicate whether bihash is comparing by identity' do
342
- Bihash.new.compare_by_identity.compare_by_identity?.must_equal true
343
- Bihash.new.compare_by_identity?.must_equal false
566
+ _(Bihash.new.compare_by_identity.compare_by_identity?).must_equal true
567
+ _(Bihash.new.compare_by_identity?).must_equal false
568
+ end
569
+ end
570
+
571
+ describe '#deconstruct_keys' do
572
+ it 'should enable pattern matching in the forward and reverse direction' do
573
+ bh = Bihash[left: :right]
574
+ bh in {left:}
575
+ _(left).must_equal :right
576
+ bh in {right:}
577
+ _(right).must_equal :left
344
578
  end
345
579
  end
346
580
 
347
581
  describe '#default' do
348
582
  it 'should not accept more than one argument' do
349
- -> { Bihash.new.default(1,2) }.must_raise ArgumentError
583
+ _(-> { Bihash.new.default(1,2) }).must_raise ArgumentError
350
584
  end
351
585
 
352
586
  describe 'when there is not a default proc' do
353
587
  it 'should return the default' do
354
588
  bh1 = Bihash[:key => 'value']
355
- bh1.default.must_be_nil
356
- bh1.default(:not_a_key).must_be_nil
357
- bh1.default(:key).must_be_nil
589
+ _(bh1.default).must_be_nil
590
+ _(bh1.default(:not_a_key)).must_be_nil
591
+ _(bh1.default(:key)).must_be_nil
358
592
 
359
593
  bh2 = Bihash.new(404)
360
594
  bh2[:key] = 'value'
361
- bh2.default.must_equal 404
362
- bh2.default(:not_a_key).must_equal 404
363
- bh2.default(:key).must_equal 404
595
+ _(bh2.default).must_equal 404
596
+ _(bh2.default(:not_a_key)).must_equal 404
597
+ _(bh2.default(:key)).must_equal 404
364
598
  end
365
599
  end
366
600
 
367
601
  describe 'when there is a default proc' do
368
602
  it 'should return the default if called with no argument' do
369
- Bihash.new { 'proc called' }.default.must_be_nil
603
+ _(Bihash.new { 'proc called' }.default).must_be_nil
370
604
  end
371
605
 
372
606
  it 'should call the default proc when called with an argument' do
373
607
  bh = Bihash.new { |bihash, key| bihash[key] = key.to_s }
374
608
  bh[:key] = 'value'
375
609
 
376
- bh.default(:key).must_equal 'key'
377
- bh[:key].must_equal 'key'
610
+ _(bh.default(:key)).must_equal 'key'
611
+ _(bh[:key]).must_equal 'key'
378
612
 
379
- bh.default(404).must_equal '404'
380
- bh[404].must_equal '404'
613
+ _(bh.default(404)).must_equal '404'
614
+ _(bh[404]).must_equal '404'
381
615
  end
382
616
  end
383
617
  end
@@ -385,13 +619,13 @@ describe Bihash do
385
619
  describe '#default=' do
386
620
  it 'should set the default object' do
387
621
  bh = Bihash.new { 'proc called' }
388
- bh[:not_a_key].must_equal 'proc called'
389
- (bh.default = 404).must_equal 404
390
- bh[:not_a_key].must_equal 404
622
+ _(bh[:not_a_key]).must_equal 'proc called'
623
+ _(bh.default = 404).must_equal 404
624
+ _(bh[:not_a_key]).must_equal 404
391
625
  end
392
626
 
393
- it 'should raise RuntimeError if called on a frozen bihash' do
394
- -> { Bihash.new.freeze.default = 404 }.must_raise RuntimeError
627
+ it 'should raise FrozenError if called on a frozen bihash' do
628
+ _(-> { Bihash.new.freeze.default = 404 }).must_raise FrozenError
395
629
  end
396
630
  end
397
631
 
@@ -401,68 +635,68 @@ describe Bihash do
401
635
  prc = bh.default_proc
402
636
  array = []
403
637
  prc.call(array, 2)
404
- array.must_equal [nil, nil, 2]
638
+ _(array).must_equal [nil, nil, 2]
405
639
  end
406
640
 
407
641
  it 'should return nil if there is no default proc' do
408
- Bihash.new.default_proc.must_be_nil
409
- Bihash.new(404).default_proc.must_be_nil
642
+ _(Bihash.new.default_proc).must_be_nil
643
+ _(Bihash.new(404).default_proc).must_be_nil
410
644
  end
411
645
  end
412
646
 
413
647
  describe '#default_proc=' do
414
648
  it 'should set the default proc' do
415
649
  bh = Bihash.new(:default_object)
416
- bh[:not_a_key].must_equal :default_object
417
- (bh.default_proc = ->(bihash, key) { '404' }).must_be_instance_of Proc
418
- bh[:not_a_key].must_equal '404'
650
+ _(bh[:not_a_key]).must_equal :default_object
651
+ _(bh.default_proc = ->(bihash, key) { '404' }).must_be_instance_of Proc
652
+ _(bh[:not_a_key]).must_equal '404'
419
653
  end
420
654
 
421
655
  it 'should set the default value to nil if argument is nil' do
422
656
  bh = Bihash.new(:default_object)
423
- bh[:not_a_key].must_equal :default_object
424
- (bh.default_proc = nil).must_be_nil
425
- bh[:not_a_key].must_be_nil
657
+ _(bh[:not_a_key]).must_equal :default_object
658
+ _(bh.default_proc = nil).must_be_nil
659
+ _(bh[:not_a_key]).must_be_nil
426
660
  end
427
661
 
428
662
  it 'should raise TypeError if not given a non-proc (except nil)' do
429
- -> { Bihash.new.default_proc = :not_a_proc }.must_raise TypeError
663
+ _(-> { Bihash.new.default_proc = :not_a_proc }).must_raise TypeError
430
664
  end
431
665
 
432
666
  it 'should raise TypeError given a lambda without 2 args' do
433
- -> { Bihash.new.default_proc = -> { '404' } }.must_raise TypeError
667
+ _(-> { Bihash.new.default_proc = -> { '404' } }).must_raise TypeError
434
668
  end
435
669
 
436
- it 'should raise RuntimeError if called on a frozen bihash' do
437
- -> { Bihash[].freeze.default_proc = proc { '' } }.must_raise RuntimeError
670
+ it 'should raise FrozenError if called on a frozen bihash' do
671
+ _(-> { Bihash[].freeze.default_proc = proc { '' } }).must_raise FrozenError
438
672
  end
439
673
  end
440
674
 
441
675
  describe '#delete' do
442
676
  it 'should return the other key if the given key is found' do
443
- Bihash[:key => 'value'].delete(:key).must_equal 'value'
444
- Bihash[:key => 'value'].delete('value').must_equal :key
677
+ _(Bihash[:key => 'value'].delete(:key)).must_equal 'value'
678
+ _(Bihash[:key => 'value'].delete('value')).must_equal :key
445
679
  end
446
680
 
447
681
  it 'should remove both keys' do
448
682
  bh1 = Bihash[:key => 'value']
449
683
  bh1.delete(:key)
450
- bh1.wont_include :key
451
- bh1.wont_include 'value'
684
+ _(bh1).wont_include :key
685
+ _(bh1).wont_include 'value'
452
686
 
453
687
  bh2 = Bihash[:key => 'value']
454
688
  bh2.delete('value')
455
- bh2.wont_include :key
456
- bh2.wont_include 'value'
689
+ _(bh2).wont_include :key
690
+ _(bh2).wont_include 'value'
457
691
  end
458
692
 
459
693
  it 'should call the block (if given) when the key is not found' do
460
694
  out = Bihash[:key => 'value'].delete(404) { |key| "#{key} not found" }
461
- out.must_equal '404 not found'
695
+ _(out).must_equal '404 not found'
462
696
  end
463
697
 
464
- it 'should raise RuntimeError if called on a frozen bihash' do
465
- -> { Bihash.new.freeze.delete(:key) }.must_raise RuntimeError
698
+ it 'should raise FrozenError if called on a frozen bihash' do
699
+ _(-> { Bihash.new.freeze.delete(:key) }).must_raise FrozenError
466
700
  end
467
701
  end
468
702
 
@@ -470,41 +704,64 @@ describe Bihash do
470
704
  it 'should delete any pairs for which the block evaluates to true' do
471
705
  bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
472
706
  bh_id = bh.object_id
473
- bh.delete_if { |key1, key2| key1.even? }.object_id.must_equal bh_id
474
- bh.must_equal Bihash[1 => :one, 3 => :three]
707
+ _(bh.delete_if { |key1, key2| key1.even? }.object_id).must_equal bh_id
708
+ _(bh).must_equal Bihash[1 => :one, 3 => :three]
475
709
  end
476
710
 
477
- it 'should raise RuntimeError if called on a frozen bihash with a block' do
478
- -> { Bihash.new.freeze.delete_if { false } }.must_raise RuntimeError
711
+ it 'should raise FrozenError if called on a frozen bihash with a block' do
712
+ _(-> { Bihash.new.freeze.delete_if { false } }).must_raise FrozenError
479
713
  end
480
714
 
481
715
  it 'should return an enumerator if not given a block' do
482
716
  enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].delete_if
483
- enum.must_be_instance_of Enumerator
484
- enum.each { |k1, k2| k1.even? }.must_equal Bihash[1 => :one, 3 => :three]
717
+ _(enum).must_be_instance_of Enumerator
718
+ _(enum.size).must_equal 4
719
+ _(enum.each { |k1, k2| k1.even? }).must_equal Bihash[1 => :one, 3 => :three]
485
720
  end
486
721
  end
487
722
 
488
723
  describe '#dig' do
489
724
  it 'should traverse nested bihashes' do
490
725
  bh = Bihash[foo: Bihash[bar: Bihash[baz: 4]]]
491
- bh.dig(:foo, :bar, :baz).must_equal 4
492
- bh.dig(:foo, :bar, 4).must_equal :baz
726
+ _(bh.dig(:foo, :bar, :baz)).must_equal 4
727
+ _(bh.dig(:foo, :bar, 4)).must_equal :baz
493
728
  end
494
729
 
495
730
  it 'should traverse nested hashes' do
496
731
  bh = Bihash[foo: {bar: {baz: 4}}]
497
- bh.dig(:foo, :bar, :baz).must_equal 4
732
+ _(bh.dig(:foo, :bar, :baz)).must_equal 4
498
733
  end
499
734
 
500
735
  it 'should traverse nested arrays' do
501
736
  bh = Bihash[foo: [[4]]]
502
- bh.dig(:foo, 0, 0).must_equal 4
737
+ _(bh.dig(:foo, 0, 0)).must_equal 4
503
738
  end
504
739
 
505
740
  it 'should return nil if any intermediate step is nil' do
506
741
  bh = Bihash[foo: Bihash[bar: Bihash[baz: 4]]]
507
- bh.dig(:foo, :bur, :boz).must_be_nil
742
+ _(bh.dig(:foo, :bur, :boz)).must_be_nil
743
+ end
744
+
745
+ describe 'when a key is not found' do
746
+ it 'should use the default value if present' do
747
+ bh = Bihash.new(404)
748
+ _(bh.dig(:not_a_key)).must_equal 404
749
+ end
750
+
751
+ it 'should use the default proc if present' do
752
+ bh = Bihash.new { "#{_2} not found" }
753
+ _(bh.dig(:not_a_key)).must_equal "not_a_key not found"
754
+ end
755
+
756
+ it 'should early return nil if the default is nil' do
757
+ bh = Bihash.new(nil)
758
+ _(bh.dig(:not_a_key, :dont_try_me)).must_be_nil
759
+ end
760
+
761
+ it 'should continue to dig on the default if it is not nil' do
762
+ bh = Bihash.new(Bihash[do_try_me: "it worked!"])
763
+ _(bh.dig(:not_a_key, :do_try_me)).must_equal "it worked!"
764
+ end
508
765
  end
509
766
  end
510
767
 
@@ -513,7 +770,30 @@ describe Bihash do
513
770
  bh = Bihash[1 => :one]
514
771
  dup = bh.dup
515
772
  dup[2] = :two
516
- bh[2].must_be_nil
773
+ _(bh[2]).must_be_nil
774
+ end
775
+
776
+ it 'should copy the default value' do
777
+ bh = Bihash.new(404)
778
+ dup = bh.dup
779
+ _(dup.default).must_equal 404
780
+ end
781
+
782
+ it 'should copy the default proc' do
783
+ bh = Bihash.new { |_,k| k.to_s }
784
+ dup = bh.dup
785
+ _(dup.default(:not_a_key)).must_equal 'not_a_key'
786
+ end
787
+
788
+ it 'should copy compare_by_identity' do
789
+ bh = Bihash[]
790
+ _(bh.dup.compare_by_identity?).must_equal false
791
+ bh.compare_by_identity
792
+ _(bh.dup.compare_by_identity?).must_equal true
793
+ end
794
+
795
+ it 'should not copy the frozen state' do
796
+ _(Bihash.new.freeze.dup.frozen?).must_equal false
517
797
  end
518
798
  end
519
799
 
@@ -521,120 +801,147 @@ describe Bihash do
521
801
  it 'should iterate over each pair in the bihash' do
522
802
  array = []
523
803
  Bihash[:k1 => 'v1', :k2 => 'v2'].each { |pair| array << pair }
524
- array.must_equal [[:k1, 'v1'], [:k2, 'v2']]
804
+ _(array).must_equal [[:k1, 'v1'], [:k2, 'v2']]
525
805
  end
526
806
 
527
807
  it 'should return the bihash if given a block' do
528
808
  bh = Bihash.new
529
- bh.each { |p| }.must_be_instance_of Bihash
530
- bh.each { |p| }.object_id.must_equal bh.object_id
809
+ _(bh.each { |p| }).must_be_instance_of Bihash
810
+ _(bh.each { |p| }.object_id).must_equal bh.object_id
531
811
  end
532
812
 
533
813
  it 'should return an enumerator if not given a block' do
534
814
  enum = Bihash[:k1 => 'v1', :k2 => 'v2'].each
535
- enum.must_be_instance_of Enumerator
536
- enum.each { |pair| pair }.must_equal Bihash[:k1 => 'v1', :k2 => 'v2']
815
+ _(enum).must_be_instance_of Enumerator
816
+ _(enum.size).must_equal 2
817
+ _(enum.each { |pair| pair }).must_equal Bihash[:k1 => 'v1', :k2 => 'v2']
537
818
  end
538
819
 
539
820
  it 'should be aliased to #each_pair' do
540
821
  bh = Bihash.new
541
- bh.method(:each_pair).must_equal bh.method(:each)
822
+ _(bh.method(:each_pair)).must_equal bh.method(:each)
542
823
  end
543
824
  end
544
825
 
545
826
  describe '#empty?' do
546
827
  it 'should indicate if the bihash is empty' do
547
- Bihash.new.empty?.must_equal true
548
- Bihash[:key => 'value'].empty?.must_equal false
828
+ _(Bihash.new.empty?).must_equal true
829
+ _(Bihash[:key => 'value'].empty?).must_equal false
830
+ end
831
+ end
832
+
833
+ describe '#except' do
834
+ it 'should return a new bihash without the pairs that are in the args' do
835
+ bh = Bihash[1 => :one, 2 => :two, 3 => :three]
836
+ _(bh.except(1, :two, "nope")).must_equal Bihash[3 => :three]
837
+ _(bh).must_equal Bihash[1 => :one, 2 => :two, 3 => :three]
838
+ end
839
+
840
+ it 'should return a new bihash without defaults copied' do
841
+ bh_default = Bihash.new(404)
842
+ _(bh_default.except.default).must_be_nil
843
+ bh_default_proc = Bihash.new { 404 }
844
+ _(bh_default_proc.except.default_proc).must_be_nil
845
+ end
846
+
847
+ it 'should return a new bihash with compare_by_identity copied' do
848
+ bh = Bihash[]
849
+ _(bh.except.compare_by_identity?).must_equal false
850
+ bh.compare_by_identity
851
+ _(bh.except.compare_by_identity?).must_equal true
549
852
  end
550
853
  end
551
854
 
552
855
  describe '#fetch' do
553
856
  it 'should return the other pair' do
554
857
  bh = Bihash[:key => 'value']
555
- bh.fetch(:key).must_equal 'value'
556
- bh.fetch('value').must_equal :key
858
+ _(bh.fetch(:key)).must_equal 'value'
859
+ _(bh.fetch('value')).must_equal :key
557
860
  end
558
861
 
559
862
  it 'should return falsey values correctly' do
560
863
  bh1 = Bihash[nil => false]
561
- bh1.fetch(nil).must_equal false
562
- bh1.fetch(false).must_be_nil
864
+ _(bh1.fetch(nil)).must_equal false
865
+ _(bh1.fetch(false)).must_be_nil
563
866
 
564
867
  bh2 = Bihash[false => nil]
565
- bh2.fetch(false).must_be_nil
566
- bh2.fetch(nil).must_equal false
868
+ _(bh2.fetch(false)).must_be_nil
869
+ _(bh2.fetch(nil)).must_equal false
567
870
  end
568
871
 
569
872
  describe 'when the key is not found' do
570
873
  it 'should raise KeyError when not supplied any default' do
571
- -> { Bihash[].fetch(:not_a_key) }.must_raise KeyError
874
+ _(-> { Bihash[].fetch(:not_a_key) }).must_raise KeyError
572
875
  end
573
876
 
574
877
  it 'should return the second arg when supplied with one' do
575
- Bihash[].fetch(:not_a_key, :second_arg).must_equal :second_arg
878
+ _(Bihash[].fetch(:not_a_key, :second_arg)).must_equal :second_arg
576
879
  end
577
880
 
578
881
  it 'should call the block if supplied with one' do
579
- Bihash[].fetch(404) { |k| "#{k} not found" }.must_equal '404 not found'
882
+ _(Bihash[].fetch(404) { |k| "#{k} not found" }).must_equal '404 not found'
580
883
  end
581
884
  end
582
885
  end
583
886
 
584
887
  describe '#fetch_values' do
585
888
  it 'should return an array of values corresponding to the given keys' do
586
- Bihash[1 => :one, 2 => :two].fetch_values(1, 2).must_equal [:one, :two]
587
- Bihash[1 => :one, 2 => :two].fetch_values(:one, :two).must_equal [1, 2]
588
- Bihash[1 => :one, 2 => :two].fetch_values(1, :two).must_equal [:one, 2]
589
- end
590
-
591
- it 'should raise a KeyError if any key is not found' do
592
- -> { Bihash.new.fetch_values(404) }.must_raise KeyError
889
+ _(Bihash[1 => :one, 2 => :two].fetch_values(1, 2)).must_equal [:one, :two]
890
+ _(Bihash[1 => :one, 2 => :two].fetch_values(:one, :two)).must_equal [1, 2]
891
+ _(Bihash[1 => :one, 2 => :two].fetch_values(1, :two)).must_equal [:one, 2]
593
892
  end
594
893
 
595
894
  it 'should not duplicate entries if a key equals its value' do
596
- Bihash[:key => :key].fetch_values(:key).must_equal [:key]
895
+ _(Bihash[:key => :key].fetch_values(:key)).must_equal [:key]
597
896
  end
598
897
 
599
898
  it 'should return an empty array with no args' do
600
- Bihash[:key => 'value'].fetch_values.must_equal []
899
+ _(Bihash[:key => 'value'].fetch_values).must_equal []
900
+ end
901
+
902
+ it 'should raise a KeyError if any key is not found without a block' do
903
+ _(-> { Bihash.new.fetch_values(404) }).must_raise KeyError
904
+ end
905
+
906
+ it 'should yield missing keys to the block if one is given' do
907
+ _(Bihash[1 => :one, 2 => :two].fetch_values(1, :two, 'three') { |k| "#{k} is missing"}).must_equal [:one, 2, 'three is missing']
601
908
  end
602
909
  end
603
910
 
604
911
  describe '#flatten' do
605
912
  it 'should extract the pairs into an array' do
606
- Bihash[:k1 => 'v1', :k2 => 'v2'].flatten.must_equal [:k1, 'v1', :k2, 'v2']
913
+ _(Bihash[:k1 => 'v1', :k2 => 'v2'].flatten).must_equal [:k1, 'v1', :k2, 'v2']
607
914
  end
608
915
 
609
916
  it 'should not flatten array keys if no argument is given' do
610
- Bihash[:key => ['v1', 'v2']].flatten.must_equal [:key, ['v1', 'v2']]
917
+ _(Bihash[:key => ['v1', 'v2']].flatten).must_equal [:key, ['v1', 'v2']]
611
918
  end
612
919
 
613
920
  it 'should flatten to the level given as an argument' do
614
- Bihash[:key => ['v1', 'v2']].flatten(2).must_equal [:key, 'v1', 'v2']
921
+ _(Bihash[:key => ['v1', 'v2']].flatten(2)).must_equal [:key, 'v1', 'v2']
615
922
  end
616
923
  end
617
924
 
618
925
  describe '#hash' do
619
926
  it 'should return the same hash code if two bihashes have the same pairs' do
620
927
  bh1, bh2 = Bihash[:k1 => 1, :k2 => 2], Bihash[2 => :k2, 1 => :k1]
621
- bh1.hash.must_equal bh2.hash
928
+ _(bh1.hash).must_equal bh2.hash
622
929
  end
623
930
  end
624
931
 
625
932
  describe '#include?' do
626
933
  it 'should indicate if the bihash contains the argument' do
627
934
  bh = Bihash[:key => 'value']
628
- bh.include?(:key).must_equal true
629
- bh.include?('value').must_equal true
630
- bh.include?(:not_a_key).must_equal false
935
+ _(bh.include?(:key)).must_equal true
936
+ _(bh.include?('value')).must_equal true
937
+ _(bh.include?(:not_a_key)).must_equal false
631
938
  end
632
939
 
633
940
  it 'should be aliased to #has_key?, #key?, and #member?' do
634
941
  bh = Bihash.new
635
- bh.method(:has_key?).must_equal bh.method(:include?)
636
- bh.method(:key?).must_equal bh.method(:include?)
637
- bh.method(:member?).must_equal bh.method(:include?)
942
+ _(bh.method(:has_key?)).must_equal bh.method(:include?)
943
+ _(bh.method(:key?)).must_equal bh.method(:include?)
944
+ _(bh.method(:member?)).must_equal bh.method(:include?)
638
945
  end
639
946
  end
640
947
 
@@ -642,39 +949,74 @@ describe Bihash do
642
949
  it 'should retain any pairs for which the block evaluates to true' do
643
950
  bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
644
951
  bh_id = bh.object_id
645
- bh.keep_if { |key1, key2| key1.even? }.object_id.must_equal bh_id
646
- bh.must_equal Bihash[2 => :two, 4 => :four]
952
+ _(bh.keep_if { |key1, key2| key1.even? }.object_id).must_equal bh_id
953
+ _(bh).must_equal Bihash[2 => :two, 4 => :four]
647
954
  end
648
955
 
649
- it 'should raise RuntimeError if called on a frozen bihash with a block' do
650
- -> { Bihash.new.freeze.keep_if { true } }.must_raise RuntimeError
956
+ it 'should raise FrozenError if called on a frozen bihash with a block' do
957
+ _(-> { Bihash.new.freeze.keep_if { true } }).must_raise FrozenError
651
958
  end
652
959
 
653
960
  it 'should return an enumerator if not given a block' do
654
961
  enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].keep_if
655
- enum.must_be_instance_of Enumerator
656
- enum.each { |k1, k2| k1.even? }.must_equal Bihash[2 => :two, 4 => :four]
962
+ _(enum).must_be_instance_of Enumerator
963
+ _(enum.size).must_equal 4
964
+ _(enum.each { |k1, k2| k1.even? }).must_equal Bihash[2 => :two, 4 => :four]
657
965
  end
658
966
  end
659
967
 
660
968
  describe '#length' do
661
969
  it 'should return the number of pairs in the bihash' do
662
- Bihash[1 => :one, 2 => :two].length.must_equal 2
970
+ _(Bihash[1 => :one, 2 => :two].length).must_equal 2
663
971
  end
664
972
  end
665
973
 
666
974
  describe '#merge' do
667
- it 'should merge bihashes, assigning each arg pair to a copy of reciever' do
975
+ it 'should merge bihashes, assigning each arg pair to a copy of receiver' do
668
976
  receiver = Bihash[:chips => :salsa, :milk => :cookies, :fish => :rice]
669
977
  original_receiver = receiver.dup
670
978
  argument = Bihash[:fish => :chips, :soup => :salad]
671
979
  return_value = Bihash[:milk => :cookies, :fish => :chips, :soup => :salad]
672
- receiver.merge(argument).must_equal return_value
673
- receiver.must_equal original_receiver
980
+ _(receiver.merge(argument)).must_equal return_value
981
+ _(receiver).must_equal original_receiver
674
982
  end
675
983
 
676
- it 'should raise TypeError if arg is not a bihash' do
677
- -> { Bihash.new.merge({:key => 'value'}) }.must_raise TypeError
984
+ it 'should accept multiple bihashes' do
985
+ receiver = Bihash[receiver: true]
986
+ original_receiver = receiver.dup
987
+ arg1 = Bihash[one: 1]
988
+ arg2 = Bihash[two: 2]
989
+ return_value = Bihash[receiver: true, one: 1, two: 2]
990
+ _(receiver.merge(arg1, arg2)).must_equal return_value
991
+ _(receiver).must_equal original_receiver
992
+ end
993
+
994
+ it 'should merge later bihashes over earlier ones' do
995
+ receiver = Bihash[a: 1]
996
+ original_receiver = receiver.dup
997
+ arg1 = Bihash[a: 2]
998
+ arg2 = Bihash[a: 3]
999
+ return_value = Bihash[a: 3]
1000
+ _(receiver.merge(arg1, arg2)).must_equal return_value
1001
+ _(receiver).must_equal original_receiver
1002
+ end
1003
+
1004
+ it 'should return a new bihash with defaults copied' do
1005
+ bh_default = Bihash.new(404)
1006
+ _(bh_default.merge.default).must_equal 404
1007
+ bh_default_proc = Bihash.new { "hello #{_2}" }
1008
+ _(bh_default_proc.merge.default(:world)).must_equal "hello world"
1009
+ end
1010
+
1011
+ it 'should return a new bihash with compare_by_identity copied' do
1012
+ bh = Bihash[]
1013
+ _(bh.merge.compare_by_identity?).must_equal false
1014
+ bh.compare_by_identity
1015
+ _(bh.merge.compare_by_identity?).must_equal true
1016
+ end
1017
+
1018
+ it 'should raise TypeError if any arg is not a bihash' do
1019
+ _(-> { Bihash.new.merge(Bihash[one: 1], Hash[two: 2]) }).must_raise TypeError
678
1020
  end
679
1021
  end
680
1022
 
@@ -683,21 +1025,39 @@ describe Bihash do
683
1025
  receiver = Bihash[:chips => :salsa, :milk => :cookies, :fish => :rice]
684
1026
  argument = Bihash[:fish => :chips, :soup => :salad]
685
1027
  return_value = Bihash[:milk => :cookies, :fish => :chips, :soup => :salad]
686
- receiver.merge!(argument).must_equal return_value
687
- receiver.must_equal return_value
1028
+ _(receiver.merge!(argument)).must_equal return_value
1029
+ _(receiver).must_equal return_value
688
1030
  end
689
1031
 
690
- it 'should raise RuntimeError if called on a frozen bihash' do
691
- -> { Bihash.new.freeze.merge!(Bihash.new) }.must_raise RuntimeError
1032
+ it 'should accept multiple bihashes' do
1033
+ receiver = Bihash[receiver: true]
1034
+ arg1 = Bihash[one: 1]
1035
+ arg2 = Bihash[two: 2]
1036
+ return_value = Bihash[receiver: true, one: 1, two: 2]
1037
+ _(receiver.merge!(arg1, arg2)).must_equal return_value
1038
+ _(receiver).must_equal return_value
692
1039
  end
693
1040
 
694
- it 'should raise TypeError if arg is not a bihash' do
695
- -> { Bihash.new.merge!({:key => 'value'}) }.must_raise TypeError
1041
+ it 'should merge later bihashes over earlier ones' do
1042
+ receiver = Bihash[a: 1]
1043
+ arg1 = Bihash[a: 2]
1044
+ arg2 = Bihash[a: 3]
1045
+ return_value = Bihash[a: 3]
1046
+ _(receiver.merge!(arg1, arg2)).must_equal return_value
1047
+ _(receiver).must_equal return_value
1048
+ end
1049
+
1050
+ it 'should raise TypeError if any arg is not a bihash' do
1051
+ _(-> { Bihash.new.merge!(Bihash[one: 1], Hash[two: 2]) }).must_raise TypeError
1052
+ end
1053
+
1054
+ it 'should raise FrozenError if called on a frozen bihash' do
1055
+ _(-> { Bihash.new.freeze.merge!(Bihash.new) }).must_raise FrozenError
696
1056
  end
697
1057
 
698
1058
  it 'should be aliased to #update' do
699
1059
  bh = Bihash.new
700
- bh.method(:update).must_equal bh.method(:merge!)
1060
+ _(bh.method(:update)).must_equal bh.method(:merge!)
701
1061
  end
702
1062
  end
703
1063
 
@@ -705,19 +1065,30 @@ describe Bihash do
705
1065
  it 'should recompute all key hash values and return the bihash' do
706
1066
  bh = Bihash[[] => :array]
707
1067
  bh[:array] << 1
708
- bh[[1]].must_be_nil
709
- bh.rehash[[1]].must_equal :array
710
- bh[[1]].must_equal :array
1068
+ _(bh[[1]]).must_be_nil
1069
+ _(bh.rehash[[1]]).must_equal :array
1070
+ _(bh[[1]]).must_equal :array
711
1071
  end
712
1072
 
713
- it 'should raise RuntimeError if called on a frozen bihash' do
714
- -> { Bihash.new.freeze.rehash }.must_raise RuntimeError
1073
+ it 'should raise FrozenError if called on a frozen bihash' do
1074
+ _(-> { Bihash.new.freeze.rehash }).must_raise FrozenError
715
1075
  end
716
1076
 
717
1077
  it 'should raise RuntimeError if called when key duplicated outside pair' do
718
- bh = Bihash[[1], [2], [3], [4]]
719
- (bh[[4]] << 1).shift
720
- -> { bh.rehash }.must_raise RuntimeError
1078
+ a, b, c, d = [:a], [:b], [:c], [:d]
1079
+ bh = Bihash[a => b, c => d]
1080
+ d.replace([:a])
1081
+ _(-> { bh.rehash }).must_raise RuntimeError
1082
+ end
1083
+
1084
+ describe 'when #compare_by_identity is set' do
1085
+ it 'should not raise when there are keys that are eql? but not equal?' do
1086
+ bh = Bihash.new.compare_by_identity
1087
+ foo1, foo2 = "foo", "foo"
1088
+ bh[foo1] = 1
1089
+ bh[foo2] = "foo"
1090
+ bh.rehash
1091
+ end
721
1092
  end
722
1093
  end
723
1094
 
@@ -725,23 +1096,38 @@ describe Bihash do
725
1096
  describe 'when some items are rejected' do
726
1097
  it 'should return a bihash with items not rejected by the block' do
727
1098
  bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
728
- bh.reject { |k1,k2| k1.even? }.must_equal Bihash[1 => :one, 3 => :three]
729
- bh.must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
1099
+ _(bh.reject { |k1,k2| k1.even? }).must_equal Bihash[1 => :one, 3 => :three]
1100
+ _(bh).must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
730
1101
  end
731
1102
  end
732
1103
 
733
1104
  describe 'when no items are rejected' do
734
1105
  it 'should return a bihash with items not rejected by the block' do
735
1106
  bh = Bihash[1 => :one, 3 => :three, 5 => :five, 7 => :seven]
736
- bh.reject { |k1,k2| k1.even? }.must_equal bh
737
- bh.must_equal bh
1107
+ _(bh.reject { |k1,k2| k1.even? }).must_equal bh
1108
+ _(bh).must_equal bh
738
1109
  end
739
1110
  end
740
1111
 
741
1112
  it 'should return an enumerator if not given a block' do
742
1113
  enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].reject
743
- enum.must_be_instance_of Enumerator
744
- enum.each { |k1,k2| k1.even? }.must_equal Bihash[1 => :one, 3 => :three]
1114
+ _(enum).must_be_instance_of Enumerator
1115
+ _(enum.size).must_equal 4
1116
+ _(enum.each { |k1,k2| k1.even? }).must_equal Bihash[1 => :one, 3 => :three]
1117
+ end
1118
+
1119
+ it 'should return a new bihash without defaults copied' do
1120
+ bh_default = Bihash.new(404)
1121
+ _(bh_default.reject { false }.default).must_be_nil
1122
+ bh_default_proc = Bihash.new { 404 }
1123
+ _(bh_default_proc.reject { false }.default_proc).must_be_nil
1124
+ end
1125
+
1126
+ it 'should return a new bihash with compare_by_identity copied' do
1127
+ bh = Bihash[]
1128
+ _(bh.reject { false }.compare_by_identity?).must_equal false
1129
+ bh.compare_by_identity
1130
+ _(bh.reject { false }.compare_by_identity?).must_equal true
745
1131
  end
746
1132
  end
747
1133
 
@@ -749,24 +1135,25 @@ describe Bihash do
749
1135
  it 'should delete any pairs for which the block evaluates to true' do
750
1136
  bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
751
1137
  bh_id = bh.object_id
752
- bh.reject! { |key1, key2| key1.even? }.object_id.must_equal bh_id
753
- bh.must_equal Bihash[1 => :one, 3 => :three]
1138
+ _(bh.reject! { |key1, key2| key1.even? }.object_id).must_equal bh_id
1139
+ _(bh).must_equal Bihash[1 => :one, 3 => :three]
754
1140
  end
755
1141
 
756
1142
  it 'should return nil if no changes were made to the bihash' do
757
1143
  bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
758
- bh.reject! { |key1, key2| key1 > 5 }.must_be_nil
759
- bh.must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
1144
+ _(bh.reject! { |key1, key2| key1 > 5 }).must_be_nil
1145
+ _(bh).must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
760
1146
  end
761
1147
 
762
- it 'should raise RuntimeError if called on a frozen bihash with a block' do
763
- -> { Bihash.new.freeze.reject! { false } }.must_raise RuntimeError
1148
+ it 'should raise FrozenError if called on a frozen bihash with a block' do
1149
+ _(-> { Bihash.new.freeze.reject! { false } }).must_raise FrozenError
764
1150
  end
765
1151
 
766
1152
  it 'should return an enumerator if not given a block' do
767
1153
  enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].reject!
768
- enum.must_be_instance_of Enumerator
769
- enum.each { |k1, k2| k1.even? }.must_equal Bihash[1 => :one, 3 => :three]
1154
+ _(enum).must_be_instance_of Enumerator
1155
+ _(enum.size).must_equal 4
1156
+ _(enum.each { |k1, k2| k1.even? }).must_equal Bihash[1 => :one, 3 => :three]
770
1157
  end
771
1158
  end
772
1159
 
@@ -775,18 +1162,32 @@ describe Bihash do
775
1162
  receiver = Bihash[]
776
1163
  original_id = receiver.object_id
777
1164
  arg = Bihash[:key => 'value']
778
- receiver.replace(arg).must_equal Bihash[:key => 'value']
1165
+ _(receiver.replace(arg)).must_equal Bihash[:key => 'value']
779
1166
  arg[:another_key] = 'another_value'
780
- receiver.object_id.must_equal original_id
781
- receiver.must_equal Bihash[:key => 'value']
1167
+ _(receiver.object_id).must_equal original_id
1168
+ _(receiver).must_equal Bihash[:key => 'value']
1169
+ end
1170
+
1171
+ it 'should copy the default value' do
1172
+ receiver = Bihash[]
1173
+ arg = Bihash.new(404)
1174
+ receiver.replace(arg)
1175
+ _(receiver.default).must_equal 404
1176
+ end
1177
+
1178
+ it 'should copy the default proc' do
1179
+ receiver = Bihash[]
1180
+ arg = Bihash.new { |_, key| key.to_s }
1181
+ receiver.replace(arg)
1182
+ _(receiver.default(:not_a_key)).must_equal 'not_a_key'
782
1183
  end
783
1184
 
784
1185
  it 'should raise TypeError if arg is not a bihash' do
785
- -> { Bihash.new.replace({:key => 'value'}) }.must_raise TypeError
1186
+ _(-> { Bihash.new.replace({:key => 'value'}) }).must_raise TypeError
786
1187
  end
787
1188
 
788
- it 'should raise RuntimeError if called on a frozen bihash' do
789
- -> { Bihash.new.freeze.replace(Bihash[:k, 'v']) }.must_raise RuntimeError
1189
+ it 'should raise FrozenError if called on a frozen bihash' do
1190
+ _(-> { Bihash.new.freeze.replace(Bihash[:k, 'v']) }).must_raise FrozenError
790
1191
  end
791
1192
  end
792
1193
 
@@ -794,28 +1195,43 @@ describe Bihash do
794
1195
  describe 'when only some items are selected' do
795
1196
  it 'should return a bihash with items selected by the block' do
796
1197
  bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
797
- bh.select { |k1,k2| k1.even? }.must_equal Bihash[2 => :two, 4 => :four]
798
- bh.must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
1198
+ _(bh.select { |k1,k2| k1.even? }).must_equal Bihash[2 => :two, 4 => :four]
1199
+ _(bh).must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
799
1200
  end
800
1201
  end
801
1202
 
802
1203
  describe 'when all items are selected' do
803
1204
  it 'should return a bihash with items selected by the block' do
804
1205
  bh = Bihash[2 => :two, 4 => :four, 6 => :six, 8 => :eight]
805
- bh.select { |k1,k2| k1.even? }.must_equal bh
806
- bh.must_equal bh
1206
+ _(bh.select { |k1,k2| k1.even? }).must_equal bh
1207
+ _(bh).must_equal bh
807
1208
  end
808
1209
  end
809
1210
 
810
1211
  it 'should return an enumerator if not given a block' do
811
1212
  enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].select
812
- enum.must_be_instance_of Enumerator
813
- enum.each { |k1,k2| k1.even? }.must_equal Bihash[2 => :two, 4 => :four]
1213
+ _(enum).must_be_instance_of Enumerator
1214
+ _(enum.size).must_equal 4
1215
+ _(enum.each { |k1,k2| k1.even? }).must_equal Bihash[2 => :two, 4 => :four]
1216
+ end
1217
+
1218
+ it 'should return a new bihash without defaults copied' do
1219
+ bh_default = Bihash.new(404)
1220
+ _(bh_default.select { true }.default).must_be_nil
1221
+ bh_default_proc = Bihash.new { 404 }
1222
+ _(bh_default_proc.select { true }.default_proc).must_be_nil
1223
+ end
1224
+
1225
+ it 'should return a new bihash with compare_by_identity copied' do
1226
+ bh = Bihash[]
1227
+ _(bh.select { true }.compare_by_identity?).must_equal false
1228
+ bh.compare_by_identity
1229
+ _(bh.select { true }.compare_by_identity?).must_equal true
814
1230
  end
815
1231
 
816
1232
  it 'should be aliased to #filter' do
817
1233
  bh = Bihash.new
818
- bh.method(:filter).must_equal bh.method(:select)
1234
+ _(bh.method(:filter)).must_equal bh.method(:select)
819
1235
  end
820
1236
  end
821
1237
 
@@ -823,133 +1239,157 @@ describe Bihash do
823
1239
  it 'should retain any pairs for which the block evaluates to true' do
824
1240
  bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
825
1241
  bh_id = bh.object_id
826
- bh.select! { |key1, key2| key1.even? }.object_id.must_equal bh_id
827
- bh.must_equal Bihash[2 => :two, 4 => :four]
1242
+ _(bh.select! { |key1, key2| key1.even? }.object_id).must_equal bh_id
1243
+ _(bh).must_equal Bihash[2 => :two, 4 => :four]
828
1244
  end
829
1245
 
830
1246
  it 'should return nil if no changes were made to the bihash' do
831
1247
  bh = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
832
- bh.select! { |key1, key2| key1 < 5 }.must_be_nil
833
- bh.must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
1248
+ _(bh.select! { |key1, key2| key1 < 5 }).must_be_nil
1249
+ _(bh).must_equal Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four]
834
1250
  end
835
1251
 
836
- it 'should raise RuntimeError if called on a frozen bihash with a block' do
837
- -> { Bihash.new.freeze.select! { true } }.must_raise RuntimeError
1252
+ it 'should raise FrozenError if called on a frozen bihash with a block' do
1253
+ _(-> { Bihash.new.freeze.select! { true } }).must_raise FrozenError
838
1254
  end
839
1255
 
840
1256
  it 'should return an enumerator if not given a block' do
841
1257
  enum = Bihash[1 => :one, 2 => :two, 3 => :three, 4 => :four].select!
842
- enum.must_be_instance_of Enumerator
843
- enum.each { |k1, k2| k1.even? }.must_equal Bihash[2 => :two, 4 => :four]
1258
+ _(enum).must_be_instance_of Enumerator
1259
+ _(enum.size).must_equal 4
1260
+ _(enum.each { |k1, k2| k1.even? }).must_equal Bihash[2 => :two, 4 => :four]
844
1261
  end
845
1262
 
846
1263
  it 'should be aliased to #filter!' do
847
1264
  bh = Bihash.new
848
- bh.method(:filter!).must_equal bh.method(:select!)
1265
+ _(bh.method(:filter!)).must_equal bh.method(:select!)
849
1266
  end
850
1267
  end
851
1268
 
852
1269
  describe '#shift' do
853
1270
  it 'should remove the oldest pair from the bihash and return it' do
854
1271
  bh = Bihash[1 => :one, 2 => :two, 3 => :three]
855
- bh.shift.must_equal [1, :one]
856
- bh.must_equal Bihash[2 => :two, 3 => :three]
1272
+ _(bh.shift).must_equal [1, :one]
1273
+ _(bh).must_equal Bihash[2 => :two, 3 => :three]
857
1274
  end
858
1275
 
859
- it 'should return the default value if bihash is empty' do
860
- Bihash.new.shift.must_be_nil
861
- Bihash.new(404).shift.must_equal 404
862
- Bihash.new { 'd3f4u17' }.shift.must_equal 'd3f4u17'
1276
+ it 'should return nil if bihash is empty' do
1277
+ _(Bihash.new.shift).must_be_nil
1278
+ _(Bihash.new(404).shift).must_be_nil
1279
+ _(Bihash.new { 'd3f4u17' }.shift).must_be_nil
863
1280
  end
864
1281
 
865
- it 'should raise RuntimeError if called on a frozen bihash' do
866
- -> { Bihash.new.freeze.shift }.must_raise RuntimeError
1282
+ it 'should raise FrozenError if called on a frozen bihash' do
1283
+ _(-> { Bihash.new.freeze.shift }).must_raise FrozenError
867
1284
  end
868
1285
  end
869
1286
 
870
1287
  describe '#size' do
871
1288
  it 'should return the number of pairs in the bihash' do
872
- Bihash[1 => :one, 2 => :two].size.must_equal 2
1289
+ _(Bihash[1 => :one, 2 => :two].size).must_equal 2
873
1290
  end
874
1291
  end
875
1292
 
876
1293
  describe '#slice' do
877
1294
  it 'should return a new bihash with only the pairs that are in the args' do
878
1295
  bh = Bihash[1 => :one, 2 => :two, 3 => :three]
879
- bh.slice(1, :one, :two, "nope").must_equal Bihash[1 => :one, 2 => :two]
880
- bh.must_equal Bihash[1 => :one, 2 => :two, 3 => :three]
1296
+ _(bh.slice(1, :one, :two, "nope")).must_equal Bihash[1 => :one, 2 => :two]
1297
+ _(bh).must_equal Bihash[1 => :one, 2 => :two, 3 => :three]
881
1298
  end
882
1299
 
883
- it 'should return a vanilla bihash without default values, etc.' do
884
- sliced_bh = Bihash.new(404).slice
885
- sliced_bh.default.must_be_nil
1300
+ it 'should return a new bihash without defaults copied' do
1301
+ bh_default = Bihash.new(404)
1302
+ _(bh_default.slice.default).must_be_nil
1303
+ bh_default_proc = Bihash.new { 404 }
1304
+ _(bh_default_proc.slice.default_proc).must_be_nil
1305
+ end
1306
+
1307
+ it 'should return a new bihash with compare_by_identity copied' do
1308
+ bh = Bihash[]
1309
+ _(bh.slice.compare_by_identity?).must_equal false
1310
+ bh.compare_by_identity
1311
+ _(bh.slice.compare_by_identity?).must_equal true
886
1312
  end
887
1313
  end
888
1314
 
889
1315
  describe '#to_h' do
890
- it 'should return a copy of the forward hash' do
1316
+ it 'without a block, should return a copy of the forward hash' do
891
1317
  bh = Bihash[:key1 => 'val1', :key2 => 'val2']
892
1318
  h = bh.to_h
893
- h.must_equal Hash[:key1 => 'val1', :key2 => 'val2']
1319
+ _(h).must_equal Hash[:key1 => 'val1', :key2 => 'val2']
894
1320
  h.delete(:key1)
895
- bh.must_include :key1
1321
+ _(bh).must_include :key1
896
1322
  end
897
1323
 
898
- it 'should be aliased to #to_hash' do
899
- bh = Bihash.new
900
- bh.method(:to_hash).must_equal bh.method(:to_h)
1324
+ it 'with a block, should transform pairs from the forward hash' do
1325
+ bh = Bihash[:key1 => 'val1', :key2 => 'val2']
1326
+ h = bh.to_h { |k,v| [k.to_s, v.to_sym] }
1327
+ _(h).must_equal Hash['key1' => :val1, 'key2' => :val2]
1328
+ _(bh).must_equal Bihash[:key1 => 'val1', :key2 => 'val2']
1329
+ end
1330
+ end
1331
+
1332
+ describe '#to_hash' do
1333
+ it 'should return a copy of the forward hash' do
1334
+ bh = Bihash[:key1 => 'val1', :key2 => 'val2']
1335
+ h = bh.to_hash
1336
+ _(h).must_equal Hash[:key1 => 'val1', :key2 => 'val2']
1337
+ h.delete(:key1)
1338
+ _(bh).must_include :key1
901
1339
  end
902
1340
  end
903
1341
 
904
1342
  describe '#to_proc' do
905
1343
  it 'should convert the bihash to a proc' do
906
- Bihash[].to_proc.must_be_instance_of Proc
1344
+ _(Bihash[].to_proc).must_be_instance_of Proc
907
1345
  end
908
1346
 
909
1347
  it 'should call #[] on the bihash when the proc is called' do
910
- Bihash[:key => 'value'].to_proc.call(:key).must_equal 'value'
1348
+ _(Bihash[:key => 'value'].to_proc.call(:key)).must_equal 'value'
911
1349
  end
912
1350
  end
913
1351
 
914
1352
  describe '#to_s' do
915
1353
  it 'should return a nice string representing the bihash' do
916
- bh = Bihash[:k1 => 'v1', :k2 => [:v2], :k3 => {:k4 => 'v4'}]
917
- bh.to_s.must_equal 'Bihash[:k1=>"v1", :k2=>[:v2], :k3=>{:k4=>"v4"}]'
1354
+ h = {:k1 => 'v1', :k2 => [:v2], :k3 => {:k4 => 'v4'}}
1355
+ bh = Bihash[h]
1356
+ keys_and_values_to_s = h.to_s.delete_prefix('{').delete_suffix('}')
1357
+ _(bh.to_s).must_equal "Bihash[#{keys_and_values_to_s}]"
918
1358
  end
919
1359
 
920
1360
  it 'should be aliased to #inspect' do
921
1361
  bh = Bihash.new
922
- bh.method(:inspect).must_equal bh.method(:to_s)
1362
+ _(bh.method(:inspect)).must_equal bh.method(:to_s)
923
1363
  end
924
1364
  end
925
1365
 
926
1366
  describe '#values_at' do
927
1367
  it 'should return an array of values corresponding to the given keys' do
928
- Bihash[1 => :one, 2 => :two].values_at(1, 2).must_equal [:one, :two]
929
- Bihash[1 => :one, 2 => :two].values_at(:one, :two).must_equal [1, 2]
930
- Bihash[1 => :one, 2 => :two].values_at(1, :two).must_equal [:one, 2]
1368
+ _(Bihash[1 => :one, 2 => :two].values_at(1, 2)).must_equal [:one, :two]
1369
+ _(Bihash[1 => :one, 2 => :two].values_at(:one, :two)).must_equal [1, 2]
1370
+ _(Bihash[1 => :one, 2 => :two].values_at(1, :two)).must_equal [:one, 2]
931
1371
  end
932
1372
 
933
1373
  it 'should use the default if a given key is not found' do
934
1374
  bh = Bihash.new(404)
935
1375
  bh[1] = :one
936
1376
  bh[2] = :two
937
- bh.values_at(1, 2, 3).must_equal [:one, :two, 404]
938
- bh.values_at(:one, :two, :three).must_equal [1, 2, 404]
1377
+ _(bh.values_at(1, 2, 3)).must_equal [:one, :two, 404]
1378
+ _(bh.values_at(:one, :two, :three)).must_equal [1, 2, 404]
939
1379
  end
940
1380
 
941
1381
  it 'should not duplicate entries if a key equals its value' do
942
- Bihash[:key => :key].values_at(:key).must_equal [:key]
1382
+ _(Bihash[:key => :key].values_at(:key)).must_equal [:key]
943
1383
  end
944
1384
 
945
1385
  it 'should return an empty array with no args' do
946
- Bihash[:key => 'value'].values_at.must_equal []
1386
+ _(Bihash[:key => 'value'].values_at).must_equal []
947
1387
  end
948
1388
  end
949
1389
 
950
1390
  describe '#initialize' do
951
- it 'should raise RuntimeError if called on a frozen bihash' do
952
- -> { Bihash.new.freeze.send(:initialize) }.must_raise RuntimeError
1391
+ it 'should raise FrozenError if called on a frozen bihash' do
1392
+ _(-> { Bihash.new.freeze.send(:initialize) }).must_raise FrozenError
953
1393
  end
954
1394
  end
955
1395
  end