hashery 1.0.0 → 1.1.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.
@@ -0,0 +1,195 @@
1
+ # LinkedList
2
+ #
3
+ # Copyright (C) 2006 Kirk Haines <khaines@enigo.com>.
4
+ #
5
+ # General Public License (GPL)
6
+ #
7
+ # Permission is hereby granted, free of charge, to any person obtaining
8
+ # a copy of this software and associated documentation files (the
9
+ # "Software"), to deal in the Software without restriction, including
10
+ # without limitation the rights to use, copy, modify, merge, publish,
11
+ # distribute, sublicense, and/or sell copies of the Software, and to
12
+ # permit persons to whom the Software is furnished to do so, subject to
13
+ # the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+
26
+ require 'enumerator'
27
+
28
+ # LinkedList implements a simple doubly linked list with efficient
29
+ # hash-like element access.
30
+ #
31
+ # This is a simple linked list implementation with efficient random
32
+ # access of data elements. It was inspired by George Moscovitis'
33
+ # LRUCache implementation found in Facets 1.7.30, but unlike the
34
+ # linked list in that cache, this one does not require the use of a
35
+ # mixin on any class to be stored. The linked list provides the
36
+ # push, pop, shift, unshift, first, last, delete and length methods
37
+ # which work just like their namesakes in the Array class, but it
38
+ # also supports setting and retrieving values by key, just like a
39
+ # hash.
40
+ #
41
+ # LinkedList was ported from the original in Kirk Hanes IOWA web framework.
42
+ #
43
+ class LinkedList
44
+
45
+ include Enumerable
46
+
47
+ # Represents a single node of the linked list.
48
+
49
+ class Node
50
+ attr_accessor :key, :value, :prev_node, :next_node
51
+
52
+ def initialize(key=nil,value=nil,prev_node=nil,next_node=nil)
53
+ @key = key
54
+ @value = value
55
+ @prev_node = prev_node
56
+ @next_node = next_node
57
+ end
58
+ end
59
+
60
+ def initialize
61
+ @head = Node.new
62
+ @tail = Node.new
63
+ @lookup = Hash.new
64
+ node_join(@head,@tail)
65
+ end
66
+
67
+ def [](v)
68
+ @lookup[v].value
69
+ end
70
+
71
+ def []=(k,v)
72
+ if @lookup.has_key?(k)
73
+ @lookup[k].value = v
74
+ else
75
+ n = Node.new(k,v,@head,@head.next_node)
76
+ node_join(n,@head.next_node)
77
+ node_join(@head,n)
78
+ @lookup[k] = n
79
+ end
80
+ v
81
+ end
82
+
83
+ def empty?
84
+ @lookup.empty?
85
+ end
86
+
87
+ def delete(k)
88
+ n = @lookup.delete(k)
89
+ v = n ? node_purge(n) : nil
90
+ v
91
+ end
92
+
93
+ def first
94
+ @head.next_node.value
95
+ end
96
+
97
+ def last
98
+ @tail.prev_node.value
99
+ end
100
+
101
+ def shift
102
+ k = @head.next_node.key
103
+ n = @lookup.delete(k)
104
+ node_delete(n) if n
105
+ end
106
+
107
+ def unshift(v)
108
+ if @lookup.has_key?(v)
109
+ n = @lookup[v]
110
+ node_delete(n)
111
+ node_join(n,@head.next_node)
112
+ node_join(@head,n)
113
+ else
114
+ n = Node.new(v,v,@head,@head.next_node)
115
+ node_join(n,@head.next_node)
116
+ node_join(@head,n)
117
+ @lookup[v] = n
118
+ end
119
+ v
120
+ end
121
+
122
+ def pop
123
+ k = @tail.prev_node.key
124
+ n = @lookup.delete(k)
125
+ node_delete(n) if n
126
+ end
127
+
128
+ def push(v)
129
+ if @lookup.has_key?(v)
130
+ n = @lookup[v]
131
+ node_delete(n)
132
+ node_join(@tail.prev_node,n)
133
+ node_join(n,@tail)
134
+ else
135
+ n = Node.new(v,v,@tail.prev_node,@tail)
136
+ node_join(@tail.prev_node,n)
137
+ node_join(n,@tail)
138
+ @lookup[v] = n
139
+ end
140
+ v
141
+ end
142
+
143
+ def queue
144
+ r = []
145
+ n = @head
146
+ while (n = n.next_node) and n != @tail
147
+ r << n.key
148
+ end
149
+ r
150
+ end
151
+
152
+ def to_a
153
+ r = []
154
+ n = @head
155
+ while (n = n.next_node) and n != @tail
156
+ r << n.value
157
+ end
158
+ r
159
+ end
160
+
161
+ def length
162
+ @lookup.length
163
+ end
164
+
165
+ def each
166
+ n = @head
167
+ while (n = n.next_node) and n != @tail
168
+ yield(n.key,n.value)
169
+ end
170
+ end
171
+
172
+ private
173
+
174
+ def node_delete(n)
175
+ node_join(n.prev_node,n.next_node)
176
+ v = n.value
177
+ end
178
+
179
+ def node_purge(n)
180
+ node_join(n.prev_node,n.next_node)
181
+ v = n.value
182
+ n.value = nil
183
+ n.key = nil
184
+ n.next_node = nil
185
+ n.prev_node = nil
186
+ v
187
+ end
188
+
189
+ def node_join(a,b)
190
+ a.next_node = b
191
+ b.prev_node = a
192
+ end
193
+
194
+ end
195
+
@@ -1,6 +1,8 @@
1
+ # OpenCascade Copyright (c) 2006 Thomas Sawyer
2
+
3
+ require 'hashery/openobject'
1
4
  #require 'facets/boolean' # bool
