I’m coming from .Net “world” and I’m having a hard time finding dependency injection information with Ruby.
Recently, I’ve discovered Dry Container and started studying it. So I got to the point where I’d like to design my classes simply using constructor injection:
class SomeClass
def initialize(some_repository) @some_repository = some_repository
end
end
And the container should be able to resolve the dependencies. Is it possible?
I don’t like the idea of forcing my classes to include that constant. I think this pollutes the classes and may force changes to all of them if I change the container in the future (for some other that uses a different injection strategy).
I wish I could use constructor injection (coded by me) and let the container create all the object graph from the application entry point (concept named Composition Root/Agent). The container would be responsible to create the first object required (the web controller, for instance) and all dependencies, but I don’t know if it’s possible to intercept this creation in Ruby frameworks. I couldn’t find anything like that in any Ruby applications.
Sorry if I didn’t make myself clear (feel free to ask). I’ll keep studying Ruby and searching more content about DI in Ruby.
A bit of time has passed. But I consider the use of the Composition-Root pattern as well. You can absolutely define your own Composition-Root class. In rails you could introduce it in your application_controller. So you would have all the creational behavior near the entry point of your application.
What are the key aspects?
get rid of the dependency container and the auto_inject.
don’t pollute the classes with these depenency-constants.
creational behavior in one single place
when moving files/refactoring you do not have to search the whole codebase for the occurrences, you only have to change it in one place
I am not particularly sure if this is the right approach for ruby or dynamically typed languages. But I am in the process of gathering more information.
“Pollute” is too vague, can you articulate what that actually means?
Everywhere where you want to use the DI-Container, you have to introduce the container dependency:
class CreateUser
include Import["users_repository"]
def call(user_attrs)
users_repository.create(user_attrs)
end
end
If you want to auto-register these dependencies, then you need the full namespace of the dependency within the include of the container. My premise is, that if you would need to move the dependencies to another folder, then you have to search in all files where the dependency is included. Maybe that is a tradeoff with the auto-register, and you could name the dependency independently of its path. But there is still the need for the container to include.
The composition root pattern is pretty similar. But you would only use it in the beginning of your application/request.