hashery 1.5.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/.ruby +30 -17
  2. data/.yardopts +1 -0
  3. data/Config.rb +28 -0
  4. data/{QED.rdoc → DEMO.rdoc} +0 -0
  5. data/HISTORY.rdoc +37 -0
  6. data/LICENSE.txt +26 -0
  7. data/NOTICE.txt +46 -0
  8. data/README.rdoc +10 -7
  9. data/lib/hashery.rb +6 -6
  10. data/lib/hashery.yml +30 -17
  11. data/lib/hashery/association.rb +169 -109
  12. data/lib/hashery/casting_hash.rb +128 -135
  13. data/lib/hashery/core_ext.rb +89 -61
  14. data/lib/hashery/crud_hash.rb +365 -0
  15. data/lib/hashery/dictionary.rb +545 -345
  16. data/lib/hashery/fuzzy_hash.rb +177 -125
  17. data/lib/hashery/ini_hash.rb +321 -0
  18. data/lib/hashery/key_hash.rb +54 -179
  19. data/lib/hashery/linked_list.rb +245 -191
  20. data/lib/hashery/lru_hash.rb +292 -202
  21. data/lib/hashery/open_cascade.rb +133 -78
  22. data/lib/hashery/open_hash.rb +127 -61
  23. data/lib/hashery/ordered_hash.rb +128 -122
  24. data/lib/hashery/path_hash.rb +238 -0
  25. data/lib/hashery/property_hash.rb +144 -80
  26. data/lib/hashery/query_hash.rb +85 -29
  27. data/lib/hashery/stash.rb +7 -3
  28. data/lib/hashery/static_hash.rb +46 -41
  29. data/test/case_association.rb +65 -4
  30. data/test/case_dictionary.rb +149 -5
  31. data/test/{case_keyhash.rb → case_key_hash.rb} +20 -14
  32. data/test/case_lru_hash.rb +162 -0
  33. data/test/{case_opencascade.rb → case_open_cascade.rb} +4 -8
  34. data/test/case_open_hash.rb +87 -0
  35. data/test/case_query_hash.rb +226 -0
  36. data/test/helper.rb +8 -0
  37. metadata +33 -63
  38. data/COPYING.rdoc +0 -45
  39. data/lib/hashery/basic_object.rb +0 -74
  40. data/lib/hashery/basic_struct.rb +0 -288
  41. data/lib/hashery/basicobject.rb +0 -1
  42. data/lib/hashery/basicstruct.rb +0 -1
  43. data/lib/hashery/castinghash.rb +0 -1
  44. data/lib/hashery/fuzzyhash.rb +0 -1
  45. data/lib/hashery/ini.rb +0 -268
  46. data/lib/hashery/keyhash.rb +0 -1
  47. data/lib/hashery/linkedlist.rb +0 -1
  48. data/lib/hashery/lruhash.rb +0 -1
  49. data/lib/hashery/memoizer.rb +0 -64
  50. data/lib/hashery/open_object.rb +0 -1
  51. data/lib/hashery/opencascade.rb +0 -1
  52. data/lib/hashery/openhash.rb +0 -1
  53. data/lib/hashery/openobject.rb +0 -1
  54. data/lib/hashery/orderedhash.rb +0 -1
  55. data/lib/hashery/ostructable.rb +0 -186
  56. data/lib/hashery/propertyhash.rb +0 -1
  57. data/lib/hashery/queryhash.rb +0 -1
  58. data/lib/hashery/statichash.rb +0 -1
  59. data/qed/01_openhash.rdoc +0 -57
  60. data/qed/02_queryhash.rdoc +0 -21
  61. data/qed/03_castinghash.rdoc +0 -13
  62. data/qed/04_statichash.rdoc +0 -22
  63. data/qed/05_association.rdoc +0 -59
  64. data/qed/06_opencascade.rdoc +0 -58
  65. data/qed/07_fuzzyhash.rdoc +0 -141
  66. data/qed/08_properyhash.rdoc +0 -38
  67. data/qed/09_ostructable.rdoc +0 -56
  68. data/qed/applique/ae.rb +0 -1
  69. data/test/case_basicstruct.rb +0 -192
  70. data/test/case_openhash.rb +0 -22