2
5
  #require 'facets/nullclass'
3
- require 'hashery/openobject'
4
6
 
5
7
  # = OpenCascade
6
8
  #
@@ -79,4 +81,3 @@ class OpenCascade < OpenObject
79
81
  end
80
82
 
81
83
  end
82
-
@@ -5,7 +5,7 @@
5
5
  #
6
6
  # Because OpenHash is a subclass of Hash, it can do everything a Hash
7
7
  # can *unless* a Hash method has been explicity exempted for use
8
- # an an open read/writer via the #omit! method.
8
+ # as an open read/writer via the #omit! method.
9
9
 
10
10
  class OpenHash < Hash
11
11
 
@@ -7,7 +7,7 @@ require 'facets/basicobject'
7
7
  #
8
8
  # OpenObject is very similar to Ruby's own OpenStruct, but it offers some
9
9
  # advantages. With OpenStruct, slots with the same name as predefined
10
- # Object methods can not be used. With OpenObject, almost any slot can be
10
+ # Object methods cannot be used. With OpenObject, almost any slot can be
11
11
  # defined. OpenObject is a subclass of BasicObject to ensure all method
12
12
  # slots, except those that are absolutely essential, are open for use.
13
13
  #
@@ -164,13 +164,13 @@ class OpenObject < BasicObject
164
164
  end
165
165
 
166
166
  #
167
- def []=(k,v)
168
- @hash[k.to_sym] = v
167
+ def []=(key, value)
168
+ @hash[key.to_sym] = value
169
169
  end
170
170
 
171
171
  #
172
- def [](k)
173
- @hash[k.to_sym]
172
+ def [](key)
173
+ @hash[key.to_sym]
174
174
  end
175
175
 
176
176
  #
@@ -1,417 +1,167 @@
1
1
  # = OrderedHash
2
2
  #
3
- # The OrderedHash class is a Hash that preserves order.
4
- # So it has some array-like extensions also. By defualt
5
- # an OrderedHash object preserves insertion order, but any
6
- # order can be specified including alphabetical key order.
3
+ # A simple ordered hash implmentation, for users of
4
+ # Ruby 1.8.7 or less.
7
5
  #
8
- # == Usage
6
+ # NOTE: As of Ruby 1.9+ this class is not needed, since
7
+ # Ruby 1.9's standard Hash tracks inseration order.
9
8
  #
