tofuhash 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -39,8 +39,8 @@
39
39
  #
40
40
  class TofuHash < Hash
41
41
  module Version
42
- MAJOR = 0
43
- MINOR = 1
42
+ MAJOR = 1
43
+ MINOR = 0
44
44
  REVISION = 0
45
45
  STRING = [MAJOR, MINOR, REVISION].join('.')
46
46
  end
@@ -51,7 +51,13 @@ class TofuHash < Hash
51
51
  if default
52
52
  raise ArgumentError, "Can't initialize Hash with both a default value and a block"
53
53
  end
54
- super() { |hash,key| yield(hash, decode(key)) }
54
+ super() do |hash,key|
55
+ if key.is_a? TofuKey then
56
+ yield(hash, decode(key))
57
+ else
58
+ yield(hash, key)
59
+ end
60
+ end
55
61
  else
56
62
  super
57
63
  end
@@ -89,15 +95,36 @@ class TofuHash < Hash
89
95
  super( encode(key), value )
90
96
  end
91
97
 
92
- alias :regular_each :each unless method_defined?(:regular_each)
98
+ alias :regular_each_pair :each_pair unless method_defined?(:regular_each_pair)
93
99
 
94
100
  # see Hash#each
95
101
  def each &block
96
- self.regular_each do |key, value|
97
- block.call( decode(key), value )
102
+ self.regular_each_pair do |key, value|
103
+ block.call( [decode(key), value] )
98
104
  end
99
105
  end
100
106
 
107
+ # see Hash#each_key
108
+ def each_key &block
109
+ self.regular_each_pair do |key, value|
110
+ block.call(decode(key))
111
+ end
112
+ end
113
+
114
+ # executes the block on each key as it is stored with TofuHash (e.g. the TofuKey)
115
+ def each_key_encoded
116
+ self.regular_each_pair do |key, value|
117
+ yield key
118
+ end
119
+ end
120
+
121
+ # see Hash#each_pair
122
+ def each_pair &block
123
+ self.regular_each_pair do |key,value|
124
+ block.call( decode(key), value )
125
+ end
126
+ end
127
+
101
128
  # see Hash#store
102
129
  def store key, value
103
130
  super( encode(key), value )
@@ -109,10 +136,14 @@ class TofuHash < Hash
109
136
  end
110
137
 
111
138
  # see Hash#has_key?
139
+ # also aliased as include?, key?, and member?
112
140
  def has_key? key
113
141
  super( encode(key) )
114
142
  end
115
-
143
+ alias_method 'include?', 'has_key?'
144
+ alias_method 'key?', 'has_key?'
145
+ alias_method 'member?', 'has_key?'
146
+
116
147
  # see Hash#keys
117
148
  def keys
118
149
  # collect will call each which decodes the key
@@ -137,11 +168,6 @@ class TofuHash < Hash
137
168
  aux
138
169
  end
139
170
 
140
- # see Hash#include?
141
- def include?(key)
142
- super(encode(key))
143
- end
144
-
145
171
  alias :regular_delete_if :delete_if
146
172
 
147
173
  # see Hash#delete_if
@@ -156,4 +182,159 @@ class TofuHash < Hash
156
182
  delete_if{ |key, value| ! yield(key, value) }
157
183
  end
158
184
 