@@ -0,0 +1,365 @@
1
+ require 'hashery/core_ext'
2
+
3
+ module Hashery
4
+
5
+ # The CRUDHash is essentailly the same as the Hash class, but it reduces the
6
+ # the set of necessary methods to the fundametal CRUD requirements. All other
7
+ # methods route through these CRUD methods. This is a better general design,
8
+ # although it is, of course, a little bit slower. The utility of this class
9
+ # becomes appearent when subclassing or delegating, as only a handful of methods
10
+ # need to be changed for all other methods to work accordingly.
11
+ #
12
+ # In addition to the CRUD features, CRUDHash supports a `#key_proc`, akin to
13
+ # `#default_proc`, that can be used to normalize keys.
14
+ #
15
+ class CRUDHash < ::Hash
16
+
17
+ #
18
+ # This method is overridden to ensure that new entries pass through
19
+ # the `#store` method.
20
+ #
21
+ # hash - [#each] Single Hash, associative array or just a list of pairs.
22
+ #
23
+ def self.[](*hash)
24
+ h = new
25
+ if hash.size == 1
26
+ hash.first.each do |k,v|
27
+ h.store(k, v)
28
+ end
29
+ else
30
+ hash.each do |(k,v)|
31
+ h.store(k, v)
32
+ end
33
+ end
34
+ h
35
+ end
36
+
37
+ #
38
+ # Alternate to #new which auto-creates sub-dictionaries as needed.
39
+ # By default the `default_proc` procuced a empty Hash and is
40
+ # self-referential so every such Hash also has the same `default_proc`.
41
+ #
42
+ # args - Pass-thru arguments to `#new`.
43
+ # block - Alternate internal procedure for default proc.
44
+ #
45
+ # Examples
46
+ #
47
+ # d = CRUDHash.auto
48
+ # d["a"]["b"]["c"] = "abc" #=> { "a"=>{"b"=>{"c"=>"abc"}}}
49
+ #
50
+ # Returns `Hash`.
51
+ #
52
+ def self.auto(*args, &block)
53
+ if block
54
+ leet = lambda { |hsh, key| hsh[key] = block.call(hsh, key) }
55
+ else
56
+ leet = lambda { |hsh, key| hsh[key] = new(&leet) }
57
+ end
58
+ new(*args, &leet)
59
+ end
60
+
61
+ #
62
+ # Set `key_proc`.
63
+ #
64
+ # Examples
65
+ #
66
+ # ch = CRUDHash.new
67
+ # ch.key_proc = Proc.new{ |key| key.to_sym }
68
+ #
69
+ # Returns `Proc`.
70
+ #
71
+ def key_proc=(proc)
72
+ raise ArgumentError unless Proc === proc or NilClass === proc
73
+ @key_proc = proc
74
+ end
75
+
76
+ #
77
+ # Get/set `key_proc`.
78
+ #
79
+ # Examples
80
+ #
81
+ # ch = CRUDHash.new
82
+ # ch.key_proc
83
+ #
84
+ # Returns `Proc`.
85
+ #
86
+ def key_proc(&block)
87
+ @key_proc = block if block
88
+ @key_proc
89
+ end
90
+
91
+ #
92
+ # Allow `#default_proc` to take a block.
93
+ #
94
+ # block - The `Proc` object to set the `default_proc`.
95
+ #
96
+ # Returns `Proc`, the `default_proc`.
97
+ #
98
+ def default_proc(&block)
99
+ self.default_proc = block if block
100
+ super()
101
+ end
102
+
103
+ #
104
+ # CRUD method for checking if key exists.
105
+ #
106
+ # key - Hash key to lookup.
107
+ #
108
+ # Returns `true/false`.
109
+ #
110
+ def key?(key)
111
+ super cast_key(key)
112
+ end
113
+
114
+ #
115
+ # CRUD method for reading value.
116
+ #
117
+ # key - Hash key to lookup.
118
+ #
119
+ # Returns value of Hash entry.
120
+ #
121
+ def read(key)
122
+ super cast_key(key)
123
+ end
124
+
125
+ #
126
+ # CRUD method for create and update.
127
+ #
128
+ # key - The `Object` to act as indexing key.
129
+ # value - The `Object` to associate with key.
130
+ #
131
+ # Returns +value+.
132
+ #
133
+ def store(key, value)
134
+ super(cast_key(key), value)
135
+ end
136
+
137
+ #
138
+ # CRUD method for delete.
139
+ #
140
+ # key - Hash key to remove.
141
+ #
142
+ # Returns value of deleted Hash entry.
143
+ #
144
+ def delete(key)
145
+ super cast_key(key)
146
+ end
147
+
148
+ #
149
+ # Like #read but raises an error if key is not present.
150
+ #
151
+ # key - Hash key to lookup.
152
+ #
153
+ # Returns the `Object` that is the Hash entry's value.
154
+ #
155
+ def fetch(key)
156
+ raise KeyError, "key not found: #{key.inspect}" unless key?(key)
157
+ read key
158
+ end
159
+
160
+ #
161
+ # Update Hash with +assoc+.
162
+ #
163
+ # assoc - Two-element `Array` or a `Hash`.
164
+ #
165
+ # Returns +assoc+.
166
+ #
167
+ def <<(assoc)
168
+ case assoc
169
+ when Hash
170
+ update(assoc)
171
+ when Array
172
+ assoc.each_slice(2) do |(k,v)|
173
+ store(k,v)
174
+ end
175
+ else
176
+ raise ArgumentError # or TypeError ?
177
+ end
178
+ end
179
+
180
+ #
181
+ # Operator for `#read`.
182
+ #
183
+ # key - Index key to lookup.
184
+ #
185
+ # Returns `Object` value of key.
186
+ #
187
+ def [](key)
188
+ #if key?(key)
189
+ # fetch(key)
190
+ #elsif default_proc
191
+ # default_proc.call(self, key)
192
+ #else
193
+ # default
194
+ #end
195
+ read(key)
196
+ end
197
+
198
+ #
199
+ # Operator for `#store`.
200
+ #
201
+ # key - The `Object` to act as indexing key.
202
+ # value - The `Object` to associate with key.
203
+ #
204
+ # Returns +value+.
205
+ #
206
+ def []=(key,value)
207
+ store(key,value)
208
+ end
209
+
210
+ #
211
+ # Update the Hash with another hash.
212
+ #
213
+ # other - Other hash or hash-like object to add to the hash.
214
+ #
215
+ # Returns +self+.
216
+ #
217
+ def update(other)
218
+ other.each do |k,v|
219
+ store(k, v)
220
+ end
221
+ self
222
+ end
223
+
224
+ #
225
+ # Alias for `#update`.
226
+ #
227
+ alias merge! update
228
+
229
+ #
230
+ # Merge the Hash with another hash, returning a new Hash.
231
+ #
232
+ # other - Other hash or hash-like object to add to the hash.
233
+ #
234
+ # Returns `Hash`.
235
+ #
236
+ def merge(other)
237
+ #super(other.rekey{ |key| cast_key(key) })
238
+ copy = dup
239
+ other.each{ |k,v| copy.store(k, v) }
240
+ copy
241
+ end
242
+
243
+ #
244
+ # Iterate over each hash pair.
245
+ #
246
+ def each #:yield:
247
+ if block_given?
248
+ keys.each do |k|
249
+ yield(k, read(k))
250
+ end
251
+ else
252
+ to_enum(:each)
253
+ end
254
+ end
255
+
256
+ #
257
+ # Alias for #each.
258
+ #
259
+ alias each_pair each
260
+
261
+ #
262
+ # Alias for `#key?`.
263
+ #
264
+ alias has_key? key?
265
+
266
+ #
267
+ # Alias for `#key?`.
268
+ #
269
+ alias member? key?
270
+
271
+ #
272
+ # Alias for `#key?`.
273
+ #
274
+ alias include? key? # why isn't it an alias for `#has_value?` ?
275
+
276
+ #
277
+ # Replace current entries with those from another Hash,
278
+ # or Hash-like object. Each entry is run through the
279
+ # casting procedure as it is added.
280
+ #
281
+ # other - Hash-like object.
282
+ #
283
+ # Returns +self+.
284
+ #
285
+ def replace(other)
286
+ super cast(other)
287
+ end
288
+
289
+ #
290
+ # Get the values at.
291
+ #
292
+ # keys - List of keys to lookup.
293
+ #
294
+ # Returns `Array` of values.
295
+ #
296
+ def values_at(*keys)
297
+ super *keys.map{ |key| cast_key(key) }
298
+ end
299
+
300
+ # Convert CRUDHash to regular Hash.
301
+ #
302
+ # TODO: Since a CRUDHash is a subclass of Hash should #to_hash just `#dup`
303
+ # insted of converting to traditional Hash?
304
+ #
305
+ def to_hash
306
+ h = {}; each{ |k,v| h[k] = v }; h
307
+ end #unless method_defined?(:to_hash)
308
+
309
+ #
310
+ # Convert CRUDHash to regular Hash.
311
+ #
312
+ # TODO: Since a CRUDHash is a subclass of Hash should #to_h just `#dup`
313
+ # insted of converting to traditional Hash?
314
+ #
315
+ # Returns `Hash`.
316
+ #
317
+ alias :to_h :to_hash
318
+
319
+ private
320
+
321
+ #
322
+ # Cast a given `hash` in accordance to the `#key_proc`.
323
+ #
324
+ # hash - Any object the responds to `#each` like a Hash.
325
+ #
326
+ # Returns `Hash`.
327
+ #
328
+ def cast(hash)
329
+ h = {}
330
+ hash.each do |k,v|
331
+ h[cast_key(k)] = v
332
+ end
333
+ h
334
+ end
335
+
336
+ #
337
+ # Callback for normalizing hash keys.
338
+ #
339
+ # key - Index key.
340
+ #
341
+ # Returns key after passing through the `key_proc`.
342
+ #
343
+ def cast_key(key)
344
+ @key_proc ? @key_proc.call(key) : key
345
+ end
346
+
347
+ # TODO: Consider value callback procs for future version of CRUDHash.
348
+ #
349
+ # #
350
+ # # Callback for writing value.
351
+ # #
352
+ # def cast_write(value)
353
+ # @write_proc ? @write_proc.call(value) : value
354
+ # end
355
+ #
356
+ # #
357
+ # # Callback for reading value.
358
+ # #
359
+ # def cast_read(value)
360
+ # @read_proc ? @read_proc.call(value) : value
361
+ # end
362
+
363
+ end
364
+
365
+ end
@@ -1,90 +1,160 @@
1
- # = Dictionary
2
- #
3
- # The Dictionary class is a Hash that preserves order.
4
- # So it has some array-like extensions also. By defualt
5
- # a Dictionary object preserves insertion order, but any
6
- # order can be specified including alphabetical key order.
7
- #
8
- # == Usage
9
- #
10
- # Just require this file and use Dictionary instead of Hash.
11
- #
12
- # # You can do simply
13
- # hsh = Dictionary.new
14
- # hsh['z'] = 1
15
- # hsh['a'] = 2
16
- # hsh['c'] = 3
17
- # p hsh.keys #=> ['z','a','c']
18
- #
19
- # # or using Dictionary[] method
20
- # hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
21
- # p hsh.keys #=> ['z','a','c']
22
- #
23
- # # but this don't preserve order
24
- # hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
25
- # p hsh.keys #=> ['a','c','z']
26
- #
27
- # # Dictionary has useful extensions: push, pop and unshift
28
- # p hsh.push('to_end', 15) #=> true, key added
29
- # p hsh.push('to_end', 30) #=> false, already - nothing happen
30
- # p hsh.unshift('to_begin', 50) #=> true, key added
31
- # p hsh.unshift('to_begin', 60) #=> false, already - nothing happen
32
- # p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"]
33
- # p hsh.pop #=> ["to_end", 15], if nothing remains, return nil
34
- # p hsh.keys #=> ["to_begin", "a", "c", "z"]
35
- # p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil
36
- #
37
- # == Usage Notes
38
- #
39
- # * You can use #order_by to set internal sort order.
40
- # * #<< takes a two element [k,v] array and inserts.
41
- # * Use ::auto which creates Dictionay sub-entries as needed.
42
- # * And ::alpha which creates a new Dictionary sorted by key.
43
- #
44
- # == Acknowledgments
45
- #
46
- # Dictionary is a ported of OrderHash 2.0 Copyright (c) 2005 Jan Molic.
47
- #
48
- # People who have contributed to this class since then include:
49
- #
50
- # * Andrew Johnson (merge, to_a, inspect, shift and Hash[])
51
- # * Jeff Sharpe (reverse and reverse!)
52
- # * Thomas Leitner (has_key? and key?)
53
- #
54
- # Copyright (c) 2005, 2009 Jan Molic, Thomas Sawyer
55
-
56
- class Dictionary
57
-
58
- include Enumerable
59
-
60
- class << self
61
- #--
62
- # TODO is this needed? Doesn't the super class do this?
63
- #++
64
-
65
- def [](*args)
66
- hsh = new
67
- if Hash === args[0]
68
- hsh.replace(args[0])
69
- elsif (args.size % 2) != 0
70
- raise ArgumentError, "odd number of elements for Hash"
71
- else
72
- while !args.empty?
73
- hsh[args.shift] = args.shift
1
+ module Hashery
2
+
3
+ # The Dictionary class is a Hash that preserves order.
4
+ # So it has some array-like extensions also. By defualt
5
+ # a Dictionary object preserves insertion order, but any
6
+ # order can be specified including alphabetical key order.
7
+ #
8
+ # Using a Dictionary is almost the same as using a Hash.
9
+ #
10
+ # # You can do simply
11
+ # hsh = Dictionary.new
12
+ # hsh['z'] = 1
13
+ # hsh['a'] = 2
14
+ # hsh['c'] = 3
15
+ # p hsh.keys #=> ['z','a','c']
16
+ #
17
+ # # or using Dictionary[] method
18
+ # hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
19
+ # p hsh.keys #=> ['z','a','c']
20
+ #
21
+ # # but this don't preserve order
22
+ # hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
23
+ # p hsh.keys #=> ['a','c','z']
24
+ #
25
+ # # Dictionary has useful extensions: push, pop and unshift
26
+ # p hsh.push('to_end', 15) #=> true, key added
27
+ # p hsh.push('to_end', 30) #=> false, already - nothing happen
28
+ # p hsh.unshift('to_begin', 50) #=> true, key added
29
+ # p hsh.unshift('to_begin', 60) #=> false, already - nothing happen
30
+ # p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"]
31
+ # p hsh.pop #=> ["to_end", 15], if nothing remains, return nil
32
+ # p hsh.keys #=> ["to_begin", "a", "c", "z"]
33
+ # p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil
34
+ #
35
+ # == Notes
36
+ #
37
+ # * You can use #order_by to set internal sort order.
38
+ # * #<< takes a two element [k,v] array and inserts.
39
+ # * Use ::auto which creates Dictionay sub-entries as needed.
40
+ # * And ::alpha which creates a new Dictionary sorted by key.
41
+ #
42
+ # == Acknowledgments
43
+ #
44
+ # Dictionary is a port of OrderHash 2.0 Copyright (c) 2005 Jan Molic.
45
+ #
46
+ # People who have contributed to this class since then include:
47
+ #
48
+ # * Andrew Johnson (merge, to_a, inspect, shift and Hash[])
49
+ # * Jeff Sharpe (reverse and reverse!)
50
+ # * Thomas Leitner (has_key? and key?)
51
+ #
52
+ # OrderedHash is public domain.
53
+ #
54
+ class Dictionary
55
+
56
+ include Enumerable
57
+
58
+ class << self
59
+ #--
60
+ # TODO is this needed? Doesn't the super class do this?
61
+ #++
62
+
63
+ def [](*args)
64
+ hsh = new
65
+ if Hash === args[0]
66
+ hsh.replace(args[0])
67
+ elsif (args.size % 2) != 0
68
+ raise ArgumentError, "odd number of elements for Hash"
69
+ else
70
+ while !args.empty?
71
+ hsh[args.shift] = args.shift
72
+ end
74
73
  end