10
- # Just require this file and use OrderedHash instead of Hash.
11
- #
12
- # # You can do simply
13
- # hsh = OrderedHash.new
14
- # hsh['z'] = 1
15
- # hsh['a'] = 2
16
- # hsh['c'] = 3
17
- # p hsh.keys #=> ['z','a','c']
18
- #
19
- # # or using OrderedHash[] method
20
- # hsh = OrderedHash['z', 1, 'a', 2, 'c', 3]
21
- # p hsh.keys #=> ['z','a','c']
22
- #
23
- # # but this don't preserve order
24
- # hsh = OrderedHash['z'=>1, 'a'=>2, 'c'=>3]
25
- # p hsh.keys #=> ['a','c','z']
26
- #
27
- # # OrderedHash 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 OrderedHash sorted by key.
43
- #
44
- # == Acknowledgments
45
- #
46
- # * Andrew Johnson (merge, to_a, inspect, shift and Hash[])
47
- # * Jeff Sharpe (reverse and reverse!)
48
- # * Thomas Leitner (has_key? and key?)
49
- #
50
- # Ported from OrderHash 2.0, Copyright (c) 2005 Jan Molic
51
-
52
- class OrderedHash
53
-
54
- include Enumerable
55
-
56
- class << self
57
- #--
58
- # TODO is this needed? Doesn't the super class do this?
59
- #++
60
-
61
- def [](*args)
62
- hsh = new
63
- if Hash === args[0]
64
- hsh.replace(args[0])
65
- elsif (args.size % 2) != 0
66
- raise ArgumentError, "odd number of elements for Hash"
67
- else
68
- while !args.empty?
69
- hsh[args.shift] = args.shift
70
- end
71
- end
72
- hsh
73
- end
74
-
75
- # Like #new but the block sets the order.
76
- #
77
- def new_by(*args, &blk)
78
- new(*args).order_by(&blk)
79
- end
9
+ # This implementation derives from the same class in
10
+ # ActiveSupport library.
80
11
 
81
- # Alternate to #new which creates a dictionary sorted by key.
82
- #
83
- # d = OrderedHash.alpha
84
- # d["z"] = 1
85
- # d["y"] = 2
86
- # d["x"] = 3
87
- # d #=> {"x"=>3,"y"=>2,"z"=>2}
88
- #
89
- # This is equivalent to:
90
- #
91
- # OrderedHash.new.order_by { |key,value| key }
92
-
93
- def alpha(*args, &block)
94
- new(*args, &block).order_by_key
95
- end
12
+ class OrderedHash < ::Hash
13
+ def to_yaml_type
14
+ "!tag:yaml.org,2002:omap"
15
+ end
96
16
 
97
- # Alternate to #new which auto-creates sub-dictionaries as needed.
98
- #
99
- # d = OrderedHash.auto
100
- # d["a"]["b"]["c"] = "abc" #=> { "a"=>{"b"=>{"c"=>"abc"}}}
101
- #
102
- def auto(*args)
103
- #AutoOrderedHash.new(*args)
104
- leet = lambda { |hsh, key| hsh[key] = new(&leet) }
105
- new(*args, &leet)
17
+ def to_yaml(opts = {})
18
+ YAML.quick_emit(self, opts) do |out|
19
+ out.seq(taguri, to_yaml_style) do |seq|
20
+ each do |k, v|
21
+ seq.add(k => v)
22
+ end
23
+ end
106
24
  end
107
25
  end
108
26
 
109
- # New Dictiionary.
110
-
111
- def initialize(*args, &blk)
112
- @order = []
113
- @order_by = nil
114
- if blk
115
- dict = self # This ensure autmatic key entry effect the
116
- oblk = lambda{ |hsh, key| blk[dict,key] } # dictionary rather then just the interal hash.
117
- @hash = Hash.new(*args, &oblk)
118
- else
119
- @hash = Hash.new(*args)
27
+ # Hash is ordered in Ruby 1.9!
28
+ if RUBY_VERSION < '1.9'
29
+ def initialize(*args, &block)
30
+ super
31
+ @keys = []
120
32
  end
121
- end
122
33
 
123
- def order
124
- reorder if @order_by
125
- @order
126
- end
34
+ def self.[](*args)
35
+ ordered_hash = new
127
36
 
128
- # Keep dictionary sorted by a specific sort order.
37
+ if (args.length == 1 && args.first.is_a?(Array))
38
+ args.first.each do |key_value_pair|
39
+ next unless (key_value_pair.is_a?(Array))
40
+ ordered_hash[key_value_pair[0]] = key_value_pair[1]
41
+ end
129
42
 
130
- def order_by( &block )
131
- @order_by = block
132
- order
133
- self
134
- end
43
+ return ordered_hash
44
+ end
135
45
 
136
- # Keep dictionary sorted by key.
137
- #
138
- # d = OrderedHash.new.order_by_key
139
- # d["z"] = 1
140
- # d["y"] = 2
141
- # d["x"] = 3
142
- # d #=> {"x"=>3,"y"=>2,"z"=>2}
143
- #
144
- # This is equivalent to:
145
- #
146
- # OrderedHash.new.order_by { |key,value| key }
147
- #
148
- # The initializer OrderedHash#alpha also provides this.
149
-
150
- def order_by_key
151
- @order_by = lambda { |k,v| k }
152
- order
153
- self
154
- end
46
+ unless (args.size % 2 == 0)
47
+ raise ArgumentError.new("odd number of arguments for Hash")
48
+ end
155
49
 
