hashery 1.5.0 → 2.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.
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