185
+ # see Hash#default
186
+ def default(key = nil)
187
+ super
188
+ end
189
+
190
+ # see Hash#delete
191
+ def delete(key, &block)
192
+ if block_given? then
193
+ super(encode(key)) {|e| yield e.decode }
194
+ else
195
+ super(encode(key))
196
+ end
197
+ end
198
+
199
+ alias :regular_fetch :fetch
200
+ class Missing; end # used to mark a missing argument
201
+
202
+ # see Hash#fetch
203
+ def fetch( key, default = Missing )
204
+ if block_given? then
205
+ if default == Missing then
206
+ super(encode(key)) {|e| yield e.decode }
207
+ else
208
+ super(encode(key),default) {|e| yield e.decode }
209
+ end
210
+ else
211
+ if default == Missing then
212
+ super(encode(key))
213
+ else
214
+ super(encode(key),default)
215
+ end
216
+ end
217
+ end
218
+
219
+ # see Hash#index
220
+ def index( value )
221
+ result = super
222
+ return decode(result) if result.is_a? TofuKey
223
+ return result
224
+ end
225
+
226
+ # see Hash#replace
227
+ def replace( other_hash )
228
+ self.clear
229
+ other_hash.each_pair do |key, value|
230
+ self[key]=value
231
+ end
232
+ end
233
+
234
+ # see Hash#select
235
+ def select
236
+ if RUBY_VERSION >= "1.9"
237
+ result = {}
238
+ each_pair do |key,value|
239
+ result[key]=value if yield(key,value)
240
+ end
241
+ return result
242
+ else
243
+ result = []
244
+ each_pair do |key,value|
245
+ result << [key,value] if yield key,value
246
+ end
247
+ return result
248
+ end
249
+ end
250
+
251
+ # see Hash#shift
252
+ def shift
253
+ result = super
254
+ result[0] = decode(result[0])
255
+ return result
256
+ end
257
+
258
+ # see Hash#sort
259
+ # when called with a block, the keys passed to the block will be the original keys.
260
+ def sort
261
+ return super if RUBY_VERSION >= "1.9"
262
+ if block_given?
263
+ result = super {|kv1,kv2| yield( [decode(kv1[0]),kv1[1]], [decode(kv2[0]),kv2[1]] ) }
264
+ else
265
+ result = super
266
+ end
267
+ result.collect {|a| [a[0].decode, a[1]] }
268
+ end
269
+
270
+ # see Hash#==
271
+ # comparison will use TofuKey comparison to retain the desired behavior.
272
+ def == obj
273
+ return false unless obj.is_a? Hash
274
+ obj.each_pair do |key, value|
275
+ return false unless (v = fetch( key ) {|k| Missing }).eql? value
276
+ end
277
+ return true
278
+ end
279
+
280
+ # see Hash#merge
281
+ def merge( other_hash, &block )
282
+ self.dup.merge!(other_hash, &block)
283
+ end
284
+
285
+ #see Hash#merge!
286
+ # see Hash#update
287
+ def merge!( other_hash )
288
+ other_hash.each_pair do |key,value|
289
+ if block_given? then
290
+ if self.has_key? key then
291
+ self[key] = yield( key, self[key], value )
292
+ else
293
+ self[key] = value
294
+ end
295
+ else
296
+ self[key] = value
297
+ end
298
+ end
299
+ self
300
+ end
301
+ alias_method 'update', 'merge!'
302
+
303
+ #see Hash#reject
304
+ def reject( &block )
305
+ self.dup.delete_if(&block)
306
+ end
307
+
308
+ alias_method 'regular_reject!','reject!'
309
+ #see Hash#reject!
310
+ def reject!( &block )
311
+ self.regular_reject! do |key, value|
312
+ block.call( decode(key), value)
313
+ end
314
+ end
315
+
316
+ #see Hash#to_hash
317
+ def to_hash
318
+ result = Hash.new
319
+ self.each_pair do |key, value|
320
+ result[key]=value
321
+ end
322
+ result
323
+ end
324
+
325
+ #see Hash#to_s
326
+ def to_s
327
+ self.to_hash.to_s
328
+ end
329
+
330
+ # see Hash#try_convert (ruby 1.9.x +)
331
+ def TofuHash.try_convert( obj )
332
+ h = super obj
333
+ return nil if h.nil?
334
+ result = TofuHash.new
335
+ h.each_pair do |key,value|
336
+ result[key] = value
337
+ end
338
+ return result
339
+ end
159
340
  end # class TofuHash
@@ -87,4 +87,20 @@ class TofuKey
87
87
  @coded_key.eql?( obj )
88
88
  end
89
89
  end
90
+
91
+ def == obj
92
+ self.eql? obj
93
+ end
94
+
95
+ def <=> obj
96
+ if obj.instance_of? Symbol
97
+ @coded_key <=> obj.to_s.downcase
98
+ elsif obj.instance_of? TofuKey
99
+ @coded_key <=> obj.coded_key
100
+ elsif obj.respond_to? :downcase
101
+ @coded_key <=> obj.downcase
102
+ else
103
+ @coded_key <=> obj
104
+ end
105
+ end
90
106
  end
data/readme.txt CHANGED
@@ -15,13 +15,13 @@ for Example:
15
15
  puts h[:mixedCaseString] #=> "string"
16
16
  puts h[11] #=> "number"
17
17
 
18
- Version:: 0.1.0 A useful and well tested subset of Hash methods is available.
18
+ Version:: 1.0.0 A well tested, complete version which support all Hash methods.
19
19
 
20
20
  Tested to work with:
21
21
  Ruby 1.8.6