156
- # Keep dictionary sorted by value.
157
- #
158
- # d = OrderedHash.new.order_by_value
159
- # d["z"] = 1
160
- # d["y"] = 2
161
- # d["x"] = 3
162
- # d #=> {"x"=>3,"y"=>2,"z"=>2}
163
- #
164
- # This is equivalent to:
165
- #
166
- # OrderedHash.new.order_by { |key,value| value }
167
-
168
- def order_by_value
169
- @order_by = lambda { |k,v| v }
170
- order
171
- self
172
- end
50
+ args.each_with_index do |val, ind|
51
+ next if (ind % 2 != 0)
52
+ ordered_hash[val] = args[ind + 1]
53
+ end
173
54
 
174
- #
175
- def reorder
176
- if @order_by
177
- assoc = @order.collect{ |k| [k,@hash[k]] }.sort_by(&@order_by)
178
- @order = assoc.collect{ |k,v| k }
55
+ ordered_hash
179
56
  end
180
- @order
181
- end
182
57
 
183
- #def ==( hsh2 )
184
- # return false if @order != hsh2.order
185
- # super hsh2
186
- #end
187
-
188
- def ==(hsh2)
189
- if hsh2.is_a?( OrderedHash )
190
- @order == hsh2.order &&
191
- @hash == hsh2.instance_variable_get("@hash")
192
- else
193
- false
58
+ def initialize_copy(other)
59
+ super(other)
60
+ @keys = other.keys
194
61
  end
195
- end
196
-
197
- def [] k
198
- @hash[ k ]
199
- end
200
-
201
- def fetch(k, *a, &b)
202
- @hash.fetch(k, *a, &b)
203
- end
204
62
 
205
- # Store operator.
206
- #
207
- # h[key] = value
208
- #
209
- # Or with additional index.
210
- #
211
- # h[key,index] = value
212
-
213
- def []=(k, i=nil, v=nil)
214
- if v
215
- insert(i,k,v)
216
- else
217
- store(k,i)
63
+ def []=(key, value)
64
+ @keys << key unless key?(key)
65
+ super(key, value)
218
66
  end
219
- end
220
-
221
- def insert( i,k,v )
222
- @order.insert( i,k )
223
- @hash.store( k,v )
224
- end
225
-
226
- def store( a,b )
227
- @order.push( a ) unless @hash.has_key?( a )
228
- @hash.store( a,b )
229
- end
230
-
231
- def clear
232
- @order = []
233
- @hash.clear
234
- end
235
-
236
- def delete( key )
237
- @order.delete( key )
238
- @hash.delete( key )
239
- end
240
-
241
- def each_key
242
- order.each { |k| yield( k ) }
243
- self
244
- end
245
-
246
- def each_value
247
- order.each { |k| yield( @hash[k] ) }
248
- self
249
- end
250
-
251
- def each
252
- order.each { |k| yield( k,@hash[k] ) }
253
- self
254
- end
255
- alias each_pair each
256
-
257
- def delete_if
258
- order.clone.each { |k| delete k if yield(k,@hash[k]) }
259
- self
260
- end
261
-
262
- def values
263
- ary = []
264
- order.each { |k| ary.push @hash[k] }
265
- ary
266
- end
267
-
268
- def keys
269
- order
270
- end
271
-
272
- def invert
273
- hsh2 = self.class.new
274
- order.each { |k| hsh2[@hash[k]] = k }
275
- hsh2
276
- end
277
-
278
- def reject(&block)
279
- self.dup.delete_if(&block)
280
- end
281
67
 
282
- def reject!( &block )
283
- hsh2 = reject(&block)
284
- self == hsh2 ? nil : hsh2
285
- end
286
-
287
- def replace(hsh2)
288
- case hsh2
289
- when Hash
290
- @order = hsh2.keys
291
- @hash = hsh2
292
- else
293
- @order = hsh2.order
294
- @hash = hsh2.hash
68
+ def delete(key)
69
+ if has_key? key
70
+ index = @keys.index(key)
71
+ @keys.delete_at(index)
72
+ end
73
+ super(key)
295
74
  end
296
- reorder
297
- end
298
-
299
- def shift
300
- key = order.first
301
- key ? [key,delete(key)] : super
302
- end
303
75
 