74
+ hsh
75
+ end
76
+
77
+ #
78
+ # Like #new but the block sets the order.
79
+ #
80
+ def new_by(*args, &blk)
81
+ new(*args).order_by(&blk)
82
+ end
83
+
84
+ #
85
+ # Alternate to #new which creates a dictionary sorted by key.
86
+ #
87
+ # d = Dictionary.alpha
88
+ # d["z"] = 1
89
+ # d["y"] = 2
90
+ # d["x"] = 3
91
+ # d #=> {"x"=>3,"y"=>2,"z"=>2}
92
+ #
93
+ # This is equivalent to:
94
+ #
95
+ # Dictionary.new.order_by { |key,value| key }
96
+
97
+ def alpha(*args, &block)
98
+ new(*args, &block).order_by_key
99
+ end
100
+
101
+ #
102
+ # Alternate to #new which auto-creates sub-dictionaries as needed.
103
+ #
104
+ # Examples
105
+ #
106
+ # d = Dictionary.auto
107
+ # d["a"]["b"]["c"] = "abc" #=> { "a"=>{"b"=>{"c"=>"abc"}}}
108
+ #
109
+ def auto(*args)
110
+ #AutoDictionary.new(*args)
111
+ leet = lambda { |hsh, key| hsh[key] = new(&leet) }
112
+ new(*args, &leet)
75
113
  end