22
22
  Ruby 1.9.1
23
23
 
24
- How is TofuHash differenct than Hash?
24
+ How is TofuHash different than Hash?
25
25
  * TofuHash wraps the key into a TofuKey which is used internally inside TofuHash.
26
26
  The TofuKey defines new behavior for handling the key. By
27
27
  default it will treat Symbol, String, and uppercase vs downcase as the same. This
@@ -30,6 +30,11 @@ How is TofuHash differenct than Hash?
30
30
 
31
31
  * TofuHash adds the method TofuHash#delete_unless
32
32
 
33
+ * TofuHash.compare_by_identity (ruby 1.9 or later) is useless because TofuHash encodes the key into a TofyKey,
34
+ therefore when compare_by_identity is used TofuHash will never match the given key. This matches
35
+ the behavior of Hash with strings for keys. The string is cloned, so compare_by_identity is also
36
+ useless for that situation.
37
+
33
38
  == Links:
34
39
 
35
40
  Start Here:: http://tofuhash.rubyforge.org
@@ -85,6 +90,9 @@ Contributors:
85
90
 
86
91
  == Release History:
87
92
 
93
+ April 4 2009 - 1.0.0 release on rubyforge and git hub. Completed testing the entire Hash interface!
94
+ Added all missing or misbehaving methods from Hash. Tested against both ruby 1.8.6 and 1.9.1.
95
+
88
96
  Feb 28 2009 - 0.1.0 release on rubyforge. Added gem, rdoc, rcov output.
89
97
  Added TofuHash::Version module
90
98
 
@@ -1,25 +1,32 @@
1
+ require 'tofuhash'
1
2
  require 'test/unit'
2
3
  require 'pp'
3
- require 'lib/tofuhash'
4
+ require 'yaml'
4
5
 
5
- =begin rdoc
6
+ =begin
6
7
  test-tofuhash.rb
7
8
 
8
9
  Test cases for TofuHash. This includes modified versions of Ruby's unit tests
9
10
  for Hash, and Ruby's Hash examples in the ri documentation. Plus some custom
10
11
  tests to validate similarities between Hash and TofuHash.
11
12
 
12
- Version: see readme.txt for details.
13
+ Version: see tofuhash.rb for details.
13
14
 
14
15
  License:
15
16
 
16
17
  tofuhash.rb is released under the MIT License + Free Software Foundation
17
- Advertising Prohibition. see readme.txt file for details.
18
+ Advertising Prohibition. see that file for details.
18
19
 
19
20
  test-tofuhash.rb (this file) is released under the Ruby License since it
20
21
  contains source code based on Ruby's released source.
21
22
  =end
22
23
 
24
+ #
25
+ # Note, when asserting a Hash vs a TofuHash; the TofuHash must appear as the
26
+ # left argument. This is because Hash doesn't know how to compare to a TofuHash
27
+ # in this release.
28
+ #
29
+
23
30
  module TofuHashTesting
24
31
  class Test_TofuHash < Test::Unit::TestCase
25
32
  def test_new
@@ -206,6 +213,11 @@ module TofuHashTesting
206
213
  assert_equal( h[b],2 ) #=> 2
207
214
  end
208
215
 
216
+ def test_ri_hash_square_bracket_code
217
+ assert_equal( TofuHash["a", 100, "b", 200], {"a"=>100, "b"=>200} ) #=> {"a"=>100, "b"=>200}
218
+ assert_equal( TofuHash["a" => 100, "b" => 200], {"a"=>100, "b"=>200} ) #=> {"a"=>100, "b"=>200}
219
+ end
220
+
209
221
  def test_ri_hash_new_code
210
222
  h = TofuHash.new("Go Fish")
211
223
  h["a"] = 100
@@ -225,32 +237,99 @@ module TofuHashTesting
225
237
  assert_equal( h.keys, ["c","d"] ) #=> ["c", "d"]
226
238
  end
227
239
 
