Class: Zache
- Inherits:
-
Object
- Object
- Zache
- Defined in:
- lib/zache.rb
Overview
It is a very simple thread-safe in-memory cache with an ability to expire keys automatically, when their lifetime is over. Use it like this:
require 'zache'
zache = Zache.new
# Expires in 5 minutes
v = zache.get(:count, lifetime: 5 * 60) { expensive() }
For more information read README file.
- Author
-
Yegor Bugayenko ([email protected])
- Copyright
-
Copyright © 2018-2025 Yegor Bugayenko
- License
-
MIT
Defined Under Namespace
Classes: Fake
Instance Method Summary collapse
-
#clean ⇒ Integer
Remove keys that are expired.
-
#empty? ⇒ Boolean
Returns TRUE if the cache is empty, FALSE otherwise.
-
#exists?(key, dirty: false) ⇒ Boolean
Checks whether the value exists in the cache by the provided key.
-
#expired?(key) ⇒ Boolean
Checks whether the key exists in the cache and is expired.
-
#get(key, lifetime: 2**32, dirty: false, placeholder: nil, eager: false) { ... } ⇒ Object
Gets the value from the cache by the provided key.
-
#initialize(sync: true, dirty: false) ⇒ Zache
constructor
Makes a new object of the cache.
-
#locked? ⇒ Boolean
Is cache currently locked doing something?.
-
#mtime(key) ⇒ Time
Returns the modification time of the key, if it exists.
-
#put(key, value, lifetime: 2**32) ⇒ Object
Put a value into the cache.
-
#remove(key) { ... } ⇒ Object
Removes the value from the cache, by the provided key.
-
#remove_all ⇒ Hash
Remove all keys from the cache.
-
#remove_by {|key| ... } ⇒ Integer
Remove all keys that match the block.
-
#size ⇒ Integer
Total number of keys currently in cache.
Constructor Details
#initialize(sync: true, dirty: false) ⇒ Zache
Makes a new object of the cache.
“sync” is whether the hash is thread-safe (‘true`) or not (`false`); it is recommended to leave this parameter untouched, unless you really know what you are doing.
If the dirty argument is set to true, a previously calculated result will be returned if it exists, even if it is already expired.
87 88 89 90 91 92 |
# File 'lib/zache.rb', line 87 def initialize(sync: true, dirty: false) @hash = {} @sync = sync @dirty = dirty @mutex = Mutex.new end |
Instance Method Details
#clean ⇒ Integer
Remove keys that are expired. This cleans up the cache by removing all keys where the lifetime has been exceeded.
248 249 250 251 252 253 254 |
# File 'lib/zache.rb', line 248 def clean synchronized do size_before = @hash.size @hash.delete_if { |key, _value| expired?(key) } size_before - @hash.size end end |
#empty? ⇒ Boolean
Returns TRUE if the cache is empty, FALSE otherwise.
259 260 261 |
# File 'lib/zache.rb', line 259 def empty? @hash.empty? end |
#exists?(key, dirty: false) ⇒ Boolean
Checks whether the value exists in the cache by the provided key. Returns TRUE if the value is here. If the key is already expired in the cache, it will be removed by this method and the result will be FALSE.
157 158 159 160 161 162 163 164 |
# File 'lib/zache.rb', line 157 def exists?(key, dirty: false) rec = @hash[key] if expired?(key) && !dirty && !@dirty @hash.delete(key) rec = nil end !rec.nil? end |
#expired?(key) ⇒ Boolean
Checks whether the key exists in the cache and is expired. If the key is absent FALSE is returned.
171 172 173 174 |
# File 'lib/zache.rb', line 171 def expired?(key) rec = @hash[key] !rec.nil? && rec[:start] < Time.now - rec[:lifetime] end |
#get(key, lifetime: 2**32, dirty: false, placeholder: nil, eager: false) { ... } ⇒ Object
Gets the value from the cache by the provided key.
If the value is not found in the cache, it will be calculated via the provided block. If the block is not given and the key doesn’t exist or is expired, an exception will be raised (unless dirty is set to true). The lifetime must be in seconds. The default lifetime is huge, which means that the key will never be expired.
If the dirty argument is set to true, a previously calculated result will be returned if it exists, even if it is already expired.
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/zache.rb', line 121 def get(key, lifetime: 2**32, dirty: false, placeholder: nil, eager: false, &block) if block_given? return @hash[key][:value] if (dirty || @dirty) && locked? && expired?(key) && @hash.key?(key) if eager return @hash[key][:value] if @hash.key?(key) put(key, placeholder, lifetime: 0) Thread.new do synchronized do calc(key, lifetime, &block) end end placeholder else synchronized do calc(key, lifetime, &block) end end else rec = @hash[key] if expired?(key) return rec[:value] if dirty || @dirty @hash.delete(key) rec = nil end raise 'The key is absent in the cache' if rec.nil? rec[:value] end end |
#locked? ⇒ Boolean
Is cache currently locked doing something?
189 190 191 |
# File 'lib/zache.rb', line 189 def locked? @mutex.locked? end |
#mtime(key) ⇒ Time
Returns the modification time of the key, if it exists. If not, current time is returned.
181 182 183 184 |
# File 'lib/zache.rb', line 181 def mtime(key) rec = @hash[key] rec.nil? ? Time.now : rec[:start] end |
#put(key, value, lifetime: 2**32) ⇒ Object
Put a value into the cache.
199 200 201 202 203 204 205 206 207 |
# File 'lib/zache.rb', line 199 def put(key, value, lifetime: 2**32) synchronized do @hash[key] = { value: value, start: Time.now, lifetime: lifetime } end end |
#remove(key) { ... } ⇒ Object
Removes the value from the cache, by the provided key. If the key is absent and the block is provided, the block will be called.
215 216 217 |
# File 'lib/zache.rb', line 215 def remove(key) synchronized { @hash.delete(key) { yield if block_given? } } end |
#remove_all ⇒ Hash
Remove all keys from the cache.
222 223 224 |
# File 'lib/zache.rb', line 222 def remove_all synchronized { @hash = {} } end |
#remove_by {|key| ... } ⇒ Integer
Remove all keys that match the block.
231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/zache.rb', line 231 def remove_by synchronized do count = 0 @hash.each_key do |k| if yield(k) @hash.delete(k) count += 1 end end count end end |
#size ⇒ Integer
Total number of keys currently in cache.
97 98 99 |
# File 'lib/zache.rb', line 97 def size @hash.size end |