76
- hsh
77
114
  end
78
115
 
79
- # Like #new but the block sets the order.
80
116
  #
81
- def new_by(*args, &blk)
82
- new(*args).order_by(&blk)
117
+ # New Dictiionary.
118
+ #
119
+ def initialize(*args, &blk)
120
+ @order = []
121
+ @order_by = nil
122
+ if blk
123
+ dict = self # This ensure autmatic key entry effect the
124
+ oblk = lambda{ |hsh, key| blk[dict,key] } # dictionary rather then just the interal hash.
125
+ @hash = Hash.new(*args, &oblk)
126
+ else
127
+ @hash = Hash.new(*args)
128
+ end
129
+ end
130
+
131
+ #
132
+ # Order of keys.
133
+ #
134
+ # Returns [Array].
135
+ #
136
+ def order
137
+ reorder if @order_by
138
+ @order
139
+ end
140
+
141
+ #
142
+ # Keep dictionary sorted by a specific sort order.
143
+ #
144
+ # block - Ordering procedure.
145
+ #
146
+ # Returns +self+.
147
+ #
148
+ def order_by( &block )
149
+ @order_by = block
150
+ order
151
+ self
83
152
  end
84
153
 
85
- # Alternate to #new which creates a dictionary sorted by key.
86
154
  #