304
- def unshift( k,v )
305
- unless @hash.include?( k )
306
- @order.unshift( k )
307
- @hash.store( k,v )
308
- true
309
- else
310
- false
76
+ def delete_if
77
+ super
78
+ sync_keys!
79
+ self
311
80
  end
312
- end
313
-
314
- def <<(kv)
315
- push(*kv)
316
- end
317
81
 
318
- def push( k,v )
319
- unless @hash.include?( k )
320
- @order.push( k )
321
- @hash.store( k,v )
322
- true
323
- else
324
- false
82
+ def reject!
83
+ super
84
+ sync_keys!
85
+ self
325
86
  end
326
- end
327
87
 
328
- def pop
329
- key = order.last
330
- key ? [key,delete(key)] : nil
331
- end
88
+ def reject(&block)
89
+ dup.reject!(&block)
90
+ end
332
91
 
333
- def inspect
334
- ary = []
335
- each {|k,v| ary << k.inspect + "=>" + v.inspect}
336
- '{' + ary.join(", ") + '}'
337
- end
92
+ def keys
93
+ @keys.dup
94
+ end
338
95
 
339
- def dup
340
- a = []
341
- each{ |k,v| a << k; a << v }
342
- self.class[*a]
343
- end
96
+ def values
97
+ @keys.collect{ |key| self[key] }
98
+ end
344
99
 
345
- def update( hsh2 )
346
- hsh2.each { |k,v| self[k] = v }
347
- reorder
348
- self
349
- end
350
- alias :merge! update
100
+ def to_hash
101
+ self
102
+ end
351
103
 
352
- def merge( hsh2 )
353
- self.dup.update(hsh2)
354
- end
104
+ def to_a
105
+ @keys.map{ |key| [ key, self[key] ] }
106
+ end
355
107
 
356
- def select
357
- ary = []
358
- each { |k,v| ary << [k,v] if yield k,v }
359
- ary
360
- end
108
+ def each_key
109
+ @keys.each{ |key| yield(key) }
110
+ end
361
111
 
362
- def reverse!
363
- @order.reverse!
364
- self
365
- end
112
+ def each_value
113
+ @keys.each{ |key| yield(self[key]) }
114
+ end
366
115
 
367
- def reverse
368
- dup.reverse!
369
- end
116
+ def each
117
+ @keys.each{ |key| yield(key, self[key]) }
118
+ end
370
119
 
371
- #
372
- def first(x=nil)
373
- return @hash[order.first] unless x
374
- order.first(x).collect { |k| @hash[k] }
375
- end
120
+ alias_method :each_pair, :each
376
121
 
377
- #
378
- def last(x=nil)
379
- return @hash[order.last] unless x
380
- order.last(x).collect { |k| @hash[k] }
381
- end
122
+ def clear
123
+ super
124
+ @keys.clear
125
+ self
126
+ end
382
127
 
383
- def length
384
- @order.length
385
- end
386
- alias :size :length
128
+ def shift
129
+ k = @keys.first
130
+ v = delete(k)
131
+ [k, v]
132
+ end
387
133
 
388
- def empty?
389
- @hash.empty?
390
- end
134
+ def merge!(other_hash)
135
+ other_hash.each{ |k,v| self[k] = v }
136
+ self
137
+ end
391
138
 
392
- def has_key?(key)
393
- @hash.has_key?(key)
394
- end
139
+ def merge(other_hash)
140
+ dup.merge!(other_hash)
141
+ end
395
142
 
396
- def key?(key)
397
- @hash.key?(key)
398
- end
143
+ # When replacing with another hash, the initial order of our
144
+ # keys must come from the other hash, ordered or not.
145
+ def replace(other)
146
+ super
147
+ @keys = other.keys
148
+ self
149
+ end
399
150
 
400
- def to_a
401
- ary = []
402
- each { |k,v| ary << [k,v] }
403
- ary
404
- end
151
+ def inspect
152
+ "#<OrderedHash #{super}>"
153
+ end
405
154
 
406
- def to_s
407
- self.to_a.to_s
155
+ private
156
+ def sync_keys!
157
+ @keys.delete_if{ |k| !key?(k) }
158
+ end
408
159
  end
160
+ end
409
161
 
410
- def to_hash
411
- @hash.dup
412
- end
162
+ require 'yaml'
413
163
 
414
- def to_h
415
- @hash.dup
416
- end
164
+ YAML.add_builtin_type("omap") do |type, val|
165
+ ActiveSupport::OrderedHash[val.map(&:to_a).map(&:first)]
417
166
  end
167
+