hashery 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/HISTORY ADDED
@@ -0,0 +1,15 @@
1
+ == 1.0.0 // 2010-04-21
2
+
3
+ This is the first release of the Facets Hashery.
4
+ Most of included classes come directly from Ruby
5
+ Facets, so they have been around a while and are
6
+ in good working condition.
7
+
8
+ Some improvements are planned for the next release.
9
+ In particular the OrderHash and Dictionary, which
10
+ presently have essentially the same coding, will
11
+ diverge to target slightly different use cases.
12
+
13
+ Changes:
14
+
15
+ * Happy Birthday!
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2005 Thomas Sawyer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
23
+
@@ -0,0 +1,47 @@
1
+ = Facets Hashery
2
+
3
+ * home: http://rubyworks.github.com/hashery
4
+ * work: http://github.com/rubyworks/hashery
5
+
6
+ == DESCRIPTION
7
+
8
+ Among Ruby Facets most common additions were an assortment
9
+ of Hash-like classes. To better support this collection of
10
+ of libraries it was deemed prudent to create a side project
11
+ specifically for them. Hence the Facets Hashery.
12
+
13
+ Included in this collect are the widely used OrderedHash,
14
+ the related but more featured Dictionary class, a number
15
+ of _open_ classes, similiar to the standard OpenStruct,
16
+ and variations of the standard Hash.
17
+
18
+
19
+ == RELEASE NOTES
20
+
21
+ Please see the HISTORY file.
22
+
23
+
24
+ == HOW TO USE
25
+
26
+ For usage information, see the individual library files include
27
+ in this collection.
28
+
29
+
30
+ == HOW TO INSTALL
31
+
32
+ To install with RubyGems simply open a console and type:
33
+
34
+ $ sudo gem install hashery
35
+
36
+ Tarball packages are available for manual site installations
37
+ via <a href="http://proutils.github.com/setup">Setup.rb</a>.
38
+
39
+
40
+ == LICENSE
41
+
42
+ Copyright (c) 2005,2010 Thomas Sawyer
43
+
44
+ This program is ditributed unser the terms of the MIT license.
45
+
46
+ See LICENSE file for details.
47
+
@@ -0,0 +1,13 @@
1
+ = ROADMAP
2
+
3
+ == 1.1.0
4
+
5
+ * Improve test coverage.
6
+ * Consider using Hashery:: namespace.
7
+ * Should Stash have a more explict name?
8
+
9
+ == 1.2.0
10
+
11
+ * Add a defined property Hash. Needed?
12
+ * Any other Hash-like classes to add?
13
+
@@ -0,0 +1,13 @@
1
+ require 'hashery/castinghash'
2
+ require 'hashery/dictionary'
3
+ require 'hashery/lruhash'
4
+ require 'hashery/memoizer'
5
+ require 'hashery/opencascade'
6
+ require 'hashery/openhash'
7
+ require 'hashery/openobject'
8
+ require 'hashery/orderedhash'
9
+ require 'hashery/ostructable'
10
+ require 'hashery/queryhash'
11
+ require 'hashery/stash'
12
+ require 'hashery/statichash'
13
+
@@ -0,0 +1,171 @@
1
+ # CastingHash is just like Hash, except that all keys and values
2
+ # are passed through casting procedures.
3
+ #--
4
+ # TODO: Handle default_proc.
5
+ #++
6
+ class CastingHash < Hash
7
+
8
+ # Default key conversion procedure.
9
+ KEY_PROC = lambda{ |x| x } #.to_s }
10
+
11
+ # Default value conversion procedure.
12
+ VAL_PROC = lambda{ |x| x }
13
+
14
+ #
15
+ def self.[](hash)
16
+ s = new
17
+ hash.each{ |k,v| s[k] = v }
18
+ s
19
+ end
20
+
21
+ #
22
+ def initialize(hash={}, value_cast=nil, &key_cast)
23
+ @key_proc = key_cast || KEY_PROC
24
+ @value_proc = value_cast.to_proc || VAL_PROC
25
+ hash.each{ |k,v| self[k] = v }
26
+ end
27
+
28
+ #
29
+ def key_proc
30
+ @key_proc
31
+ end
32
+
33
+ #
34
+ def key_proc=(proc)
35
+ @key_proc = proc.to_proc
36
+ end
37
+
38
+ #
39
+ def value_proc
40
+ @value_proc
41
+ end
42
+
43
+ #
44
+ def value_proc=(proc)
45
+ @value_proc = proc.to_proc
46
+ end
47
+
48
+ #
49
+ def [](k)
50
+ super(key_proc[k])
51
+ end
52
+
53
+ #
54
+ def []=(k,v)
55
+ super(key_proc[k], value_proc[v])
56
+ end
57
+
58
+ #
59
+ def <<(other)
60
+ case other
61
+ when Hash
62
+ super(cast(other))
63
+ when Array
64
+ self[other[0]] = other[1]
65
+ else
66
+ raise ArgumentError
67
+ end
68
+ end
69
+
70
+ def fetch(k)
71
+ super(key_proc[k])
72
+ end
73
+
74
+ #
75
+ def store(k, v)
76
+ super(key_proc[k], value_proc[v])
77
+ end
78
+
79
+ #
80
+ def key?(k)
81
+ super(key_proc[k])
82
+ end
83
+
84
+ #
85
+ def has_key?(k)
86
+ super(key_proc[k])
87
+ end
88
+
89
+ # Synonym for Hash#rekey, but modifies the receiver in place (and returns it).
90
+ #
91
+ # foo = { :name=>'Gavin', :wife=>:Lisa }.to_stash
92
+ # foo.rekey!{ |k| k.upcase } #=> { "NAME"=>"Gavin", "WIFE"=>:Lisa }
93
+ # foo.inspect #=> { "NAME"=>"Gavin", "WIFE"=>:Lisa }
94
+ #
95
+ def rekey!(*args, &block)
96
+ # for backward comptability (DEPRECATE?).
97
+ block = args.pop.to_sym.to_proc if args.size == 1
98
+ if args.empty?
99
+ block = lambda{|k| k} unless block
100
+ keys.each do |k|
101
+ nk = block[k]
102
+ self[nk] = delete(k) #if nk
103
+ end
104
+ else
105
+ raise ArgumentError, "3 for 2" if block
106
+ to, from = *args
107
+ self[to] = delete(from) if has_key?(from)
108
+ end
109
+ self
110
+ end
111
+
112
+ #
113
+ def rekey(*args, &block)
114
+ dup.rekey!(*args, &block)
115
+ end
116
+
117
+ #
118
+ def delete(k)
119
+ super(key_proc[k])
120
+ end
121
+
122
+ #
123
+ def update(other)
124
+ super(cast(other))
125
+ end
126
+
127
+ # Same as #update.
128
+ def merge!(other)
129
+ super(cast(other))
130
+ end
131
+
132
+ #
133
+ def replace(other)
134
+ super(cast(other))
135
+ end
136
+
137
+ #
138
+ def values_at(*keys)
139
+ super(keys.map(&key_proc))
140
+ end
141
+
142
+ #
143
+ def to_hash
144
+ h = {}; each{ |k,v| h[k] = v }; h
145
+ end
146
+
147
+ #
148
+ alias_method :to_h, :to_hash
149
+
150
+ private
151
+
152
+ #
153
+ def cast(hash)
154
+ h
155
+ hash.each do |k,v|
156
+ h[key_proc[k]] = value_proc[v]
157
+ end
158
+ h
159
+ end
160
+
161
+ end
162
+
163
+
164
+ #class Hash
165
+ #
166
+ # # Convert a Hash to a CastingHash.
167
+ # def to_casting_hash(value_cast=nil, &key_cast)
168
+ # CastingHash.new(self, value_cast, &key_cast)
169
+ # end
170
+ #
171
+ #end
@@ -0,0 +1,430 @@
1
+ # = Dictionary.rb
2
+ #
3
+ # Copyright (c) 2005, 2009 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
+ # == Acknowledgments
15
+ #
16
+ # * Andrew Johnson (merge, to_a, inspect, shift and Hash[])
17
+ # * Jeff Sharpe (reverse and reverse!)
18
+ # * Thomas Leitner (has_key? and key?)
19
+ #
20
+ # Ported from OrderHash 2.0, Copyright (c) 2005 Jan Molic
21
+
22
+ # = Dictionary
23
+ #
24
+ # The Dictionary class is a Hash that preserves order.
25
+ # So it has some array-like extensions also. By defualt
26
+ # a Dictionary object preserves insertion order, but any
27
+ # order can be specified including alphabetical key order.
28
+ #
29
+ # == Usage
30
+ #
31
+ # Just require this file and use Dictionary instead of Hash.
32
+ #
33
+ # # You can do simply
34
+ # hsh = Dictionary.new
35
+ # hsh['z'] = 1
36
+ # hsh['a'] = 2
37
+ # hsh['c'] = 3
38
+ # p hsh.keys #=> ['z','a','c']
39
+ #
40
+ # # or using Dictionary[] method
41
+ # hsh = Dictionary['z', 1, 'a', 2, 'c', 3]
42
+ # p hsh.keys #=> ['z','a','c']
43
+ #
44
+ # # but this don't preserve order
45
+ # hsh = Dictionary['z'=>1, 'a'=>2, 'c'=>3]
46
+ # p hsh.keys #=> ['a','c','z']
47
+ #
48
+ # # Dictionary has useful extensions: push, pop and unshift
49
+ # p hsh.push('to_end', 15) #=> true, key added
50
+ # p hsh.push('to_end', 30) #=> false, already - nothing happen
51
+ # p hsh.unshift('to_begin', 50) #=> true, key added
52
+ # p hsh.unshift('to_begin', 60) #=> false, already - nothing happen
53
+ # p hsh.keys #=> ["to_begin", "a", "c", "z", "to_end"]
54
+ # p hsh.pop #=> ["to_end", 15], if nothing remains, return nil
55
+ # p hsh.keys #=> ["to_begin", "a", "c", "z"]
56
+ # p hsh.shift #=> ["to_begin", 30], if nothing remains, return nil
57
+ #
58
+ # == Usage Notes
59
+ #
60
+ # * You can use #order_by to set internal sort order.
61
+ # * #<< takes a two element [k,v] array and inserts.
62
+ # * Use ::auto which creates Dictionay sub-entries as needed.
63
+ # * And ::alpha which creates a new Dictionary sorted by key.
64
+ #
65
+ class Dictionary
66
+
67
+ include Enumerable
68
+
69
+ class << self
70
+ #--
71
+ # TODO is this needed? Doesn't the super class do this?
72
+ #++
73
+
74
+ def [](*args)
75
+ hsh = new
76
+ if Hash === args[0]
77
+ hsh.replace(args[0])
78
+ elsif (args.size % 2) != 0
79
+ raise ArgumentError, "odd number of elements for Hash"
80
+ else
81
+ while !args.empty?
82
+ hsh[args.shift] = args.shift
83
+ end
84
+ end
85
+ hsh
86
+ end
87
+
88
+ # Like #new but the block sets the order.
89
+ #
90
+ def new_by(*args, &blk)
91
+ new(*args).order_by(&blk)
92
+ end
93
+
94
+ # Alternate to #new which creates a dictionary sorted by key.
95
+ #
96
+ # d = Dictionary.alpha
97
+ # d["z"] = 1
98
+ # d["y"] = 2
99
+ # d["x"] = 3
100
+ # d #=> {"x"=>3,"y"=>2,"z"=>2}
101
+ #
102
+ # This is equivalent to:
103
+ #
104
+ # Dictionary.new.order_by { |key,value| key }
105
+
106
+ def alpha(*args, &block)
107
+ new(*args, &block).order_by_key
108
+ end
109
+
110
+ # Alternate to #new which auto-creates sub-dictionaries as needed.
111
+ #
112
+ # d = Dictionary.auto
113
+ # d["a"]["b"]["c"] = "abc" #=> { "a"=>{"b"=>{"c"=>"abc"}}}
114
+ #
115
+ def auto(*args)
116
+ #AutoDictionary.new(*args)
117
+ leet = lambda { |hsh, key| hsh[key] = new(&leet) }
118
+ new(*args, &leet)
119
+ end
120
+ end
121
+
122
+ # New Dictiionary.
123
+
124
+ def initialize(*args, &blk)
125
+ @order = []
126
+ @order_by = nil
127
+ if blk
128
+ dict = self # This ensure autmatic key entry effect the
129
+ oblk = lambda{ |hsh, key| blk[dict,key] } # dictionary rather then just the interal hash.
130
+ @hash = Hash.new(*args, &oblk)
131
+ else
132
+ @hash = Hash.new(*args)
133
+ end
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
+ def reorder
189
+ if @order_by
190
+ assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by(&@order_by)
191
+ @order = assoc.collect{ |k,v| k }
192
+ end
193
+ @order
194
+ end
195
+
196
+ #def ==( hsh2 )
197
+ # return false if @order != hsh2.order
198
+ # super hsh2
199
+ #end
200
+
201
+ def ==(hsh2)
202
+ if hsh2.is_a?( Dictionary )
203
+ @order == hsh2.order &&
204
+ @hash == hsh2.instance_variable_get("@hash")
205
+ else
206
+ false
207
+ end
208
+ end
209
+
210
+ def [] k
211
+ @hash[ k ]
212
+ end
213
+
214
+ def fetch(k, *a, &b)
215
+ @hash.fetch(k, *a, &b)
216
+ end
217
+
218
+ # Store operator.
219
+ #
220
+ # h[key] = value
221
+ #
222
+ # Or with additional index.
223
+ #
224
+ # h[key,index] = value
225
+
226
+ def []=(k, i=nil, v=nil)
227
+ if v
228
+ insert(i,k,v)
229
+ else
230
+ store(k,i)
231
+ end
232
+ end
233
+
234
+ def insert( i,k,v )
235
+ @order.insert( i,k )
236
+ @hash.store( k,v )
237
+ end
238
+
239
+ def store( a,b )
240
+ @order.push( a ) unless @hash.has_key?( a )
241
+ @hash.store( a,b )
242
+ end
243
+
244
+ def clear
245
+ @order = []
246
+ @hash.clear
247
+ end
248
+
249
+ def delete( key )
250
+ @order.delete( key )
251
+ @hash.delete( key )
252
+ end
253
+
254
+ def each_key
255
+ order.each { |k| yield( k ) }
256
+ self
257
+ end
258
+
259
+ def each_value
260
+ order.each { |k| yield( @hash[k] ) }
261
+ self
262
+ end
263
+
264
+ def each
265
+ order.each { |k| yield( k,@hash[k] ) }
266
+ self
267
+ end
268
+ alias each_pair each
269
+
270
+ def delete_if
271
+ order.clone.each { |k| delete k if yield(k,@hash[k]) }
272
+ self
273
+ end
274
+
275
+ def values
276
+ ary = []
277
+ order.each { |k| ary.push @hash[k] }
278
+ ary
279
+ end
280
+
281
+ def keys
282
+ order
283
+ end
284
+
285
+ def invert
286
+ hsh2 = self.class.new
287
+ order.each { |k| hsh2[@hash[k]] = k }
288
+ hsh2
289
+ end
290
+
291
+ def reject(&block)
292
+ self.dup.delete_if(&block)
293
+ end
294
+
295
+ def reject!( &block )
296
+ hsh2 = reject(&block)
297
+ self == hsh2 ? nil : hsh2
298
+ end
299
+
300
+ def replace(hsh2)
301
+ case hsh2
302
+ when Hash
303
+ @order = hsh2.keys
304
+ @hash = hsh2
305
+ else
306
+ @order = hsh2.order
307
+ @hash = hsh2.hash
308
+ end
309
+ reorder
310
+ end
311
+
312
+ def shift
313
+ key = order.first
314
+ key ? [key,delete(key)] : super
315
+ end
316
+
317
+ def unshift( k,v )
318
+ unless @hash.include?( k )
319
+ @order.unshift( k )
320
+ @hash.store( k,v )
321
+ true
322
+ else
323
+ false
324
+ end
325
+ end
326
+
327
+ def <<(kv)
328
+ push(*kv)
329
+ end
330
+
331
+ def push( k,v )
332
+ unless @hash.include?( k )
333
+ @order.push( k )
334
+ @hash.store( k,v )
335
+ true
336
+ else
337
+ false
338
+ end
339
+ end
340
+
341
+ def pop
342
+ key = order.last
343
+ key ? [key,delete(key)] : nil
344
+ end
345
+
346
+ def inspect
347
+ ary = []
348
+ each {|k,v| ary << k.inspect + "=>" + v.inspect}
349
+ '{' + ary.join(", ") + '}'
350
+ end
351
+
352
+ def dup
353
+ a = []
354
+ each{ |k,v| a << k; a << v }
355
+ self.class[*a]
356
+ end
357
+
358
+ def update( hsh2 )
359
+ hsh2.each { |k,v| self[k] = v }
360
+ reorder
361
+ self
362
+ end
363
+ alias :merge! update
364
+
365
+ def merge( hsh2 )
366
+ self.dup.update(hsh2)
367
+ end
368
+
369
+ def select
370
+ ary = []
371
+ each { |k,v| ary << [k,v] if yield k,v }
372
+ ary
373
+ end
374
+
375
+ def reverse!
376
+ @order.reverse!
377
+ self
378
+ end
379
+
380
+ def reverse
381
+ dup.reverse!
382
+ end
383
+
384
+ #
385
+ def first(x=nil)
386
+ return @hash[order.first] unless x
387
+ order.first(x).collect { |k| @hash[k] }
388
+ end
389
+
390
+ #
391
+ def last(x=nil)
392
+ return @hash[order.last] unless x
393
+ order.last(x).collect { |k| @hash[k] }
394
+ end
395
+
396
+ def length
397
+ @order.length
398
+ end
399
+ alias :size :length
400
+
401
+ def empty?
402
+ @hash.empty?
403
+ end
404
+
405
+ def has_key?(key)
406
+ @hash.has_key?(key)
407
+ end
408
+
409
+ def key?(key)
410
+ @hash.key?(key)
411
+ end
412
+
413
+ def to_a
414
+ ary = []
415
+ each { |k,v| ary << [k,v] }
416
+ ary
417
+ end
418
+
419
+ def to_s
420
+ self.to_a.to_s
421
+ end
422
+
423
+ def to_hash
424
+ @hash.dup
425
+ end
426
+
427
+ def to_h
428
+ @hash.dup
429
+ end
430
+ end