87
- # d = Dictionary.alpha
155
+ # Keep dictionary sorted by key.
156
+ #
157
+ # d = Dictionary.new.order_by_key
88
158
  # d["z"] = 1
89
159
  # d["y"] = 2
90
160
  # d["x"] = 3
@@ -93,332 +163,462 @@ class Dictionary
93
163
  # This is equivalent to:
94
164
  #
95
165
  # Dictionary.new.order_by { |key,value| key }
96
-
97
- def alpha(*args, &block)
98
- new(*args, &block).order_by_key
166
+ #
167
+ # The initializer Dictionary#alpha also provides this.
168
+ #
169
+ # Returns +self+.
170
+ #
171
+ def order_by_key
172
+ @order_by = Proc.new{ |k,v| k }
173
+ order
174
+ self
99
175
  end
100
176
 
101
- # Alternate to #new which auto-creates sub-dictionaries as needed.
102
177
  #
103
- # d = Dictionary.auto
104
- # d["a"]["b"]["c"] = "abc" #=> { "a"=>{"b"=>{"c"=>"abc"}}}
178
+ # Keep dictionary sorted by value.
105
179
  #
106
- def auto(*args)
107
- #AutoDictionary.new(*args)
108
- leet = lambda { |hsh, key| hsh[key] = new(&leet) }
109
- new(*args, &leet)
180
+ # d = Dictionary.new.order_by_value
181
+ # d["z"] = 1
182
+ # d["y"] = 2
183
+ # d["x"] = 3
184
+ # d #=> {"x"=>3,"y"=>2,"z"=>2}
185
+ #
186
+ # This is equivalent to:
187
+ #
188
+ # Dictionary.new.order_by { |key,value| value }
189
+ #
190
+ def order_by_value
191
+ @order_by = Proc.new{ |k,v| v }
192
+ order
193
+ self
110
194
  end
111
- end
112
195
 
113
- # New Dictiionary.
114
-
115
- def initialize(*args, &blk)
116
- @order = []
117
- @order_by = nil
118
- if blk
119
- dict = self # This ensure autmatic key entry effect the
120
- oblk = lambda{ |hsh, key| blk[dict,key] } # dictionary rather then just the interal hash.
121
- @hash = Hash.new(*args, &oblk)
122
- else
123
- @hash = Hash.new(*args)
196
+ #
197
+ # Re-apply the sorting procedure.
198
+ #
199
+ def reorder
200
+ if @order_by
201
+ assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by(&@order_by)
202
+ @order = assoc.collect{ |k,v| k }
203
+ end
204
+ @order
124
205
  end
125
- end
126
-
127
- #
128
206
 
129
- def order
130
- reorder if @order_by
131
- @order
132
- end
133
-
134
- # Keep dictionary sorted by a specific sort order.
135
-
136
- def order_by( &block )
137
- @order_by = block
138
- order
139
- self
140
- end
207
+ #def ==( hsh2 )
208
+ # return false if @order != hsh2.order
209
+ # super hsh2
210
+ #end
141
211
 
