diff --git a/lib/dry/system/provider_registrar.rb b/lib/dry/system/provider_registrar.rb index f0ae4bb9..4ab6d178 100644 --- a/lib/dry/system/provider_registrar.rb +++ b/lib/dry/system/provider_registrar.rb @@ -13,7 +13,8 @@ module System # provider registrar is responsible for loading provider files and exposing an API for # running the provider lifecycle steps. # - # @api private + # @api public + # @since 1.1.0 class ProviderRegistrar # @api private attr_reader :providers @@ -21,6 +22,14 @@ class ProviderRegistrar # @api private attr_reader :container + # Returns the container exposed to providers as `target_container`. + # + # @return [Dry::System::Container] + # + # @api public + # @since 1.1.0 + alias_method :target_container, :container + # @api private def initialize(container) @providers = {} @@ -202,7 +211,7 @@ def build_provider(name, namespace:, source: nil, &block) Provider.new( name: name, namespace: namespace, - target_container: container, + target_container: target_container, source_class: source_class ) end @@ -213,7 +222,7 @@ def build_provider_from_source(name, source:, group:, namespace:, &block) Provider.new( name: name, namespace: namespace, - target_container: container, + target_container: target_container, source_class: source_class, &block ) diff --git a/spec/integration/container/providers/custom_provider_registrar_spec.rb b/spec/integration/container/providers/custom_provider_registrar_spec.rb new file mode 100644 index 00000000..f39c2cf1 --- /dev/null +++ b/spec/integration/container/providers/custom_provider_registrar_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +RSpec.describe "Providers / Custom provider registrar" do + specify "Customizing the target_container for providers" do + # Create a provider registrar that exposes a container _wrapper_ (i.e. something resembling a + # Hanami slice) as the target_container. + provider_registrar = Class.new(Dry::System::ProviderRegistrar) do + def self.for_wrapper(wrapper) + Class.new(self) do + define_singleton_method(:new) do |container| + super(container, wrapper) + end + end + end + + attr_reader :wrapper + + def initialize(container, wrapper) + super(container) + @wrapper = wrapper + end + + def target_container + wrapper + end + end + + # Create the wrapper, which has an internal Dry::System::Container (configured with our custom + # provider_registrar) that it then delegates to. + container_wrapper = Class.new do + define_singleton_method(:container) do + @container ||= Class.new(Dry::System::Container).tap do |container| + container.config.provider_registrar = provider_registrar.for_wrapper(self) + end + end + + def self.register_provider(...) + container.register_provider(...) + end + + def self.start(...) + container.start(...) + end + end + + # Create a provider to expose its given `target` so we can make expecations about it + exposed_target = nil + container_wrapper.register_provider(:my_provider) do + start do + exposed_target = target + end + end + container_wrapper.start(:my_provider) + + expect(exposed_target).to be container_wrapper + end +end