hashery 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,274 @@
1
+ # LRU based Hash
2
+
3
+ require 'enumerator'
4
+
5
+ # Hash with LRU expiry policy. There are at most max_size elements in a
6
+ # LRUHash. When adding more elements old elements are removed according
7
+ # to LRU policy.
8
+ #
9
+ # by Robert Klemme
10
+
11
+ class LRUHash
12
+
13
+ include Enumerable
14
+
15
+ attr_reader :max_size
16
+
17
+ attr_accessor :default
18
+ attr_accessor :default_proc
19
+ attr_accessor :release_proc
20
+
21
+ def initialize(max_size, default_value = nil, &block)
22
+ @max_size = normalize_max(max_size)
23
+ @default = default_value
24
+ @default_proc = block
25
+
26
+ @h = {}
27
+ @head = Node.new
28
+ @tail = front(Node.new)
29
+ end
30
+
31
+ def each_pair
32
+ if block_given?
33
+ each_node do |n|
34
+ yield [n.key, n.value]
35
+ end
36
+ else
37
+ enum_for :each_pair
38
+ end
39
+ end
40
+
41
+ alias each each_pair
42
+
43
+ def each_key
44
+ if block_given?
45
+ each_node do |n|
46
+ yield n.key
47
+ end
48
+ else
49
+ enum_for :each_key
50
+ end
51
+ end
52
+
53
+ def each_value
54
+ if block_given?
55
+ each_node do |n|
56
+ yield n.value
57
+ end
58
+ else
59
+ enum_for :each_value
60
+ end
61
+ end
62
+
63
+ def size
64
+ @h.size
65
+ end
66
+
67
+ def empty?
68
+ @head.succ.equal? @tail
69
+ end
70
+
71
+ def fetch(key, &b)
72
+ n = @h[key]
73
+
74
+ if n
75
+ front(n).value
76
+ else
77
+ (b || FETCH)[key]
78
+ end
79
+ end
80
+
81
+ def [](key)
82
+ fetch(key) do |k|
83
+ @default_proc ? @default_proc[self, k] : default
84
+ end
85
+ end
86
+
87
+ def keys
88
+ @h.keys
89
+ end
90
+
91
+ def values
92
+ @h.map {|k,n| n.value}
93
+ end
94
+
95
+ def has_key?(key)
96
+ @h.has_key? key
97
+ end
98
+
99
+ alias key? has_key?
100
+ alias member? has_key?
101
+ alias include? has_key?
102
+
103
+ def has_value?(value)
104
+ each_pair do |k, v|
105
+ return true if value.eql? v
106
+ end
107
+
108
+ false
109
+ end
110
+
111
+ alias value? has_value?
112
+
113
+ def values_at(*key_list)
114
+ key_list.map {|k| self[k]}
115
+ end
116
+
117
+ def assoc(key)
118
+ n = @h[key]
119
+
120
+ if n
121
+ front(n)
122
+ [n.key, n.value]
123
+ end
124
+ end
125
+
126
+ def rassoc(value)
127
+ each_node do |n|
128
+ if value.eql? n.value
129
+ front(n)
130
+ return [n.key, n.value]
131
+ end
132
+ end
133
+ nil
134
+ end
135
+
136
+ def key(value)
137
+ pair = rassoc(value) and pair.first
138
+ end
139
+
140
+ def store(key, value)
141
+ # same optimization as in Hash
142
+ key = key.dup.freeze if String === key && !key.frozen?
143
+
144
+ n = @h[key]
145
+
146
+ unless n
147
+ if size == max_size
148
+ # reuse node to optimize memory usage
149
+ n = delete_oldest
150
+ n.key = key
151
+ n.value = value
152
+ else
153
+ n = Node.new key, value
154
+ end
155
+
156
+ @h[key] = n
157
+ end
158
+
159
+ front(n).value = value
160
+ end
161
+
162
+ alias []= store
163
+
164
+ def delete(key)
165
+ n = @h[key] and remove_node(n).value
166
+ end
167
+
168
+ def delete_if
169
+ each_node do |n|
170
+ remove_node n if yield n.key, n.value
171
+ end
172
+ end
173
+
174
+ def max_size=(limit)
175
+ limit = normalize_max(limit)
176
+
177
+ while size > limit
178
+ delete_oldest
179
+ end
180
+
181
+ @max_size = limit
182
+ end
183
+
184
+ def clear
185
+ until empty?
186
+ delete_oldest
187
+ end
188
+
189
+ self
190
+ end
191
+
192
+ def to_s
193
+ s = nil
194
+ each_pair {|k, v| (s ? (s << ', ') : s = '{') << k.to_s << '=>' << v.to_s}
195
+ s ? (s << '}') : '{}'
196
+ end
197
+
198
+ alias inspect to_s
199
+
200
+ private
201
+
202
+ # iterate nodes
203
+ def each_node
204
+ n = @head.succ
205
+
206
+ until n.equal? @tail
207
+ succ = n.succ
208
+ yield n
209
+ n = succ
210
+ end
211
+
212
+ self
213
+ end
214
+
215
+ # move node to front
216
+ def front(node)
217
+ node.insert_after(@head)
218
+ end
219
+
220
+ # remove the node and invoke release_proc
221
+ # if set
222
+ def remove_node(node)
223
+ n = @h.delete(node.key)
224
+ n.unlink
225
+ release_proc and release_proc[n.key, n.value]
226
+ n
227
+ end
228
+
229
+ # remove the oldest node returning the node
230
+ def delete_oldest
231
+ n = @tail.pred
232
+ raise "Cannot delete from empty hash" if @head.equal? n
233
+ remove_node n
234
+ end
235
+
236
+ # Normalize the argument in order to be usable as max_size
237
+ # criterion is that n.to_i must be an Integer and it must
238
+ # be larger than zero.
239
+ def normalize_max(n)
240
+ n = n.to_i
241
+ raise ArgumentError, 'Invalid max_size: %p' % n unless Integer === n && n > 0
242
+ n
243
+ end
244
+
245
+ #
246
+ FETCH = Proc.new {|k| raise KeyError, 'key not found'}
247
+
248
+ # A single node in the doubly linked LRU list of nodes
249
+ Node = Struct.new :key, :value, :pred, :succ do
250
+ def unlink
251
+ pred.succ = succ if pred
252
+ succ.pred = pred if succ
253
+ self.succ = self.pred = nil
254
+ self
255
+ end
256
+
257
+ def insert_after(node)
258
+ raise 'Cannot insert after self' if equal? node
259
+ return self if node.succ.equal? self
260
+
261
+ unlink
262
+
263
+ self.succ = node.succ
264
+ self.pred = node
265
+
266
+ node.succ.pred = self if node.succ
267
+ node.succ = self
268
+
269
+ self
270
+ end
271
+ end
272
+
273
+ end
274
+
@@ -0,0 +1,64 @@
1
+ # Memoizer
2
+ #
3
+ # Copyright (c) 2006 Erik Veenstra
4
+ #
5
+ # See http://javathink.blogspot.com/2008/09/what-is-memoizer-and-why-should-you.html
6
+
7
+ # Memoizer wraps objects to provide cached method calls.
8
+ #
9
+ # class X
10
+ # def initialize ; @tick = 0 ; end
11
+ # def tick; @tick + 1; end
12
+ # def memo; @memo ||= Memoizer.new(self) ; end
13
+ # end
14
+ #
15
+ # x = X.new
16
+ # x.tick #=> 1
17
+ # x.memo.tick #=> 2
18
+ # x.tick #=> 3
19
+ # x.memo.tick #=> 2
20
+ # x.tick #=> 4
21
+ # x.memo.tick #=> 2
22
+ #
23
+ # You can also use to cache collections of objects to gain code speed ups.
24
+ #
25
+ # points = points.collect{|point| Memoizer.cache(point)}
26
+ #
27
+ # After our algorithm has finished using points, we want to get rid of
28
+ # these Memoizer objects. That's easy:
29
+ #
30
+ # points = points.collect{|point| point.__self__ }
31
+ #
32
+ # Or if you prefer (it is ever so slightly safer):
33
+ #
34
+ # points = points.collect{|point| Memoizer.uncache(point)}
35
+ #
36
+ class Memoizer
37
+
38
+ #private :class, :clone, :display, :type, :method, :to_a, :to_s
39
+ private *instance_methods(true).select{ |m| m.to_s !~ /^__/ }
40
+
41
+ def initialize(object)
42
+ @self = object
43
+ @cache = {}
44
+ end
45
+
46
+ def __self__ ; @self ; end
47
+
48
+ # Not thread-safe! Speed is important in caches... ;]
49
+ def method_missing(method_name, *args, &block)
50
+ @cache[[method_name, args, block]] ||= @self.__send__(method_name, *args, &block)
51
+ end
52
+
53
+ #def self; @self; end
54
+
55
+ def self.cache(object)
56
+ new(object)
57
+ end
58
+
59
+ def self.uncache(cached_object)
60
+ cached_object.instance_variable_get('@self')
61
+ end
62
+
63
+ end
64
+
@@ -0,0 +1,82 @@
1
+ #require 'facets/boolean' # bool
2
+ #require 'facets/nullclass'
3
+ require 'hashery/openobject'
4
+
5
+ # = OpenCascade
6
+ #
7
+ # OpenCascade is subclass of OpenObject. It differs in a few
8
+ # significant ways.
9
+ #
10
+ # The main reason this class is labeled "cascade", every internal
11
+ # Hash is transformed into an OpenCascade dynamically upon access.
12
+ # This makes it easy to create "cascading" references.
13
+ #
14
+ # h = { :x => { :y => { :z => 1 } } }
15
+ # c = OpenCascade[h]
16
+ # c.x.y.z #=> 1
17
+ #
18
+ # As soon as you access a node it automatically becomes an OpenCascade.
19
+ #
20
+ # c = OpenCascade.new #=> #<OpenCascade:0x7fac3680ccf0 {}>
21
+ # c.r #=> #<OpenCascade:0x7fac368084c0 {}>
22
+ # c.a.b #=> #<OpenCascade:0x7fac3680a4f0 {}>
23
+ #
24
+ # But if you set a node, then that will be it's value.
25
+ #
26
+ # c.a.b = 4 #=> 4
27
+ #
28
+ # To query a node without causing the auto-creation of an OpenCasade
29
+ # object, use the ?-mark.
30
+ #
31
+ # c.a.z? #=> nil
32
+ #
33
+ # Finally, you can set a node and get the reciever back using
34
+ # the !-mark.
35
+ #
36
+ # c = OpenCascade.new #=> #<OpenCascade:0x7fac3680ccf0 {}>
37
+ # c.x!(4).y!(3) #=> #<OpenCascade:0x7fac3680ccf0 {:x=>4, :y=>3}>
38
+ #
39
+ #--
40
+ # Last, when an entry is not found, 'null' is returned rather then 'nil'.
41
+ # This allows for run-on entries withuot error. Eg.
42
+ #
43
+ # o = OpenCascade.new
44
+ # o.a.b.c #=> null
45
+ #
46
+ # Unfortuately this requires an explict test for null? in 'if' conditions.
47
+ #
48
+ # if o.a.b.c.null? # true if null
49
+ # if o.a.b.c.nil? # true if nil or null
50
+ # if o.a.b.c.not? # true if nil or null or false
51
+ #
52
+ # So be sure to take that into account.
53
+ #++
54
+
55
+ class OpenCascade < OpenObject
56
+
57
+ def method_missing(sym, *args, &blk)
58
+ type = sym.to_s[-1,1]
59
+ name = sym.to_s.gsub(/[=!?]$/, '').to_sym
60
+ case type
61
+ when '='
62
+ self[name] = args.first
63
+ when '!'
64
+ @hash.__send__(key, *args, &blk)
65
+ when '?'
66
+ self[name]
67
+ else
68
+ if key?(name)
69
+ val = self[name]
70
+ if Hash === val
71
+ self[name] = OpenCascade.new(val) #self.class.new(val)
72
+ else
73
+ self[name]
74
+ end
75
+ else
76
+ self[name] = OpenCascade.new #self.class.new
77
+ end
78
+ end
79
+ end
80
+
81
+ end
82
+
@@ -0,0 +1,65 @@
1
+ # = OpenHash
2
+ #
3
+ # OpenHash is very similar to Ruby's own OpenStruct, but it offers some
4
+ # useful advantages in that it is a true Hash object.
5
+ #
6
+ # Because OpenHash is a subclass of Hash, it can do everything a Hash
7
+ # can *unless* a Hash method has been explicity exempted for use
8
+ # an an open read/writer via the #omit! method.
9
+
10
+ class OpenHash < Hash
11
+
12
+ # New OpenHash.
13
+ def initialize(data={})
14
+ super()
15
+ merge!(data)
16
+ end
17
+
18
+ #
19
+ def respond_to?(name)
20
+ key?(name.to_sym) || super(name)
21
+ end
22
+
23
+ #
24
+ def to_h
25
+ dup
26
+ end
27
+
28
+ #
29
+ def to_hash
30
+ dup
31
+ end
32
+
33
+ #
34
+ def inspect
35
+ super
36
+ end
37
+
38
+ # Omit specific Hash methods from slot protection.
39
+ def omit!(*methods)
40
+ methods.reject!{ |x| x.to_s =~ /^__/ }
41
+ (class << self; self; end).class_eval{ private *methods }
42
+ end
43
+
44
+ # Route get and set calls.
45
+ def method_missing(s,*a, &b)
46
+ type = s.to_s[-1,1]
47
+ name = s.to_s.sub(/[!?=]$/, '')
48
+ key = name.to_sym
49
+ case type
50
+ when '='
51
+ self[key] = a[0]
52
+ #when '!'
53
+ # self[s] = OpenHash.new
54
+ when '?'
55
+ key?(key)
56
+ else
57
+ if key?(key)
58
+ self[key]
59
+ else
60
+ super(s,*a,&b)
61
+ end
62
+ end
63
+ end
64
+
65
+ end