142
- # Keep dictionary sorted by key.
143
- #
144
- # d = Dictionary.new.order_by_key
145
- # d["z"] = 1
146
- # d["y"] = 2
147
- # d["x"] = 3
148
- # d #=> {"x"=>3,"y"=>2,"z"=>2}
149
- #
150
- # This is equivalent to:
151
- #
152
- # Dictionary.new.order_by { |key,value| key }
153
- #
154
- # The initializer Dictionary#alpha also provides this.
155
-
156
- def order_by_key
157
- @order_by = lambda { |k,v| k }
158
- order
159
- self
160
- end
212
+ #
213
+ # Is the dictionary instance equivalent to another?
214
+ #
215
+ def ==(hsh2)
216
+ if hsh2.is_a?( Dictionary )
217
+ @order == hsh2.order &&
218
+ @hash == hsh2.instance_variable_get("@hash")
219
+ else
220
+ false
221
+ end
222
+ end
161
223
 
162
- # Keep dictionary sorted by value.
163
- #
164
- # d = Dictionary.new.order_by_value
165
- # d["z"] = 1
166
- # d["y"] = 2
167
- # d["x"] = 3
168
- # d #=> {"x"=>3,"y"=>2,"z"=>2}
169
- #
170
- # This is equivalent to:
171
- #
172
- # Dictionary.new.order_by { |key,value| value }
224
+ #
225
+ # Lookup entry with key.
226
+ #
227
+ def [] key
228
+ @hash[ key ]
229
+ end
173
230
 
174
- def order_by_value
175
- @order_by = lambda { |k,v| v }
176
- order
177
- self
178
- end
231
+ #
232
+ # Featch entry given +key+.
233
+ #
234
+ def fetch(key, *a, &b)
235
+ @hash.fetch(key, *a, &b)
236
+ end
179
237
 
180
- #
238
+ #
239
+ # Store operator.
240
+ #
241
+ # h[key] = value
242
+ #
243
+ # Or with additional index.
244
+ #
245
+ # h[key,index] = value
246
+ #
247
+ def []=(k, i=nil, v=nil)
248
+ if v
249
+ insert(i,k,v)
250
+ else
251
+ store(k,i)
252
+ end
253
+ end
181
254
 
182
- def reorder
183
- if @order_by
184
- assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by(&@order_by)
185
- @order = assoc.collect{ |k,v| k }
255
+ #
256
+ # Insert entry into dictionary at specific index position.
257
+ #
258
+ # index - [Integer] Position of order placement.
259
+ # key - [Object] Key to associate with value.
260
+ # value - [Object] Value to associate with key.
261
+ #
262
+ # Returns `value` stored.
263
+ #
264
+ def insert(index, key, value)
265
+ @order.insert(index, key)
266
+ @hash.store(key, value)
186
267
  end
187
- @order
188
- end
189
268
 
190
- #def ==( hsh2 )
191
- # return false if @order != hsh2.order
192
- # super hsh2
193
- #end
269
+ #
270
+ # Add entry into dictionary.
271
+ #
272
+ # Returns `value`.
273
+ #
274
+ def store(key, value)
275
+ @order.push(key) unless @hash.has_key?(key)
276
+ @hash.store(key, value)
277
+ end
194
278
 
195
- def ==(hsh2)
196
- if hsh2.is_a?( Dictionary )
197
- @order == hsh2.order &&
198
- @hash == hsh2.instance_variable_get("@hash")
199
- else
200
- false
279
+ #
280
+ # Clear dictionary of all entries.
281
+ #
282
+ def clear
283
+ @order = []
284
+ @hash.clear
201
285
  end
202
- end
203
286
 
204
- def [] k
205
- @hash[ k ]
206
- end
287
+ #
288
+ # Delete the entry with given +key+.
289
+ #
290
+ def delete(key)
291
+ @order.delete(key)
292
+ @hash.delete(key)
293
+ end
207
294
 
208
- def fetch(k, *a, &b)
209
- @hash.fetch(k, *a, &b)
210
- end
295
+ #
296
+ # Iterate over each key.
297
+ #
298
+ def each_key
299
+ order.each { |k| yield( k ) }
300
+ self
301
+ end
211
302
 
212
- # Store operator.
213
- #
214
- # h[key] = value
215
- #
216
- # Or with additional index.
217
- #
218
- # h[key,index] = value
303
+ #
304
+ # Iterate over each value.
305
+ #
306
+ def each_value
307
+ order.each { |k| yield( @hash[k] ) }
308
+ self
309
+ end
219
310
 
220
- def []=(k, i=nil, v=nil)
221
- if v
222
- insert(i,k,v)
223
- else
224
- store(k,i)
311
+ #
312
+ # Iterate over each key-value pair.
313
+ #
314
+ def each
315
+ order.each { |k| yield( k,@hash[k] ) }
316
+ self
225
317
  end
226
- end
227
318
 
228
- def insert( i,k,v )
229
- @order.insert( i,k )
230
- @hash.store( k,v )
231
- end
319
+ alias each_pair each
232
320
 
233
- def store( a,b )
234
- @order.push( a ) unless @hash.has_key?( a )
235
- @hash.store( a,b )
236
- end
321
+ #
322
+ # Delete entry if it fits conditional block.
323
+ #
324
+ def delete_if
325
+ order.clone.each { |k| delete k if yield(k,@hash[k]) }
326
+ self
327
+ end
237
328
 
238
- def clear
239
- @order = []
240
- @hash.clear
241
- end
329
+ #
330
+ # List of all dictionary values.
331
+ #
332
+ # Returns [Array].
333
+ #
334
+ def values
335
+ ary = []
336
+ order.each { |k| ary.push @hash[k] }
337
+ ary
338
+ end
242
339
 
