These are chat archives for dry-rb/chat

11th
Dec 2016
Andrew Thauer
@andrewthauer
Dec 11 2016 22:22

Question - I’m looking at using dry-rb to build a re-usable gem that allows the consuming application to define & configure the persistenance layer, and other infrastructure related dependencies. I’ve got this more or less working when I explictly ask the container to resolve the dependency just before I need the service.

class SaveMyEntity
  def call(entity)
    my_repo = MyContainer[‘persistence.my_repo’]
    my_repo.save(entity)
  end
end

I’m wondering if I can also use either dry-auto_inject and/or dry-system to also do this? However, from what I can see, using the auto loading and the auto injector work differently.

Import = Dry::AutoInject(MyContainer)
class SaveMyEntity
  include Import[‘persistence.my_repo’] # throws Dry::System::ComponentLoadError: could not load component
end
Tim Riley
@timriley
Dec 11 2016 22:32
@andrewthauer That should work. How are you setting up that “my_repo” registration?
Andrew Thauer
@andrewthauer
Dec 11 2016 22:35
Sorry, I realized that using Application.inject vs Dry::AutoInject seem to behave differently. My mistake, the above example does work, but the Application.injector (suggested by the dry-system example), throws the above exception.
Does one lazy load dependencies and the other does not?
Tim Riley
@timriley
Dec 11 2016 22:36
Dry::System.injector will give you an injector that lazy loads for containers that aren’t finalized, yes
But it’ll only lazy load according to certain strategies (though we’re actively improving that), so if your “persistence.my_repo” registration falls outside of that, it’ll not work
Andrew Thauer
@andrewthauer
Dec 11 2016 22:40
Import = Dry::AutoInject(Container) # works
Import = Container.injector #  throws Dry::System::ComponentLoadError: could not load component

I’m currently calling one of those 2 lines inside a module pretty much when the gem is initially loaded. The first line seems to work, but the 2nd throws the error when I use the Import in a subsequent ruby file. Should I be deferrring these until after the consuming application has a chance to register it’s own dependencies?

Basically, just trying to figure out the correct pattern to use moving forward. I’m not sure, if I should be bothering with dry-system in my case or just leverage dry-container and possibly dry-auto_inject.

Tim Riley
@timriley
Dec 11 2016 22:48
You’re probably seeing an error with dry-system’s injector because it’s trying to do the lazy load as soon as it is included into a class
whereas plain auto-inject doesn’t do any lazy loading at all
which is why it doesn’t bother to resolve the deps until you actually initialize that class
in dry-system master branch we’ve actually changed lazy loading so it does it upon class initialization too, not at include-time anymore
so that might be worth trying
but yeah, in general with dry-system for a non-finalized container, if the lazy loading fails, you’ll get an error
for a finalized container, your deps should all be registered anyway, since the container is frozen at that point
Andrew Thauer
@andrewthauer
Dec 11 2016 22:54
I don’t recall actually using the finalize! method (although I did see it). Is this required or not? I don’t have any issues having the consumer of the gem have to explicitly call something to finalize and freeze the dependencies. Just need to make sure things get loaded correctly without a wierd order of operation issues. Is there particular pattern you would suggest to allow for a consumer to configure dependencies?
Tim Riley
@timriley
Dec 11 2016 23:05
So you’re expecting the consumer to supply their own container?
Andrew Thauer
@andrewthauer
Dec 11 2016 23:12
Or configure pieces of the main gem’s application container. It’s kindof two old. The initial usage of this new domain model will be integrated into 1 system which I’d like to leverage the existing persistence there. However, in the future the same domain logic could be useful in other applications. Further thinking down the road, it might make senes to extract out persistence to a shared application in an SOA environment. I figure putting the architectural boundaries now should afford making this refactoring much easier down the road (i.e. swapping out a repository from a database call to a web service call).
Tim Riley
@timriley
Dec 11 2016 23:14
right, OK. So if you’re providing a container and it’s a Dry::System::Container, then I’d expect a boot sequence for the container to be something like this when running in a production-like environment:
# Require the container
require_relative "main/container"

# Load files with manually registered dependencies
Main::Container.require "component/container/persistence"

# Finalize the container, which does auto-registration if you've configured it
Main::Container.finalize!
That way your container reaches a reliable state right up front and is frozen from there
the lazy loading feature of non-finalized dry-system containers is really meant for isolated unit testing, where you want it to be fast, so you load up an empty container (don’t finalize) and then rely on the lazy loading to get just the right combination of components you need for your unit test to run
Tim Riley
@timriley
Dec 11 2016 23:19
If you don’t want to require your consumers to undergo a “boot” process like this, then maybe dry-system is not what you want. (But then you’d do without features like auto-registration etc., but I’m not sure how important they are for you in this thing you’re building)
Andrew Thauer
@andrewthauer
Dec 11 2016 23:31
I think, I’d be fine having the consumer go through a boot process. The initial consumer will be a rails application. So my initial thought is to have config/initializer that does the boot process your suggesting (where the middle line loading the rails defined repositories, etc.). I’ll have to try out a couple things to see what works. Thanks for you help!