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 © 2002 Yoshinori K. Okuji <[email protected]> Copyright © 2009 Michael Fellinger <[email protected]>

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

Defined Under Namespace

Classes: CacheObject

Constant Summary collapse

VERSION =
'0.3'
KeyError =

On 1.8 we raise IndexError, on 1.9 we raise KeyError

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Struct

#values_at

Constructor Details

#initialize(options = {}, &hook) ⇒ LRUHash

Returns 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 Attribute Details

#expirationObject

Returns the value of attribute expiration

Returns:

  • (Object)

    the current value of expiration



25
26
27
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 25

def expiration
  @expiration
end

#hitsObject

Returns the value of attribute hits

Returns:

  • (Object)

    the current value of hits



25
26
27
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 25

def hits
  @hits
end

#hookObject

Returns the value of attribute hook

Returns:

  • (Object)

    the current value of hook



25
26
27
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 25

def hook
  @hook
end

#listObject

Returns the value of attribute list

Returns:

  • (Object)

    the current value of list



25
26
27
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 25

def list
  @list
end

#max_countObject

Returns the value of attribute max_count

Returns:

  • (Object)

    the current value of max_count



25
26
27
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 25

def max_count
  @max_count
end

#max_totalObject

Returns the value of attribute max_total

Returns:

  • (Object)

    the current value of max_total



25
26
27
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 25

def max_total
  @max_total
end

#max_valueObject

Returns the value of attribute max_value

Returns:

  • (Object)

    the current value of max_value



25
26
27
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 25

def max_value
  @max_value
end

#missesObject

Returns the value of attribute misses

Returns:

  • (Object)

    the current value of misses



25
26
27
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 25

def misses
  @misses
end

#objsObject

Returns the value of attribute objs

Returns:

  • (Object)

    the current value of objs



25
26
27
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 25

def objs
  @objs
end

#total_sizeObject

Returns the value of attribute total_size

Returns:

  • (Object)

    the current value of total_size



25
26
27
# File 'lib/ramaze/snippets/ramaze/lru_hash.rb', line 25

def total_size
  @total_size
end

Instance Method Details

#[](key) ⇒ Object



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

#[]=(key, obj) ⇒ Object



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

#clearObject 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

#delete(key) ⇒ Object 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

#each_key(&block) ⇒ Object



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

#each_pairObject



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

#each_valueObject



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

#empty?Boolean

Returns:

  • (Boolean)


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

def empty?
  objs.empty?
end

#expireObject



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

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

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.



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

#index(given_value) ⇒ Object



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

#key?(key) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#keysObject



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

def keys
  objs.keys
end

#sizeObject Also known as: length



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

def size
  objs.size
end

#statisticsObject



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

#store(key, value) ⇒ Object



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

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

#to_hashObject



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

def to_hash
  objs.dup
end

#value?(given_value) ⇒ Boolean

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

#valuesObject



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

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