243
- def delete( key )
244
- @order.delete( key )
245
- @hash.delete( key )
246
- end
340
+ #
341
+ # List of all dictionary keys.
342
+ #
343
+ # Returns [Array].
344
+ #
345
+ def keys
346
+ order
347
+ end
247
348
 
248
- def each_key
249
- order.each { |k| yield( k ) }
250
- self
251
- end
349
+ #
350
+ # Invert the dictionary.
351
+ #
352
+ # Returns [Dictionary] New dictionary that is inverse of the original.
353
+ #
354
+ def invert
355
+ hsh2 = self.class.new
356
+ order.each { |k| hsh2[@hash[k]] = k }
357
+ hsh2
358
+ end
252
359
 
253
- def each_value
254
- order.each { |k| yield( @hash[k] ) }
255
- self
256
- end
360
+ #
361
+ # Reject entries based on give condition block and return
362
+ # new dictionary.
363
+ #
364
+ # Returns [Dictionary].
365
+ #
366
+ def reject(&block)
367
+ self.dup.delete_if(&block)
368
+ end
257
369
 
258
- def each
259
- order.each { |k| yield( k,@hash[k] ) }
260
- self
261
- end
262
- alias each_pair each
370
+ #
371
+ # Reject entries based on give condition block.
372
+ #
373
+ # Returns [Hash] of rejected entries.
374
+ #
375
+ # FIXME: This looks like it is implemented wrong!!!
376
+ #
377
+ def reject!( &block )
378
+ hsh2 = reject(&block)
379
+ self == hsh2 ? nil : hsh2
380
+ end
263
381
 
264
- def delete_if
265
- order.clone.each { |k| delete k if yield(k,@hash[k]) }
266
- self
267
- end
382
+ #
383
+ # Replace dictionary entries with new table.
384
+ #
385
+ def replace(hsh2)
386
+ case hsh2
387
+ when Dictionary
388
+ @order = hsh2.order
389
+ @hash = hsh2.to_h
390
+ when Hash
391
+ @hash = hsh2
392
+ @order = @hash.keys
393
+ else
394
+ @hash = hsh2.to_h
395
+ @order = @hash.keys
396
+ end
397
+ reorder
398
+ end
268
399
 
269
- def values
270
- ary = []
271
- order.each { |k| ary.push @hash[k] }
272
- ary
273
- end
400
+ #
401
+ # Remove entry from the to top of dictionary.
402
+ #
403
+ def shift
404
+ key = order.first
405
+ key ? [key,delete(key)] : super
406
+ end
274
407
 
275
- def keys
276
- order
277
- end
408
+ #
409
+ # Push entry on to the top of dictionary.
410
+ #
411
+ def unshift( k,v )
412
+ unless @hash.include?( k )
413
+ @order.unshift( k )
414
+ @hash.store( k,v )
415
+ true
416
+ else
417
+ false
418
+ end
419
+ end
278
420
 
279
- def invert
280
- hsh2 = self.class.new
281
- order.each { |k| hsh2[@hash[k]] = k }
282
- hsh2
283
- end
421
+ #
422
+ # Same as #push.
423
+ #
424
+ def <<(kv)
425
+ push(*kv)
426
+ end
284
427
 
285
- def reject(&block)
286
- self.dup.delete_if(&block)
287
- end
428
+ #
429
+ # Push entry on to bottom of the dictionary.
430
+ #
431
+ def push(k,v)
432
+ unless @hash.include?( k )
433
+ @order.push( k )
434
+ @hash.store( k,v )
435
+ true
436
+ else
437
+ false
438
+ end
439
+ end
288
440
 
289
- def reject!( &block )
290
- hsh2 = reject(&block)
291
- self == hsh2 ? nil : hsh2
292
- end
441
+ #
442
+ # Pop entry off the bottom of dictionary.
443
+ #
444
+ def pop
445
+ key = order.last
446
+ key ? [key,delete(key)] : nil
447
+ end
293
448
 
294
- def replace(hsh2)
295
- case hsh2
296
- when Hash
297
- @order = hsh2.keys
298
- @hash = hsh2
299
- else
300
- @order = hsh2.order
301
- @hash = hsh2.hash
449
+ #
450
+ # Inspection string for Dictionary.
451
+ #
452
+ # Returns [String].
453
+ #
454
+ def inspect
455
+ ary = []
456
+ each {|k,v| ary << k.inspect + "=>" + v.inspect}
457
+ '{' + ary.join(", ") + '}'
302
458
  end
303
- reorder
304
- end
305
459
 
306
- def shift
307
- key = order.first
308
- key ? [key,delete(key)] : super
309
- end
460
+ #
461
+ # Duplicate dictionary.
462
+ #
463
+ # Returns [Dictionary].
464
+ #
465
+ def dup
466
+ a = []
467
+ each{ |k,v| a << k; a << v }
468
+ self.class[*a]
469
+ end
310
470
 
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
471
+ #
472
+ # Update dictionary with other hash.
473
+ #
474
+ # Returns self.
475
+ #
476
+ def update( hsh2 )
477
+ hsh2.each { |k,v| self[k] = v }
478
+ reorder
479
+ self
318
480
  end
319
- end
320
481
 
