Class: Ramaze::LRUHash

Inherits:
Struct show all
Includes:
Enumerable
Defined in:
lib/ramaze/snippets/ramaze/lru_hash.rb

Overview

A Hash-alike LRU cache that provides fine-grained control over content restrictions.

It allows you to set:

  • a maximum number of elements
  • the maximum amount of memory used for all elements
  • the allowed memory-size per element
  • time to live

Differences to the original implementation include:

  • The Cache is now a Struct for speed
  • Object memory size is obtained via Marshal::dump instead of #to_s

Note that due to calculating object size with Marshal, you might have to do some evaluation as to how large your values will be when marshaled, for example a String will have String#size + 10. This differs from object to object and between versions of Marshal, so be generous.

Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org> Copyright © 2009 Michael Fellinger <manveru@rubyists.com>

You may redistribute it and/or modify it under the same terms as Ruby.

Defined Under Namespace

Classes: CacheObject

Constant Summary

VERSION =
'0.3'
KeyError =

On 1.8 we raise IndexError, on 1.9 we raise KeyError

Module.const_defined?(:KeyError) ? KeyError : IndexError

Instance Method Summary (collapse)

Methods inherited from Struct

#values_at

Constructor Details

- (LRUHash) initialize(options = {}, &hook)

A new instance of LRUHash



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 35

def initialize(options = {}, &hook)
  self.max_value  = options[:max_value]
  self.max_total  = options[:max_total]
  self.max_count  = options[:max_count]
  self.expiration = options[:expiration]

  avoid_insane_options

  self.hook = hook

  self.objs = {}
  self.list = []

  self.total_size = 0
  self.hits = self.misses = 0
end

Instance Method Details

- (Object) [](key)



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 121

def [](key)
  expire

  unless objs.key?(key)
    self.misses += 1
    return
  end

  obj = objs[key]
  obj.atime = Time.now.to_i

  list.delete_if{|list_key| key == list_key }
  list << key

  self.hits += 1
  obj.content
end

- (Object) []=(key, obj)



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 139

def []=(key, obj)
  expire

  invalidate key if key?(key)

  size = Marshal.dump(obj).size

  if max_value && max_value < max_total
    warn "%p isn't cached because it exceeds max_value %p" % [obj, max_value]
    return obj
  end

  if max_value.nil? && max_total && max_total < size
    warn "%p isn't cached because it exceeds max_total: %p" % [obj, max_total]
    return obj
  end

  invalidate list.first if max_count && max_count == list.size

  self.total_size += size

  if max_total
    invalidate list.first until total_size < max_total
  end

  objs[key] = CacheObject.new(obj, size, Time.now.to_i)
  list << key

  obj
end

- (Object) clear Also known as: invalidate_all



103
104
105
106
107
108
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 103

def clear
  objs.each{|key, obj| hook.call(key, obj) } if hook
  objs.clear
  list.clear
  self.total_size = 0
end

- (Object) delete(key) Also known as: invalidate



89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 89

def delete(key)
  return unless objs.key?(key)
  obj = objs[key]

  hook.call(key, obj.content) if hook
  self.total_size -= obj.size
  objs.delete key

  list.delete_if{|list_key| key == list_key }

  obj.content
end

- (Object) each_key(&block)



180
181
182
183
184
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 180

def each_key(&block)
  return enum_for(:each_key) unless block_given?
  objs.each_key{|key| yield key }
  self
end

- (Object) each_pair



174
175
176
177
178
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 174

def each_pair
  return enum_for(:each_pair) unless block_given?
  objs.each{|key, obj| yield key, obj.content }
  self
end

- (Object) each_value



186
187
188
189
190
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 186

def each_value
  return enum_for(:each_value) unless block_given?
  objs.each_value{|obj| yield obj.content }
  self
end

- (Boolean) empty?

Returns:

  • (Boolean)


192
193
194
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 192

def empty?
  objs.empty?
end

- (Object) expire



111
112
113
114
115
116
117
118
119
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 111

def expire
  return unless expiration
  now = Time.now.to_i

  list.each_with_index do |key, index|
    break unless (objs[key].atime + expiration) <= now
    invalidate key
  end
end

- (Object) fetch(key, default = (p_default = true; nil))

Note that this method diverges from the default behaviour of the Ruby Hash. If the cache doesn’t find content for the given key, it will store the given default instead. Optionally it also takes a block, the return value of the block is then stored and returned.

Examples:

lru = LRUHash.new
lru.fetch(:a) # => KeyError: key not found: :a
lru.fetch(:a, :b) # => :b
lru.fetch(:a) # => :b
lru.fetch(:c){|key| key.to_s } # => 'c'
lru.fetch(:c) # => 'c'


209
210
211
212
213
214
215
216
217
218
219
220
221
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 209

def fetch(key, default = (p_default = true; nil))
  if key?(key)
    value = self[key]
  elsif p_default.nil?
    value = self[key] = default
  elsif block_given?
    value = self[key] = yield(key)
  else
    raise KeyError, "key not found: %p" % [key]
  end

  value
end

- (Object) index(given_value)



64
65
66
67
68
69
70
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 64

def index(given_value)
  objs.each do |key, obj|
    return key if given_value == obj.content
  end

  nil
end

- (Boolean) key?(key)

Returns:

  • (Boolean)


52
53
54
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 52

def key?(key)
  objs.key?(key)
end

- (Object) keys



72
73
74
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 72

def keys
  objs.keys
end

- (Object) size Also known as: length



76
77
78
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 76

def size
  objs.size
end

- (Object) statistics



223
224
225
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 223

def statistics
  {:size => total_size, :count => list.size, :hits => hits, :misses => misses}
end

- (Object) store(key, value)



170
171
172
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 170

def store(key, value)
  self[key] = value
end

- (Object) to_hash



81
82
83
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 81

def to_hash
  objs.dup
end

- (Boolean) value?(given_value)

Returns:

  • (Boolean)


56
57
58
59
60
61
62
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 56

def value?(given_value)
  objs.each do |key, obj|
    return true if given_value == obj.content
  end

  false
end

- (Object) values



85
86
87
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 85

def values
  objs.map{|key, obj| obj.content }
end