tofuhash 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/tofuhash/tofuhash.rb +193 -12
- data/lib/tofuhash/tofukey.rb +16 -0
- data/readme.txt +10 -2
- data/test/test-tofuhash.rb +459 -36
- metadata +2 -2
data/lib/tofuhash/tofuhash.rb
CHANGED
@@ -39,8 +39,8 @@
|
|
39
39
|
#
|
40
40
|
class TofuHash < Hash
|
41
41
|
module Version
|
42
|
-
MAJOR =
|
43
|
-
MINOR =
|
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()
|
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 :
|
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.
|
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
|
data/lib/tofuhash/tofukey.rb
CHANGED
@@ -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::
|
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
|
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
|
|
data/test/test-tofuhash.rb
CHANGED
@@ -1,25 +1,32 @@
|
|
1
|
+
require 'tofuhash'
|
1
2
|
require 'test/unit'
|
2
3
|
require 'pp'
|
3
|
-
require '
|
4
|
+
require 'yaml'
|
4
5
|
|
5
|
-
=begin
|
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
|
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
|
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
|
229
|
-
|
230
|
-
|
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
|
-
|
234
|
-
|
235
|
-
assert_equal [
|
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
|
-
|
239
|
-
h
|
240
|
-
|
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
|
246
|
-
|
247
|
-
|
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
|
251
|
-
h = {
|
252
|
-
|
253
|
-
|
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
|
269
|
-
=
|
270
|
-
|
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
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
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:
|
7
|
-
date: 2009-
|
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
|