hashery 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+