228
- def test_ri_hash_square_bracket_code
229
- assert_equal( TofuHash["a", 100, "b", 200], {"a"=>100, "b"=>200} ) #=> {"a"=>100, "b"=>200}
230
- assert_equal( TofuHash["a" => 100, "b" => 200], {"a"=>100, "b"=>200} ) #=> {"a"=>100, "b"=>200}
240
+ def test_ri_hash_equality
241
+ =begin
242
+ TODO: KNOWN ISSUE, Hash doesn't know how to compare to TofuHash.
243
+ =end
244
+ h1 = TofuHash[ "a" => 1, "c" => 2 ]
245
+ h2 = { 7 => 35, "c" => 2, "a" => 1 }
246
+ h3 = TofuHash[ "a" => 1, "c" => 2, 7 => 35 ]
247
+ h4 = TofuHash[ "a" => 1, "d" => 2, "f" => 35 ]
248
+ assert_equal( h1 == h2, false ) #=> false
249
+ # assert_equal( h2 == h3, true ) #=> true; however see issue
250
+ assert_equal( h3 == h4, false ) #=> false
231
251
  end
252
+
253
+ def test_ri_element_reference
254
+ h = TofuHash[ "a" => 100, "b" => 200 ]
255
+ assert_equal( h["a"], 100 )
256
+ assert_equal( h["b"], 200 )
257
+
258
+ # case-insensitive access...
259
+ assert_equal( h["A"], 100 )
260
+ assert_equal( h["B"], 200 )
232
261
 
233
- def test_to_a
234
- hash = TofuHash["a" => 2, "b" => 1]
235
- assert_equal [["a",2],["b",1]], hash.to_a
262
+ # indifferent access...
263
+ assert_equal( h[:a], 100 )
264
+ assert_equal( h[:b], 200 )
265
+
266
+ # both
267
+ assert_equal( h[:A], 100 )
268
+ assert_equal( h[:B], 200 )
236
269
  end
270
+
271
+ def test_ri_element_assignment
272
+ h = TofuHash[ "a" => 100, "b" => 200 ]
273
+ h["a"] = 9
274
+ h["c"] = 4
275
+
276
+ # tofu examples...
277
+ h[:a] = 10
278
+ h["D"] = 5
279
+ h[:e] = 6
280
+ h[:F] = 7
281
+ assert_equal( h, {"a"=>10, "b"=>200, "c"=>4, "d"=>5, "e"=>6, "f"=>7} )
282
+ assert_equal( h, {:a=>10, "b"=>200, "c"=>4, "D"=>5, :e=>6, :F=>7} )
283
+ end
284
+
285
+ def test_ri_clear
286
+ h = TofuHash[ "a" => 100, "b" => 200 ]
287
+ h.clear
288
+ assert_equal( h, {} )
289
+ end
290
+
291
+ def test_ri_default_access
292
+ h = TofuHash.new
293
+ assert_nil( h.default )
294
+ assert_nil( h.default(2) )
295
+
296
+ h = TofuHash.new("cat")
297
+ assert_equal( h.default, "cat" )
298
+ assert_equal( h.default(2), "cat" )
237
299
 
238
- def test_ri_to_a
239
- h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
240
- result = h.to_a
241
- sorted = h.to_a.sort { |a,b| a[0] <=> b[0] }
242
- assert_equal( [['a',100],['c',300],['d',400]], sorted )
300
+ h = TofuHash.new {|hash,key| hash[key] = key.to_i*10}
301
+ assert_equal( h.default, 0 )
302
+ assert_equal( h.default(2), 20 )
243
303
  end
244
304
 
245
- def test_include?
246
- hash = TofuHash["a" => 2, "b" => 1]
247
- assert( hash.include?( 'a' ))
305
+ def test_ri_default_assignment
306
+ h = TofuHash[ "a" => 100, "b" => 200 ]
307
+ h.default = "Go fish"
308
+ assert_equal( 100, h["a"] )
309
+ assert_equal( "Go fish", h["z"] )
310
+ h.default = proc do |hash, key|
311
+ hash[key] = key + key
312
+ end
313
+ assert( h[2].is_a?( Proc ))
314
+ assert( h["cat"].is_a?( Proc ))
315
+ assert_same( h[2], h["cat"] )
248
316
  end
249
317
 
250
- def test_ri_include?
251
- h = { "a" => 100, "b" => 200 }
252
- assert( h.has_key?("a") )
253
- assert_equal( false, h.has_key?("z"))
318
+ def test_ri_default_proc
319
+ h = TofuHash.new {|h,k| h[k] = k*k }
320
+ p = h.default_proc
321
+ assert( p.is_a?( Proc ))
322
+ a = []
323
+ p.call( a, 2 )
324
+ assert_equal( [nil, nil, 4], a )
325
+ end
326
+
327
+ def test_ri_delete
328
+ h = TofuHash[ "a" => 100, "b" => 200 ]
329
+ assert_equal( h.delete("a"), 100 )
330
+ assert_equal( h.delete("z"), nil )
331
+ assert_equal( h.delete("z") { |el| "#{el} not found" }, "z not found" )
332
+ assert_equal( h, { "b" => 200 } )
254
333
  end
