Deps mixin customization

Hey,
with hanami-controller 2.x making it a bit more natural to use Deps mixing, rather than own DI mechanism, i am curious, is it currently possible to load the constant, but not actually initialise object with Deps ?
Certain libraries are exposing different methods for initialising classes depending on circumstances, and I would like to have smth like

include Deps["my_lib_1": "my_libs.my_lib_1"]
def handle
  my_lib_1.for_collection.new(...)
end

Currently, it seems like Deps is trying to give me the instance, which is not what I want in all the cases.
Is there a way to configure this behaviour ?

Here is something I found in dry-system docs: Method: Dry::System::Config::ComponentDir#instance= — Documentation for dry-system (1.0.1)

And used that in my config/app.rb for workers:

prepare_container do |container|
      container.config.component_dirs.dir("app") do |dir|
        dir.instance = proc do |component|
          if component.key.match?(/workers\./)
            component.loader.constant(component)
          else
            component.loader.call(component)
          end
        end
      end
    end

Now it injects class instead of instance for all my workers. I’m not sure if that’s the best approach though.

1 Like

This is not recommended due to the coupling this introduces into the code. The purpose of the DI system is to decouple the specific constant identity from the usage, so that you are free to swap implementations without breaking things.

It sounds like you need to provide an alternative instance kind that doesn’t use the normal new(...) form. There is support for this use-case via an undocumented feature called the manifest registry.

# config/app.rb
module Bookshelf
  class App < Hanami::App
    prepare_container do |container|
      container.config.registrations_dir = "config/registrations"
    end
  end
end

# config/registrations/my_lib.rb
Bookshelf::App.register("my_lib.collection") do
  MyLib.for_collection.new(...)
end

# app/actions/foo.rb
module Bookshelf
  module Actions
    class Foo < Hanami::Action
      include Deps[my_lib: "my_lib.collection"]

      def handle(req, res)
        my_lib.call(...)
      end
    end
  end
end

The goal of autoinjection is for the container keys to be the canonical identity for your various dependencies. If you start mixing them with constants, you will lose the flexibility this affords you, and create coupling between implementation and usage. This is not always the wrong choice, but it should be made with caution.

1 Like

That is an interesting idea, thanks for suggestion !
Probably this would be even more tricky if I need smth like MyLib.for_collection(req.params[...]).new....
Might be easier to not use Deps in this case :slight_smile:

If you need to inject request state into the initializer, then no I wouldn’t recommend using Deps.