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