321
- def <<(kv)
322
- push(*kv)
323
- end
482
+ alias :merge! update
324
483
 
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
484
+ #
485
+ # Merge other hash creating new dictionary.
486
+ #
487
+ # Returns [Dictionary].
488
+ #
489
+ def merge(hsh2)
490
+ self.dup.update(hsh2)
332
491
  end
333
- end
334
492
 
335
- def pop
336
- key = order.last
337
- key ? [key,delete(key)] : nil
338
- end
493
+ #
494
+ # Select items from dictiornary.
495
+ #
496
+ # Returns [Array] of two-element arrays.
497
+ #
498
+ def select
499
+ ary = []
500
+ each { |k,v| ary << [k,v] if yield k,v }
501
+ ary
502
+ end
339
503
 
340
- def inspect
341
- ary = []
342
- each {|k,v| ary << k.inspect + "=>" + v.inspect}
343
- '{' + ary.join(", ") + '}'
344
- end
504
+ #
505
+ # Reverse the order of the dictionary.
506
+ #
507
+ # Returns self.
508
+ #
509
+ def reverse!
510
+ @order.reverse!
511
+ self
512
+ end
345
513
 
346
- def dup
347
- a = []
348
- each{ |k,v| a << k; a << v }
349
- self.class[*a]
350
- end
514
+ #
515
+ # Reverse the order of duplicte dictionary.
516
+ #
517
+ # Returns [Dictionary].
518
+ #
519
+ def reverse
520
+ dup.reverse!
521
+ end
351
522
 
352
- def update( hsh2 )
353
- hsh2.each { |k,v| self[k] = v }
354
- reorder
355
- self
356
- end
357
- alias :merge! update
523
+ #
524
+ # Get/set initial entry value.
525
+ #
526
+ def first(x=nil)
527
+ return @hash[order.first] unless x
528
+ order.first(x).collect { |k| @hash[k] }
529
+ end
358
530
 
359
- def merge( hsh2 )
360
- self.dup.update(hsh2)
361
- end
531
+ #
532
+ # Get/set last entry value.
533
+ #
534
+ def last(x=nil)
535
+ return @hash[order.last] unless x
536
+ order.last(x).collect { |k| @hash[k] }
537
+ end
362
538
 
363
- def select
364
- ary = []
365
- each { |k,v| ary << [k,v] if yield k,v }
366
- ary
367
- end
539
+ #
540
+ # Number of items in the dictionary.
541
+ #
542
+ def length
543
+ @order.length
544
+ end
368
545
 
369
- def reverse!
370
- @order.reverse!
371
- self
372
- end
546
+ alias :size :length
373
547
 
374
- def reverse
375
- dup.reverse!
376
- end
548
+ #
549
+ # Is the dictionary empty?
550
+ #
551
+ # Returns `true` or `false`.
552
+ #
553
+ def empty?
554
+ @hash.empty?
555
+ end
377
556
 
378
- #
379
- def first(x=nil)
380
- return @hash[order.first] unless x
381
- order.first(x).collect { |k| @hash[k] }
382
- end
557
+ #
558
+ # Does the dictionary have a given +key+.
559
+ #
560
+ # Returns `true` or `false`.
561
+ #
562
+ def has_key?(key)
563
+ @hash.has_key?(key)
564
+ end
383
565
 
384
- #
385
- def last(x=nil)
386
- return @hash[order.last] unless x
387
- order.last(x).collect { |k| @hash[k] }
388
- end
566
+ #
567
+ # Does the dictionary have a given +key+.
568
+ #
569
+ # Returns `true` or `false`.
570
+ #
571
+ def key?(key)
572
+ @hash.key?(key)
573
+ end
389
574
 
390
- def length
391
- @order.length
392
- end
393
- alias :size :length
575
+ #
576
+ # Convert to array.
577
+ #
578
+ # Returns [Array] of two-element arrays.
579
+ #
580
+ def to_a
581
+ ary = []
582
+ each { |k,v| ary << [k,v] }
583
+ ary
584
+ end
394
585
 
395
- def empty?
396
- @hash.empty?
397
- end
586
+ #
587
+ # Convert to array then to string.
588
+ #
589
+ # Returns [String].
590
+ #
591
+ def to_s
592
+ self.to_a.to_s
593
+ end
398
594
 
399
- def has_key?(key)
400
- @hash.has_key?(key)
401
- end
595
+ #
596
+ # Get a duplicate of the underlying hash table.
597
+ #
598
+ # Returns [Hash].
599
+ #
600
+ def to_hash
601
+ @hash.dup
602
+ end
402
603
 
403
- def key?(key)
404
- @hash.key?(key)
405
- end
604
+ #
605
+ # Get a duplicate of the underlying hash table.
606
+ #
607
+ # Returns [Hash].
608
+ #
609
+ def to_h
610
+ @hash.dup
611
+ end
406
612
 
407
- def to_a
408
- ary = []
409
- each { |k,v| ary << [k,v] }
410
- ary
411
- end
613
+ protected
412
614
 
413
- def to_s
414
- self.to_a.to_s
415
- end
615
+ #
616
+ # Underlying hash table.
617
+ #
618
+ def hash_table
619
+ @hash
620
+ end
416
621
 
417
- def to_hash
418
- @hash.dup
419
622
  end
420
623
 
421
- def to_h
422
- @hash.dup
423
- end
424
624
  end