Cache

Cache provides two interfaces for caching. Set up a handler:

require 'dry/effects'

class CacheMiddleware
  # Providing scope is required
  # All cache values will be scoped with this key
  include Dry::Effects::Handler.Cache(:blog)

  def initialize(app)
    @app = app
  end

  def call(env)
    with_cache { @app.env }
  end
end

Using prepend:

require 'dry/effects'

class ShowUsers
  include Dry::Effects.Resolve(:user_repo)
  # It will cache .find_user calls
  # Users with the same id won't be searched twice
  # Effectively (no pun intended),
  # it's `memoize` scoped with the call in CacheMiddleware
  prepend Dry::Effects.Cache(blog: :find_user)

  def call(user_ids)
    users = user_ids.map { find_user(id) }
    # ...
  end

  def find_user(id)
    user_repo.find(id)
  end
end

Or using include:

require 'dry/effects'

class ShowUsers
  include Dry::Effects.Resolve(:user_repo)
  # When included, adds #cache method
  include Dry::Effects.Cache(:blog)

  def call(user_ids)
    users = user_ids.map { cache(:user, id) { user_repo.find(id) } }
    # ...
  end
end

Cache longevity

The default cache handler doesn't (yet) support long-lived storage. Cache values are discarded once with_cache returns.

Using in tests

It's usually OK to have a global handler for cache effects:

require 'dry/effects'

with_cache = Object.new.extend(Dry::Effects::Handler.Cache(:my_app, as: :call))

RSpec.configure do |config|
  config.around(:each) { with_cache.(&ex) }
end