Hi, I have some problems with understanding how to pass manual dependencies in business logic code.
Can it possible to use them in code when class instance with manual dependency is auto injected dependency to another class instance too?
As example I have 4 classes. RootClass, MyClass, MyNestedClass, MyAnotherNestedClass.
RootClass has dependency which uses MyClass.
MyClass has dependency which uses MyNestedClass or MyAnotherNestedClass.
class MyClass
include Import[my_nested_class: nested_class]
def call(options)
nested_class.call(options)
end
end
MyClass can be called with MyNestedClass or MyAnotherNestedClass as dependency. And I understand how it can be done with usual object creation.
MyClass.new.call(options)
# or
MyClass.new(nested_class: MyAnotherNestedClass.new).call(options)
But when I want to call an instance of MyClass as auto injected dependency of another class instance (RootClass) I can not do it with manual dependencies passing.
class RootClass
include Import[my_class]
def call(options)
my_class.call(options) # and I cannot pass MyAnotherNestedClass as dependency with this syntax
end
end
Can it be done somehow or I don’t understand some principles of DI usage?
P.S. I know about registering classes instead of instances, but I want to know can I do this with instances or not.
Yes, this can be done but you must change how you’re thinking about the class and instances.
The class here is being used as an Inversion of Control Container whereas the instance is intended to be used as a first-class function.
In regular OOP Ruby, people tend to think of classes and instances as part of the same concern. The goal of dry-auto_inject is to handle the class instantiation for you so you can just think of logic in a functional way.
So, the way to do what you want here is to define multiple identities for RootClass which inject different dependencies.
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'dry-auto_inject'
end
require 'dry/core/container'
require 'dry/auto_inject'
class ExampleContainer
extend Dry::Core::Container::Mixin
register "my_class" do
MyClass.new
end
register "my_nested_class" do
MyNestedClass.new
end
register "my_another_nested_class" do
MyAnotherNestedClass.new
end
register "my_class.another" do
MyClass.new(nested_class: resolve("my_another_nested_class"))
end
register "root_class" do
RootClass.new
end
register "root_class.alternate" do
RootClass.new(my_class: resolve("my_class.another"))
end
end
Import = Dry::AutoInject(ExampleContainer)
class RootClass
include Import[:my_class]
def call = my_class.call
end
class MyClass
include Import[nested_class: "my_nested_class"]
def call = nested_class.call
end
class MyNestedClass
def call = "called from #{self.class.name}"
end
class MyAnotherNestedClass
def call = "called from #{self.class.name}"
end
puts ExampleContainer["root_class"].call
puts ExampleContainer["root_class.alternate"].call