tofuhash 0.1.0 → 1.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/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
|