Resolve is an effect for injecting dependencies. A simple usage example:
include Dry::Effects.Resolve(:user_repo)
name = values.values_at(:first_name, :last_name).join(' ')
user_repo.create(**values.merge(name: name))
end
end
Providing user_repo in tests:
RSpec.describe CreateUser do
# adds #provide
include Dry::Effects::Handler.Resolve
subject(:create_user) { described_class.new }
let(:user_repo) { double(:user_repo) }
it 'creates a user' do
expect(user_repo).to receive(:create).with(
first_name: 'John',
last_name: 'Doe',
name: 'John Doe'
)
provide(user_repo: user_repo) { create_user.(first_name: 'John', last_name: 'Doe') }
end
end
Providing dependencies with middleware:
include Dry::Effects::Handler.Resolve
@app = app
@dependencies = dependencies
end
provide(@dependencies) { @app.(env) }
end
end
Then in config.ru:
# ...some bootstrapping code ...
use ProviderMiddleware, user_repo: UserRepo.new
run Application.new
Compatibility with dry-container and dry-system
Any object that responds to .key? and .[] can be used for providing dependencies. Thus, the default Resolve provider is compatible with dry-container and dry-system out of the box.
# Assuming App is a subclass of Dry::System::Container
provide(App) { @app.(env) }
end
Providing static values
One can pass a container to the module builder:
include Dry::Effects::Handler.Resolve(Application)
@app = app
end
# Here Application will be used for resolving dependencies
provide { @app.(env) }
end
end
Injecting many keys and using aliases
include Dry::Effects.Resolve(
# Injected as .schema
# but resolved with 'operations.create_user.schema'
'operations.create_user.schema',
# Injected as .repo
# but resolved with 'repos.user_repo'
repo: 'repos.user_repo'
)
result = schema.(values)
if result.success?
user = repo.create(result.to_h)
[:ok, user]
else
[:err, result]
end
end
end
Overriding dependencies in test environment
Sometimes you may want to push dependencies through an existing handler. This is normally needed for testing when you want to replace some dependencies in a test environment for an assembled app, like a Rack application. Passing overridable: true enables it:
include Dry::Effects::Handler.Resolve
@app = app
end
provide(Application, overridable: overridable?) { @app.(env) }
end
.eql?('test')
end
end
Now in tests, you can override some dependencies at will:
RSpec.describe do
include Rack::Test::Methods
include Dry::Effects::Handler.Provider
let(:app) do
# building an assembled rack app
end
describe 'POST /users' do
let(:user_repo) { double(:user_repo) }
it 'creates a user' do
expect(user_repo).to receive(:create).with(
first_name: 'John', last_name: 'Doe'
).and_return(1)
# Overriding one dependency
# It will only work if `overridable: true` is passed
# in the middleware
provide('repos.user_repo' => user_repo) do
post(
'/users',
JSON.dump(first_name: 'John', last_name: 'Doe'),
'CONTENT_TYPE' => 'application/json'
)
end
end
end
end