Automatic Dependency Injection where you get to see and keep control of the constructor.
There are 3 ways to inject a dependency.
Place a Dependency expression as the default value of your dependency:
class MyClass
include LowType
def initialize(logger: Dependency)
@logger = logger # => "logger" is injected.
end
endℹ️ This method requires LowType in order to use the def(dependency: Dependency) syntax.
Or you may like to use the more traditional include syntax:
class MyClass
include Dependencies[:logger]
def my_method
@logger # => "@logger" is injected.
end
endThis method hides and creates the constructor on your behalf.
logger = Providers[:my_provider]ℹ️ Use this method only when necessary, it's the least "in the spirit" of dependency injection and takes more lines of code to stub in a test.
Provide the dependency with:
Providers.define(:logger) do
Logger.new
endNamespaced string keys are fine too:
Providers.define('billing.payment_provider') do
PaymentProvider.new
endProviders lets you do something special; mix "classical" dependency injection (passing an arg to new) with "provider" style dependency injection (via a framework):
Providers.define(:provider_dependency) do
ProviderDependency.new
end
# Define both a "provider" and a "classical" dependency:
class MyClass
include LowType
def initialize(provider_dependency: Dependency, classical_dependency:)
@provider_dependency = provider_dependency
@classical_dependency = classical_dependency
end
end
# Then call without "provider_dependency":
MyClass.new(classical_dependency: ClassicalDependency.new)The omitted provider_dependency argument will automatically be injected from the provider_dependency provider by Providers!
Now you get to have your classical dependency cake 🍰 and eat it too with an automatically injected dependency spoon 🥣
A Dependency Expression defines the dependency to be injected, the provider that will inject it and the name of the local variable that it will be made available as.
The def(dependency: Dependency) syntax is an Expression; an object composed via a query builder like interface.
ℹ️ The value after the pipe | becomes the provider key. When the provider key is omitted then the positional/keyword argument becomes the provider key.
To define a provider with a different name to that of the local variable do:
def initialize(dependency_one: Dependency | :provider_one)
dependency_one # => Dependency injected from :provider_one.
endFor providers with string keys do:
def initialize(dependency_two: Dependency | 'billing.provider_two')
dependency_two # => Dependency injected from 'billing.provider_two'.
endDependencies on multiple lines:
def initialize(
dependency_one: Dependency | :provider_one,
dependency_two: Dependency | 'billing.provider_two',
)
dependency_one # => Dependency injected from :provider_one.
dependency_two # => Dependency injected from 'billing.provider_two'.
endThe include style syntax supports the same functionallity as the Dependency Expression syntax.
Multiple dependencies:
class MyClass
include Dependencies[:dependency_one, :dependency_two]
endDependency with differing dependency and provider key:
class MyClass
include Dependencies[dependency_one: :provider_one]
# Instance variable @dependency_one is now available on instantiation.
endNamespaced dependency:
class MyClass
include Dependencies['billing.provider_two']
# Dependency injected without the namespace as @provider_two.
endSeparating dependencies on multiple lines:
class MyClass
include Dependencies[
:dependency_one,
'billing.provider_two',
dependency_three: :provider_three,
dependency_four: 'billing.provider_four',
:five_dependencies_is_still_quite_reasonable_yeah,
]
endlogger = Providers['namespace.my_provider']Multiple dependencies:
dependency_one, dependency_two = Providers[:provider_one, 'provider_two']A boot sequence taken from the boot file of Raindeer:
require 'low_event' # Defines 'low.event.pool' provider.
Providers.define('rain.router') do
require_relative 'router/router'
Rain::Router.new
end
Providers.define('rain.matrix') do
require_relative 'matrix/matrix'
Rain::Matrix.new(event_pool: Providers['low.event.pool'])
end
Providers.define('low.loop') do
require 'low_loop'
LowLoop.new(router: Providers['rain.router'], renderer: Providers['rain.matrix'])
endAdd gem 'providers' to your Gemfile then:
bundle install