255
334
 
256
335
  def test_ri_delete_if
@@ -265,21 +344,365 @@ module TofuHashTesting
265
344
  assert_equal TofuHash['b' => 1], hash
266
345
  end
267
346
 
268
- def test_ri_hash_equality
269
- =begin
270
- TODO: KNOWN ISSUE, Hash doesn't know how to compare to TofuHash.
347
+ def test_ri_each
348
+ results = []
349
+ h = TofuHash[ "a" => 100, "b" => 200 ]
350
+ h.each {|key, value| results << "#{key} is #{value}" }
351
+ assert_equal( ["a is 100", "b is 200"], results)
352
+
353
+ results = []
354
+ h.each {|x| results << x}
355
+ assert_equal( [["a", 100], ["b", 200]], results )
356
+ end
357
+
358
+ def test_ri_each_key
359
+ results = []
360
+ h = TofuHash[ "a" => 100, "b" => 200 ]
361
+ h.each_key {|key| results << key }
362
+ assert_equal( ["a", "b"], results)
363
+ end
364
+
365
+ def test_ri_each_pair
366
+ results = []
367
+ h = TofuHash[ "a" => 100, "b" => 200 ]
368
+ h.each_pair {|key, value| results << "#{key} is #{value}" }
369
+ assert_equal( ["a is 100", "b is 200"], results)
370
+ end
371
+
372
+ def test_ri_each_value
373
+ results = []
374
+ h = TofuHash[ "a" => 100, "b" => 200 ]
375
+ h.each_value {|value| results << value }
376
+ assert_equal( [100,200], results)
377
+ end
378
+
379
+ def test_ri_empty?
380
+ h = TofuHash[ "a" => 100, "b" => 200 ]
381
+ assert( !h.empty? )
382
+ h = TofuHash[]
383
+ assert( h.empty? )
384
+ end
385
+
386
+ def test_ri_fetch
387
+ h = TofuHash[ "a" => 100, "b" => 200 ]
388
+ assert_equal( 100, h.fetch("a"))
389
+ assert_equal( "go fish", h.fetch("z", "go fish"))
390
+ assert_equal( "go fish, z", h.fetch("z") { |el| "go fish, #{el}"})
391
+ # supress expected warning message from clutter test output
392
+ old_VERBOSE = $VERBOSE
393
+ $VERBOSE = nil
394
+ #note, this will generate a warning "block supersedes default value argument"
395
+ assert_equal( "go fish, z", h.fetch("z", 99) { |el| "go fish, #{el}"})
396
+ $VERBOSE = old_VERBOSE
397
+ if RUBY_VERSION >= "1.9" then
398
+ assert_raise( KeyError ) do
399
+ h.fetch("z")
400
+ end
401
+ else
402
+ assert_raise( IndexError ) do
403
+ h.fetch("z")
404
+ end
405
+ end
406
+ end
407
+
408
+ def test_ri_has_key
409
+ h = TofuHash[ "a" => 100, "b" => 200 ]
410
+ assert( h.has_key?( "a" ) )
411
+ assert( h.has_key?( :a ) )
412
+ assert( h.has_key?( "A" ) )
413
+ assert( h.has_key?( :A ) )
414
+ assert( !h.has_key?( "z" ) )
415
+ end
416
+
417
+ def test_ri_include
418
+ h = TofuHash[ "a" => 100, "b" => 200 ]
419
+ assert( h.include?( "a" ) )
420
+ assert( h.include?( :a ) )
421
+ assert( h.include?( "A" ) )
422
+ assert( h.include?( :A ) )
423
+ assert( !h.include?( "z" ) )
424
+ end
271
425
 
