C0 code coverage information
Generated on Sat Feb 02 17:44:28 +0100 2008 with rcov 0.8.1.2
Code reported as executed by Ruby looks like this...
and this: this line is also marked as covered.
Lines considered as run by rcov, but not reported by Ruby, look like this,
and this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not executed.
1 # = dictionary.rb
2 #
3 # == Copyright (c) 2005 Jan Molic, Thomas Sawyer
4 #
5 # Ruby License
6 #
7 # This module is free software. You may use, modify, and/or redistribute this
8 # software under the same terms as Ruby.
9 #
10 # This program is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 # FOR A PARTICULAR PURPOSE.
13 #
14 # == Special Thanks
15 #
16 # Ported from OrderHash 2.0, Copyright (c) 2005 jan molic
17 #
18 # Thanks to Andrew Johnson for his suggestions and fixes of Hash[],
19 # merge, to_a, inspect and shift.
20 #
21 # == Authors & Contributors
22 #
23 # * Jan Molic
24 # * Thomas Sawyer
25
26 # Author:: Jan Molic
27 # Copyright:: Copyright (c) 2006 Jan Molic
28 # License:: Ruby License
29
30 # = Dictionary
31 #
32 # The Dictionary class is a Hash that preserves order.
33 # So it has some array-like extensions also. By defualt
34 # a Dictionary object preserves insertion order, but any
35 # order can be specified including alphabetical key order.
36 #
37 # == Usage
38 #
39 # Just require this file and use Dictionary instead of Hash.
40 #
41 # # You can do simply
42 # hsh = Dictionary.new
43 # hsh['z'] = 1
44 # hsh['a'] = 2
45 # hsh['c'] = 3
46 # p hsh.keys #=> ['z','a','c']
47 #
48 # # or using Dictionary[] method
49 # hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
50 # p hsh.keys #=> ['z','a','c']
51 #
52 # # but this don't preserve order
53 # hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
54 # p hsh.keys #=> ['a','c','z']
55 #
56 # # Dictionary has useful extensions: push, pop and unshift
57 # p hsh.push('to_end', 15) #=> true, key added
58 # p hsh.push('to_end', 30) #=> false, already - nothing happen
59 # p hsh.unshift('to_begin', 50) #=> true, key added
60 # p hsh.unshift('to_begin', 60) #=> false, already - nothing happen
61 # p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"]
62 # p hsh.pop #=> ["to_end", 15], if nothing remains, return nil
63 # p hsh.keys #=> ["to_begin", "a", "c", "z"]
64 # p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil
65 #
66 # == Usage Notes
67 #
68 # * You can use #order_by to set internal sort order.
69 # * #<< takes a two element [k,v] array and inserts.
70 # * Use ::auto which creates Dictionay sub-entries as needed.
71 # * And ::alpha which creates a new Dictionary sorted by key.
72
73 module Ramaze
74 class Dictionary
75
76 class << self
77
78 #--
79 # TODO is this needed? Doesn't the super class do this?
80 #++
81
82 def []( *args )
83 hsh = new
84 if Hash === args[0]
85 hsh.replace(args[0])
86 elsif (args.size % 2) != 0
87 raise ArgumentError, "odd number of elements for Hash"
88 else
89 while !args.empty?
90 hsh[args.shift] = args.shift
91 end
92 end
93 hsh
94 end
95
96 # Like #new but the block sets the order.
97 #
98 def new_by( *args, &blk )
99 new(*args).order_by(&blk)
100 end
101
102 # Alternate to #new which creates a dictionary sorted by key.
103 #
104 # d = Dictionary.alpha
105 # d["z"] = 1
106 # d["y"] = 2
107 # d["x"] = 3
108 # d #=> {"x"=>3,"y"=>2,"z"=>2}
109 #
110 # This is equivalent to:
111 #
112 # Dictionary.new.order_by { |key,value| key }
113
114 def alpha( *args, &block )
115 new( *args, &block ).order_by_key
116 end
117
118 # Alternate to #new which auto-creates sub-dictionaries as needed.
119 #
120 # d = Dictionary.auto
121 # d["a"]["b"]["c"] = "abc" #=> { "a"=>{"b"=>{"c"=>"abc"}}}
122 #
123 def auto( *args )
124 #AutoDictionary.new(*args)
125 leet = lambda { |hsh, key| hsh[key] = new( &leet ) }
126 new(*args, &leet)
127 end
128 end
129
130 def initialize( *args, &blk )
131 @order = []
132 @order_by = nil
133 @hash = Hash.new( *args, &blk )
134 end
135
136 def order
137 reorder if @order_by
138 @order
139 end
140
141 # Keep dictionary sorted by a specific sort order.
142
143 def order_by( &block )
144 @order_by = block
145 order
146 self
147 end
148
149 # Keep dictionary sorted by key.
150 #
151 # d = Dictionary.new.order_by_key
152 # d["z"] = 1
153 # d["y"] = 2
154 # d["x"] = 3
155 # d #=> {"x"=>3,"y"=>2,"z"=>2}
156 #
157 # This is equivalent to:
158 #
159 # Dictionary.new.order_by { |key,value| key }
160 #
161 # The initializer Dictionary#alpha also provides this.
162
163 def order_by_key
164 @order_by = lambda { |k,v| k }
165 order
166 self
167 end
168
169 # Keep dictionary sorted by value.
170 #
171 # d = Dictionary.new.order_by_value
172 # d["z"] = 1
173 # d["y"] = 2
174 # d["x"] = 3
175 # d #=> {"x"=>3,"y"=>2,"z"=>2}
176 #
177 # This is equivalent to:
178 #
179 # Dictionary.new.order_by { |key,value| value }
180
181 def order_by_value
182 @order_by = lambda { |k,v| v }
183 order
184 self
185 end
186
187 #
188
189 def reorder
190 if @order_by
191 assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by( &@order_by )
192 @order = assoc.collect{ |k,v| k }
193 end
194 @order
195 end
196
197 #def ==( hsh2 )
198 # return false if @order != hsh2.order
199 # super hsh2
200 #end
201
202 def ==( hsh2 )
203 if hsh2.is_a?( Dictionary )
204 @order == hsh2.order &&
205 @hash == hsh2.instance_variable_get("@hash")
206 else
207 false
208 end
209 end
210
211 def [] k
212 @hash[ k ]
213 end
214
215 def fetch( k )
216 @hash.fetch( k )
217 end
218
219 # Store operator.
220 #
221 # h[key] = value
222 #
223 # Or with additional index.
224 #
225 # h[key,index] = value
226
227 def []=(k, i=nil, v=nil)
228 if v
229 insert(i,k,v)
230 else
231 store(k,i)
232 end
233 end
234
235 def insert( i,k,v )
236 @order.insert( i,k )
237 @hash.store( k,v )
238 end
239
240 def store( a,b )
241 @order.push( a ) unless @hash.has_key?( a )
242 @hash.store( a,b )
243 end
244
245 def clear
246 @order = []
247 @hash.clear
248 end
249
250 def delete( key )
251 @order.delete( key )
252 @hash.delete( key )
253 end
254
255 def each_key
256 order.each { |k| yield( k ) }
257 self
258 end
259
260 def each_value
261 order.each { |k| yield( @hash[k] ) }
262 self
263 end
264
265 def each
266 order.each { |k| yield( k,@hash[k] ) }
267 self
268 end
269 alias each_pair each
270
271 def delete_if
272 order.clone.each { |k| delete k if yield }
273 self
274 end
275
276 def values
277 ary = []
278 order.each { |k| ary.push @hash[k] }
279 ary
280 end
281
282 def keys
283 order
284 end
285
286 def invert
287 hsh2 = self.class.new
288 order.each { |k| hsh2[@hash[k]] = k }
289 hsh2
290 end
291
292 def reject( &block )
293 self.dup.delete_if &block
294 end
295
296 def reject!( &block )
297 hsh2 = reject &block
298 self == hsh2 ? nil : hsh2
299 end
300
301 def replace( hsh2 )
302 @order = hsh2.order
303 @hash = hsh2.hash
304 end
305
306 def shift
307 key = order.first
308 key ? [key,delete(key)] : super
309 end
310
311 def unshift( k,v )
312 unless @hash.include?( k )
313 @order.unshift( k )
314 @hash.store( k,v )
315 true
316 else
317 false
318 end
319 end
320
321 def <<(kv)
322 push *kv
323 end
324
325 def push( k,v )
326 unless @hash.include?( k )
327 @order.push( k )
328 @hash.store( k,v )
329 true
330 else
331 false
332 end
333 end
334
335 def pop
336 key = order.last
337 key ? [key,delete(key)] : nil
338 end
339
340 def to_a
341 ary = []
342 each { |k,v| ary << [k,v] }
343 ary
344 end
345
346 def to_s
347 self.to_a.to_s
348 end
349
350 def inspect
351 ary = []
352 each {|k,v| ary << k.inspect + "=>" + v.inspect}
353 '{' + ary.join(", ") + '}'
354 end
355
356 def dup
357 self.class[*to_a.flatten]
358 end
359
360 def update( hsh2 )
361 hsh2.each { |k,v| self[k] = v }
362 reorder
363 self
364 end
365 alias :merge! update
366
367 def merge( hsh2 )
368 self.dup.update(hsh2)
369 end
370
371 def select
372 ary = []
373 each { |k,v| ary << [k,v] if yield k,v }
374 ary
375 end
376
377 def find
378 each{|k,v| return k, v if yield(k,v) }
379 return nil
380 end
381
382 def first
383 @hash[order.first]
384 end
385
386 def last
387 @hash[order.last]
388 end
389
390 def length
391 @order.length
392 end
393 alias :size :length
394
395 def empty?
396 @hash.empty?
397 end
398
399 end
400 end
401
402
403
404 # _____ _
405 # |_ _|__ ___| |_
406 # | |/ _ \/ __| __|
407 # | | __/\__ \ |_
408 # |_|\___||___/\__|
409 #
410
411 =begin testing
412
413 require 'test/unit'
414
415 class TC_Dictionary < Test::Unit::TestCase
416
417 def test_create
418 hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
419 assert_equal( ['z','a','c'], hsh.keys )
420 end
421
422 def test_op_store
423 hsh = Dictionary.new
424 hsh['z'] = 1
425 hsh['a'] = 2
426 hsh['c'] = 3
427 assert_equal( ['z','a','c'], hsh.keys )
428 end
429
430 def test_push
431 hsh = Dictionary['a', 1, 'c', 2, 'z', 3]
432 assert( hsh.push('end', 15) )
433 assert_equal( 15, hsh['end'] )
434 assert( ! hsh.push('end', 30) )
435 assert( hsh.unshift('begin', 50) )
436 assert_equal( 50, hsh['begin'] )
437 assert( ! hsh.unshift('begin', 60) )
438 assert_equal( ["begin", "a", "c", "z", "end"], hsh.keys )
439 assert_equal( ["end", 15], hsh.pop )
440 assert_equal( ["begin", "a", "c", "z"], hsh.keys )
441 assert_equal( ["begin", 50], hsh.shift )
442 end
443
444 def test_insert
445 # front
446 h = Dictionary['a', 1, 'b', 2, 'c', 3]
447 r = Dictionary['d', 4, 'a', 1, 'b', 2, 'c', 3]
448 assert_equal( 4, h.insert(0,'d',4) )
449 assert_equal( r, h )
450 # back
451 h = Dictionary['a', 1, 'b', 2, 'c', 3]
452 r = Dictionary['a', 1, 'b', 2, 'c', 3, 'd', 4]
453 assert_equal( 4, h.insert(-1,'d',4) )
454 assert_equal( r, h )
455 end
456
457 def test_update
458 # with other orderred hash
459 h1 = Dictionary['a', 1, 'b', 2, 'c', 3]
460 h2 = Dictionary['d', 4]
461 r = Dictionary['a', 1, 'b', 2, 'c', 3, 'd', 4]
462 assert_equal( r, h1.update(h2) )
463 assert_equal( r, h1 )
464 # with other hash
465 h1 = Dictionary['a', 1, 'b', 2, 'c', 3]
466 h2 = { 'd' => 4 }
467 r = Dictionary['a', 1, 'b', 2, 'c', 3, 'd', 4]
468 assert_equal( r, h1.update(h2) )
469 assert_equal( r, h1 )
470 end
471
472 def test_merge
473 # with other orderred hash
474 h1 = Dictionary['a', 1, 'b', 2, 'c', 3]
475 h2 = Dictionary['d', 4]
476 r = Dictionary['a', 1, 'b', 2, 'c', 3, 'd', 4]
477 assert_equal( r, h1.merge(h2) )
478 # with other hash
479 h1 = Dictionary['a', 1, 'b', 2, 'c', 3]
480 h2 = { 'd' => 4 }
481 r = Dictionary['a', 1, 'b', 2, 'c', 3, 'd', 4]
482 assert_equal( r, h1.merge(h2) )
483 end
484
485 def test_order_by
486 h1 = Dictionary['a', 3, 'b', 2, 'c', 1]
487 h1.order_by{ |k,v| v }
488 assert_equal( [1,2,3], h1.values )
489 assert_equal( ['c','b','a'], h1.keys )
490 end
491
492 def test_op_store
493 h1 = Dictionary[]
494 h1[:a] = 1
495 h1[:c] = 3
496 assert_equal( [1,3], h1.values )
497 h1[:b,1] = 2
498 assert_equal( [1,2,3], h1.values )
499 assert_equal( [:a,:b,:c], h1.keys )
500 end
501
502 end
503
504 =end
Generated using the rcov code coverage analysis tool for Ruby version 0.8.1.2.