Class: SafeMemoize::Stores::RailsCache

Inherits:
Base
  • Object
show all
Defined in:
lib/safe_memoize/stores/rails_cache.rb

Overview

Cache store adapter backed by any +ActiveSupport::Cache::Store+.

Not auto-required. Add to your Rails initializer: require "safe_memoize/stores/rails_cache"

Compatible with any +ActiveSupport::Cache::Store+ implementation (+MemoryStore+, +FileStore+, +MemCacheStore+, +RedisCacheStore+, etc.) and with +Rails.cache+ directly.

Because +ActiveSupport::Cache+ returns +nil+ for both a cache miss and a cached +nil+ value, this adapter wraps every value in a two-element sentinel envelope before writing. The envelope is transparent to callers.

TTL is forwarded as +expires_in:+ to the cache, so the underlying store manages expiry natively — there is no lazy-expiry overhead on read.

#clear uses +delete_matched+ scoped to the adapter's namespace, so it never clears entries belonging to other parts of the application. The backend must respond to +delete_matched+ (all standard Rails cache stores do); a +NotImplementedError+ is raised if it does not.

#keys returns an empty array — +ActiveSupport::Cache::Store+ does not expose a standard key enumeration API. Override the method if your backend supports it.

Examples:

Basic setup

# config/initializers/safe_memoize.rb
require "safe_memoize/stores/rails_cache"

MEMO_STORE = SafeMemoize::Stores::RailsCache.new(Rails.cache)

class MyService
  prepend SafeMemoize
  def fetch(id) = http_get(id)
  memoize :fetch, store: MEMO_STORE, ttl: 300
end

Dedicated cache store (recommended for production)

MEMO_STORE = SafeMemoize::Stores::RailsCache.new(
  ActiveSupport::Cache::RedisCacheStore.new(url: ENV["REDIS_URL"]),
  namespace: "myapp:memo"
)

Constant Summary collapse

VALUE_TAG =

Tag prepended to every stored value so cached +nil+/+false+ are distinguishable from a cache miss.

"safe_memoize:v1"

Constants inherited from Base

Base::MISS

Instance Method Summary collapse

Constructor Details

#initialize(cache, namespace: "safe_memoize") ⇒ RailsCache

Returns a new instance of RailsCache.

Parameters:

  • cache (ActiveSupport::Cache::Store)

    the cache store to use; typically +Rails.cache+ or a dedicated store instance

  • namespace (String) (defaults to: "safe_memoize")

    key prefix used to scope all entries; defaults to +"safe_memoize"+



58
59
60
61
# File 'lib/safe_memoize/stores/rails_cache.rb', line 58

def initialize(cache, namespace: "safe_memoize")
  @cache = cache
  @namespace = namespace
end

Instance Method Details

#clearvoid

This method returns an undefined value.

Removes all entries written by this adapter (scoped to the namespace).

Delegates to +delete_matched+ on the underlying store; raises +NotImplementedError+ if the store does not support it.

Raises:

  • (NotImplementedError)

    if the backing store does not respond to +delete_matched+



96
97
98
99
100
101
102
103
# File 'lib/safe_memoize/stores/rails_cache.rb', line 96

def clear
  unless @cache.respond_to?(:delete_matched)
    raise NotImplementedError,
      "#{@cache.class} does not support delete_matched — " \
      "implement clear manually or use a store that supports it (e.g. MemoryStore, RedisCacheStore)"
  end
  @cache.delete_matched(/\A#{Regexp.escape(@namespace)}:/)
end

#delete(key) ⇒ void

This method returns an undefined value.

Parameters:

  • key (Object)


84
85
86
# File 'lib/safe_memoize/stores/rails_cache.rb', line 84

def delete(key)
  @cache.delete(cache_key(key))
end

#exist?(key) ⇒ Boolean

Parameters:

  • key (Object)

Returns:

  • (Boolean)


117
118
119
# File 'lib/safe_memoize/stores/rails_cache.rb', line 117

def exist?(key)
  @cache.exist?(cache_key(key))
end

#keysArray

Returns an empty array.

+ActiveSupport::Cache::Store+ does not expose a key enumeration API. Override this method if your backend supports key listing.

Returns:

  • (Array)


111
112
113
# File 'lib/safe_memoize/stores/rails_cache.rb', line 111

def keys
  []
end

#read(key) ⇒ Object

Returns the stored value, or Base::MISS if absent or unrecognised.

Parameters:

  • key (Object)

    cache key (serialized with Marshal + Base64)

Returns:

  • (Object)

    the stored value, or Base::MISS if absent or unrecognised



65
66
67
68
69
70
# File 'lib/safe_memoize/stores/rails_cache.rb', line 65

def read(key)
  raw = @cache.read(cache_key(key))
  return MISS unless raw.is_a?(Array) && raw.length == 2 && raw[0] == VALUE_TAG

  raw[1]
end

#write(key, value, expires_in: nil) ⇒ void

This method returns an undefined value.

Parameters:

  • key (Object)

    cache key

  • value (Object)

    value to store (may be +nil+ or +false+)

  • expires_in (Numeric, nil) (defaults to: nil)

    TTL in seconds forwarded to the cache as +expires_in:+; +nil+ means no expiry



77
78
79
80
# File 'lib/safe_memoize/stores/rails_cache.rb', line 77

def write(key, value, expires_in: nil)
  opts = expires_in ? {expires_in: expires_in} : {}
  @cache.write(cache_key(key), [VALUE_TAG, value], **opts)
end