272
- h1 = TofuHash[ "a" => 1, "c" => 2 ]
273
- h2 = { 7 => 35, "c" => 2, "a" => 1 }
274
- h3 = TofuHash[ "a" => 1, "c" => 2, 7 => 35 ]
275
- h4 = TofuHash[ "a" => 1, "d" => 2, "f" => 35 ]
276
- puts "h1"
277
- assert_equal( h1 == h2, false ) #=> false
278
- puts "h2"
279
- assert_equal( h2 == h3, true ) #=>
280
- puts "h3"
281
- assert_equal( h3 == h4, false ) #=> false
282
- =end
426
+ def test_include?
427
+ hash = TofuHash["a" => 2, "b" => 1]
428
+ assert( hash.include?( 'a' ))
429
+ end
430
+
431
+ def test_ri_include?
432
+ h = { "a" => 100, "b" => 200 }
433
+ assert( h.has_key?("a") )
434
+ assert_equal( false, h.has_key?("z"))
435
+ end
436
+
437
+ def test_ri_key_question
438
+ h = TofuHash[ "a" => 100, "b" => 200 ]
439
+ assert( h.key?( "a" ) )
440
+ assert( h.key?( :a ) )
441
+ assert( h.key?( "A" ) )
442
+ assert( h.key?( :A ) )
443
+ assert( !h.key?( "z" ) )
444
+ end
445
+
446
+ def test_ri_member_question
447
+ h = TofuHash[ "a" => 100, "b" => 200 ]
448
+ assert( h.member?( "a" ) )
449
+ assert( h.member?( :a ) )
450
+ assert( h.member?( "A" ) )
451
+ assert( h.member?( :A ) )
452
+ assert( !h.member?( "z" ) )
453
+ end
454
+
455
+ def test_ri_has_value
456
+ h = TofuHash[ "a" => 100, "b" => 200 ]
457
+ assert( h.has_value?( 100 ))
458
+ assert( !h.has_value?( 999 ))
459
+ end
460
+
461
+ def test_ri_value_question
462
+ h = TofuHash[ "a" => 100, "b" => 200 ]
463
+ assert( h.value?( 100 ))
464
+ assert( !h.value?( 999 ))
465
+ end
466
+
467
+ def test_ri_index
468
+ old_VERBOSE = $VERBOSE
469
+ $VERBOSE = nil
470
+ # supress deprecated warning message (1.9)
471
+ h = TofuHash[ "a" => 100, "b" => 200 ]
472
+ assert_equal( "b", h.index( 200 ))
473
+ assert_equal( nil, h.index( 999 ))
474
+ $VERBOSE = old_VERBOSE
475
+ end
476
+
477
+ def test_ri_replace
478
+ h = TofuHash[ "a" => 100, "b" => 200 ]
479
+ h2 = {"c" => 300, "d" => 400 }
480
+ h.replace( h2 )
481
+ assert_equal( h, h2 )
482
+ h2["c"] = 999
483
+ assert_equal( 300, h["c"] )
484
+ h.each_key_encoded do |key|
485
+ assert_instance_of( TofuKey, key )
486
+ end
487
+ end
488
+
489
+ def test_ri_invert
490
+ h = TofuHash[ "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 ]
491
+ if RUBY_VERSION >= "1.9" then
492
+ assert_equal( h.invert, {0=>"a", 100=>"m", 200=>"d", 300=>"y"} )
493
+ else
494
+ assert_equal( h.invert, {0=>"a", 100=>"n", 200=>"d", 300=>"y"} )
495
+ end
496
+ end
497
+
498
+ def test_ri_keys
499
+ h = { "a" => 100, "b" => 200, "c" => 300, "d" => 400 }
500
+ assert_equal( ["a", "b", "c", "d"], h.keys )
501
+ end
502
+
503
+ def test_ri_length_and_size
504
+ h = TofuHash[ "d" => 100, "a" => 200, "v" => 300, "e" => 400 ]
505
+ assert_equal( 4, h.length )
506
+ assert_equal( 4, h.size )
507
+ assert_equal( 200, h.delete("a") )
508
+ assert_equal( 3, h.length )
509
+ assert_equal( 3, h.size )
510
+ end
511
+
512
+ def test_ri_merge
513
+ h1 = TofuHash[ "a" => 100, "b" => 200 ]
514
+ h2 = { "b" => 254, "c" => 300 }
515
+ result = h1.merge(h2)
516
+ assert_equal( result, {"a"=>100, "b"=>254, "c"=>300} )
517
+ result.regular_each_pair do | key, value |
518
+ assert_instance_of( TofuKey, key )
519
+ end
520
+ assert_equal( h1, {"a"=>100, "b" => 200 })
521
+
522
+ result = h1.merge(h2) do |key,old_val,new_val|
523
+ assert_equal( "b", key )
524
+ assert_equal( 200, old_val )
525
+ assert_equal( 254, new_val )
526
+ :happy
527
+ end
528
+ assert_equal( result, {"a"=>100, "b"=>:happy, "c"=>300} )
529
+ result.regular_each_pair do | key, value |
530
+ assert_instance_of( TofuKey, key )
531
+ end
532
+ assert_equal( h1, {"a"=>100, "b" => 200 })
533
+ end
534
+
535
+ def test_ri_merge!
536
+ h1 = TofuHash[ "a" => 100, "b" => 200 ]
537
+ h2 = { "b" => 254, "c" => 300 }
538
+ result = h1.merge!(h2)
539
+ assert_equal( result, {"a"=>100, "b"=>254, "c"=>300} )
540
+ assert_equal( h1, result)
541
+ end
542
+
543
+ def test_ri_update
544
+ h1 = TofuHash[ "a" => 100, "b" => 200 ]
545
+ h2 = { "b" => 254, "c" => 300 }
546
+ result = h1.update(h2)
547
+ assert_equal( result, {"a"=>100, "b"=>254, "c"=>300} )
548
+ assert_equal( h1, result)
549
+ end
550
+
551
+ def test_ri_rehash
552
+ a = [ "a", "b" ]
553
+ c = [ "c", "d" ]
554
+ h = TofuHash[ a => 100, c => 300 ]
555
+ assert_equal( 100, h[a] )
556
+ a[0] = "z"
557
+ assert_equal( nil, h[a] )
558
+ assert_equal( h.rehash, {["z", "b"]=>100, ["c", "d"]=>300} )
559
+ assert_equal( 100, h[a] )
560
+ end
561
+
562
+ def test_ri_reject
563
+ h = TofuHash["a" => 100, "b" => 200, "c" => 300]
564
+ result = h.reject { |key,value| key >= 'b' }
565
+ assert_equal TofuHash['a' => 100], result
566
+ assert_equal TofuHash["a" => 100, "b" => 200, "c" => 300], h
567
+ end
568
+
569
+ def test_ri_reject!
570
+ h = TofuHash["a" => 100, "b" => 200, "c" => 300]
571
+ result = h.reject! { |key,value| key >= 'b' }
572
+ assert_equal TofuHash['a' => 100], result
573
+ assert_equal TofuHash["a" => 100], h
574
+
575
+ result = h.reject! { |key,value| key >= 'b' }
576
+ assert_equal nil, result
577
+ assert_equal TofuHash["a" => 100], h
578
+ end
579
+
580
+ def test_ri_select
581
+ h = TofuHash[ "a" => 100, "b" => 200, "c" => 300 ]
582
+ if RUBY_VERSION < "1.9" then
583
+ assert_equal( [["b", 200], ["c", 300]], h.select {|k,v| k > "a"} )
584
+ assert_equal( [["a", 100]], h.select {|k,v| v < 200} )
585
+ else
586
+ assert_equal( {"b"=> 200,"c"=>300}, h.select {|k,v| k > "a"} )
587
+ assert_equal( {"a"=> 100}, h.select {|k,v| v < 200} )
588
+ end
589
+ end
590
+
591
+ def test_ri_shift
592
+ h = TofuHash[ 1 => "a", 2 => "b", 3 => "c" ]
593
+ assert_equal( [1,"a"], h.shift )
594
+ assert_equal( h, {2 => "b", 3 => "c"} )
595
+ end
596
+
597
+ def test_ri_sort
598
+ h = TofuHash[ "a" => 20, "b" => 30, "c" => 10 ]
599
+ assert_equal( [["a", 20], ["b", 30], ["c", 10]], h.sort )
600
+ assert_equal( [["c", 10], ["a", 20], ["b", 30]], h.sort {|a,b| a[1]<=>b[1]} )
601
+ end
602
+
603
+ def test_ri_store
604
+ h = TofuHash[ "a" => 100, "b" => 200 ]
605
+ h.store("a",9)
606
+ h.store("c",4)
607
+
608
+ # tofu examples...
609
+ h.store(:a,10)
610
+ h.store("D",5)
611
+ h.store(:e,6)
612
+ h.store(:F,7)
613
+ assert_equal( h, {"a"=>10, "b"=>200, "c"=>4, "d"=>5, "e"=>6, "f"=>7} )
614
+ assert_equal( h, {:a=>10, "b"=>200, "c"=>4, "D"=>5, :e=>6, :F=>7} )
615
+ end
616
+
617
+ def test_to_a
618
+ hash = TofuHash["a" => 2, "b" => 1]
619
+ assert_equal [["a",2],["b",1]], hash.to_a
620
+ end
621
+
622
+ def test_ri_to_a
623
+ h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
624
+ result = h.to_a
625
+ sorted = h.to_a.sort { |a,b| a[0] <=> b[0] }
626
+ assert_equal( [['a',100],['c',300],['d',400]], sorted )
627
+ end
628
+
629
+ def test_ri_to_hash
630
+ h = TofuHash["a" => 2, "b" => 1]
631
+ hash = h.to_hash
632
+ assert_same( Hash, hash.class )
633
+ assert_equal( {"a" => 2, "b" => 1}, hash )
634
+ end
635
+
636
+ def test_ri_to_s
637
+ # note this was modified from 1.8.6 in expectation of 1.9 which should preserve insert order.
638
+ h = TofuHash[ "a" => 100, "c" => 300, "d" => 400 ]
639
+ if RUBY_VERSION < "1.9"
640
+ assert_equal( h.to_s, "a100c300d400" )
641
+ else
642
+ assert_equal( h.to_s, '{"a"=>100, "c"=>300, "d"=>400}' )
643
+ end
644
+ end
645
+
646
+ def test_ri_to_yaml
647
+ h = TofuHash[ :a => 100, "c" => 300, "d" => 400 ]
648
+ serialized = h.to_yaml
649
+ new_obj = YAML::load(serialized)
650
+ assert_equal( h, new_obj )
651
+ end
652
+
653
+ def test_ri_values
654
+ h = TofuHash[ "a" => 100, "b" => 200, "c" => 300 ]
655
+ assert_equal( [100, 200, 300], h.values )
656
+ end
657
+
658
+ def test_ri_values_at
659
+ h = TofuHash[ "cat" => "feline", "dog" => "canine", "cow" => "bovine" ]
660
+ assert_equal( ["bovine", "feline"], h.values_at("cow", "cat"))
661
+ assert_equal( ["bovine", "feline"], h.values_at("Cow", "cAt"))
662
+ assert_equal( ["bovine", "feline"], h.values_at(:cow, :cAt))
663
+ end
664
+
665
+ def test_compare_by_identity
666
+ return if RUBY_VERSION < "1.9"
667
+ # adapted from http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l85
668
+ h1 = TofuHash[ "a" => 100, "b" => 200, :c => "c" ]
669
+ assert_same(h1.compare_by_identity, h1)
670
+ assert( h1.compare_by_identity? )
671
+ assert_equal( nil, h1["a"] ) # => nil # different objects.
672
+ assert_equal( nil, h1[:c] ) # => nil # note Hash would return "c" since same symbols are all same. But TofuHash duplicates the key (just like a string key in Hash).
673
+ end
674
+
675
+ def test_try_convert
676
+ return if RUBY_VERSION < "1.9"
677
+ assert_equal( nil, TofuHash.try_convert( [:a,:b] ) )
678
+ result = TofuHash.try_convert( {:a => :b} )
679
+ assert_instance_of( TofuHash, result )
680
+ assert_equal( result, {:a=>:b} )
283
681
  end
284
682
  end # class TestHash
683
+
684
+ class TestTofuKey < Test::Unit::TestCase
685
+ def test_eql?
686
+ k = TofuKey.new :happy
687
+ assert_equal( k, :HAPPY )
688
+
689
+ k = TofuKey.new 123
690
+ assert_equal( k, 123 )
691
+ end
692
+
693
+ def test_compare
694
+ k = TofuKey.new :happy
695
+ assert_equal( 1, k <=> :Angry )
696
+ assert_equal( 1, k <=> "angRY" )
697
+ assert_equal( 0, k <=> :HAPPY )
698
+ assert_equal( 0, k <=> "Happy" )
699
+ assert_equal( -1, k <=> :Sad )
700
+ assert_equal( -1, k <=> "sad" )
701
+
702
+ k = TofuKey.new 123
703
+ assert_equal( 1, k <=> 122 )
704
+ assert_equal( 0, k <=> 123 )
705
+ assert_equal( -1, k <=> 124 )
706
+ end
707
+ end
285
708
  end # module TofuHash
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: tofuhash
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.0
7
- date: 2009-02-28 00:00:00 -06:00
6
+ version: 1.0.0
7
+ date: 2009-04-04 00:00:00 -05:00
8
8
  summary: TofuHash, a Hash that is case-insensitive and treats symbols and strings as equals (customizable); always preserving the original key.
9
9
  require_paths:
10
10
  - lib