Class: Zache

Inherits:
Object
  • Object
show all
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 Yegor Bugayenko

License

MIT

Defined Under Namespace

Classes: Fake

Instance Method Summary collapse

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 and is already expired.


77
78
79
80
81
82
# File 'lib/zache.rb', line 77

def initialize(sync: true, dirty: false)
  @hash = {}
  @sync = sync
  @dirty = dirty
  @mutex = Mutex.new
end

Instance Method Details

#cleanObject

Remove keys that are expired.


181
182
183
# File 'lib/zache.rb', line 181

def clean
  synchronized { @hash.delete_if { |_key, value| expired?(value) } }
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 hash, it will be removed by this method and the result will be FALSE.

Returns:

  • (Boolean)

121
122
123
124
125
126
127
128
# File 'lib/zache.rb', line 121

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.

Returns:

  • (Boolean)

132
133
134
135
# File 'lib/zache.rb', line 132

def expired?(key)
  rec = @hash[key]
  !rec.nil? && rec[:start] < Time.now - rec[:lifetime]
end

#get(key, lifetime: 2**32, dirty: 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, 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 and is already expired.


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/zache.rb', line 100

def get(key, lifetime: 2**32, dirty: false)
  if block_given?
    if (dirty || @dirty) && locked? && expired?(key) && @hash.key?(key)
      return @hash[key][:value]
    end
    synchronized { calc(key, lifetime) { yield } }
  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?

Returns:

  • (Boolean)

145
146
147
# File 'lib/zache.rb', line 145

def locked?
  @mutex.locked?
end

#mtime(key) ⇒ Object

Returns the modification time of the key, if it exists. If not, current time is returned.


139
140
141
142
# File 'lib/zache.rb', line 139

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.


150
151
152
153
154
155
156
157
158
# File 'lib/zache.rb', line 150

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 hash, by the provied key. If the key is absent and the block is provided, the block will be called.


162
163
164
# File 'lib/zache.rb', line 162

def remove(key)
  synchronized { @hash.delete(key) { yield if block_given? } }
end

#remove_allObject

Remove all keys from the cache.


167
168
169
# File 'lib/zache.rb', line 167

def remove_all
  synchronized { @hash = {} }
end

#remove_byObject

Remove all keys that match the block.


172
173
174
175
176
177
178
# File 'lib/zache.rb', line 172

def remove_by
  synchronized do
    @hash.keys.each do |k|
      @hash.delete(k) if yield(k)
    end
  end
end

#sizeObject

Total number of keys currently in cache.


85
86
87
# File 'lib/zache.rb', line 85

def size
  @hash.size
end