Mize
Lightweight memoization for Ruby. Annotate any method or function with a
single memoize call and Mize handles result caching, invalidation, and
Rails-aware reload for you.
Works with Ruby 3.1 – 4.0 (tested via .all_images.yml).
Installation
gem install mize
or add to your Gemfile:
gem 'mize'
Usage
Memoize a method
Results are cached per receiver. Two different objects get independent caches even for identical arguments:
class A
@@c = 0
memoize method:
def foo(x)
"foo #{x} #{@@c += 1}"
end
end
a1 = A.new
a1.foo(23) # => "foo 23 1"
a1.foo(23) # => "foo 23 1" (cached)
a2 = A.new
a2.foo(23) # => "foo 23 2" (different receiver)
a2.foo(23) # => "foo 23 2" (cached)
Memoize a function
Results are cached globally — the receiver is ignored. All instances share one cache entry per argument set:
class B
@@c = 0
memoize function:
def foo(x)
"foo #{x} #{@@c += 1}"
end
end
b1 = B.new
b1.foo(23) # => "foo 23 1"
b2 = B.new
b2.foo(23) # => "foo 23 1" (same cache as b1)
Options
| Option | Default | Effect |
|---|---|---|
freeze: |
false |
Freeze the cached result if possible |
store_nil: |
true |
Cache nil results; false recomputes |
memoize method:, freeze: true, store_nil: false
def expensive_query
# ...
end
Keyword arguments are supported and included in the cache key:
memoize method:
def lookup(name, limit: 10)
# ...
end
lookup("flori") # => computes
lookup("flori") # => cached
lookup("flori", limit: 5) # => separate cache entry
Clearing the cache
Clear a single method's cache entries by name:
a1.mize_cache_clear_name(:foo)
Clear all cached entries for a receiver (method memoization) or for the class (function memoization):
a1.mize_cache_clear # method: only a1's cache
B.mize_cache_clear # function: global shared cache
Global cache clearing
Mize.cache_clear walks the entire object space for anything including
Mize::CacheProtocol and clears it. Use this in test setups or teardown:
Mize.cache_clear
Rails integration
When Rails is loaded, Mize registers a railtie that calls
Mize.cache_clear on every to_prepare block (i.e. on each code reload in
development). If your model or class defines a #reload method, Mize
prepends its own reload that delegates to super and then clears
memoized entries automatically.
Custom cache backend
The default backend is a plain in-memory Hash. Swap in your own
(e.g. Redis, Memcached) by implementing the
Mize::CacheProtocol interface:
class RedisCache
include Mize::CacheProtocol
def initialize
@r = Redis.new
end
def read(name, = nil)
@r.get("mize:#{name}")
end
def write(name, value, = nil)
@r.set("mize:#{name}", Marshal.dump(value))
end
def exist?(name, = nil)
!@r.get("mize:#{name}").nil?
end
def delete(name, = nil)
@r.del("mize:#{name}")
end
def clear( = nil)
# delete matching keys or flushdb
end
def each_name(&block)
# yield each key
self
end
def prototype
dup
end
end
Mize.configure do
cache RedisCache.new
end
Any object that include Mize::CacheProtocol and implements read,
write, exist?, delete, clear, each_name, and prototype will
work.
Documentation
Complete API documentation is available at: GitHub.io
Repository
Author
License
MIT