From ae13fbdd7267d27a6d001a06461fc17e41b7bbd6 Mon Sep 17 00:00:00 2001 From: Tim Riley Date: Fri, 17 May 2024 17:17:44 +1000 Subject: [PATCH] Allow provider sources to specify provider options The one provider option currently supported is `namespace:`. Allowing a provider source to specify e.g. `provider_options: {namespace: true}` is useful if the source will register multiple components at different keys. In such a case, the provider will typically want to have a `namespace: true` provider option. Specifying this option at the time of provider source registration then makes for a simpler and less error-prone experience when the user eventually registers a provider using that source. The user now no longer needs to remember to provide their own explicit `namespace: true`. --- lib/dry/system.rb | 6 +-- lib/dry/system/provider_registrar.rb | 22 ++++++---- lib/dry/system/provider_source_registry.rb | 27 +++++++++---- .../provider_sources/provider_options_spec.rb | 40 +++++++++++++++++++ 4 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 spec/integration/container/providers/provider_sources/provider_options_spec.rb diff --git a/lib/dry/system.rb b/lib/dry/system.rb index 1707f627..50671e27 100644 --- a/lib/dry/system.rb +++ b/lib/dry/system.rb @@ -30,15 +30,15 @@ def self.register_provider_sources(path) # Registers a provider source, which can be used as the basis for other providers # # @api public - def self.register_provider_source(name, group:, source: nil, &block) + def self.register_provider_source(name, group:, source: nil, provider_options: {}, &block) if source && block raise ArgumentError, "You must supply only a `source:` option or a block, not both" end if source - provider_sources.register(name: name, group: group, source: source) + provider_sources.register(name: name, group: group, source: source, provider_options: provider_options) else - provider_sources.register_from_block(name: name, group: group, &block) + provider_sources.register_from_block(name: name, group: group, provider_options: provider_options, &block) end end diff --git a/lib/dry/system/provider_registrar.rb b/lib/dry/system/provider_registrar.rb index 4ab6d178..2211affc 100644 --- a/lib/dry/system/provider_registrar.rb +++ b/lib/dry/system/provider_registrar.rb @@ -46,7 +46,7 @@ def freeze # @see Container.register_provider # @api private - def register_provider(name, namespace: nil, from: nil, source: nil, if: true, &block) + def register_provider(name, from: nil, source: nil, if: true, **provider_options, &block) raise ProviderAlreadyRegisteredError, name if providers.key?(name) if from && source.is_a?(Class) @@ -63,13 +63,18 @@ def register_provider(name, namespace: nil, from: nil, source: nil, if: true, &b if from build_provider_from_source( name, - namespace: namespace, source: source || name, group: from, + options: provider_options, &block ) else - build_provider(name, namespace: namespace, source: source, &block) + build_provider( + name, + source: source, + options: provider_options, + &block + ) end providers[provider.name] = provider @@ -205,7 +210,7 @@ def provider_paths } end - def build_provider(name, namespace:, source: nil, &block) + def build_provider(name, source: nil, options:, &block) source_class = source || Provider::Source.for(name: name, &block) Provider.new( @@ -216,14 +221,15 @@ def build_provider(name, namespace:, source: nil, &block) ) end - def build_provider_from_source(name, source:, group:, namespace:, &block) - source_class = System.provider_sources.resolve(name: source, group: group) + def build_provider_from_source(name, source:, group:, options:, &block) + source = System.provider_sources.resolve(name: source, group: group) Provider.new( + **source.provider_options, + **options, name: name, - namespace: namespace, target_container: target_container, - source_class: source_class, + source_class: source.source, &block ) end diff --git a/lib/dry/system/provider_source_registry.rb b/lib/dry/system/provider_source_registry.rb index e75899b2..b78b3e67 100644 --- a/lib/dry/system/provider_source_registry.rb +++ b/lib/dry/system/provider_source_registry.rb @@ -6,6 +6,17 @@ module Dry module System # @api private class ProviderSourceRegistry + # @api private + class Registration + attr_reader :source + attr_reader :provider_options + + def initialize(source:, provider_options:) + @source = source + @provider_options = provider_options + end + end + attr_reader :sources def initialize @@ -18,19 +29,19 @@ def load_sources(path) end end - def register(name:, group:, source:) - sources[key(name, group)] = source + def register(name:, group:, source:, provider_options:) + sources[key(name, group)] = Registration.new( + source: source, + provider_options: provider_options + ) end - def register_from_block(name:, group:, &block) + def register_from_block(name:, group:, provider_options:, &block) register( name: name, group: group, - source: Provider::Source.for( - name: name, - group: group, - &block - ) + source: Provider::Source.for(name: name, group: group, &block), + provider_options: provider_options, ) end diff --git a/spec/integration/container/providers/provider_sources/provider_options_spec.rb b/spec/integration/container/providers/provider_sources/provider_options_spec.rb new file mode 100644 index 00000000..fcc0705b --- /dev/null +++ b/spec/integration/container/providers/provider_sources/provider_options_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +RSpec.describe "Providers / Provider sources / Provider options" do + let(:container) { Class.new(Dry::System::Container) } + + specify "provider_options registered with provider sources are used when creating corresponding providers" do + Dry::System.register_provider_source(:db, group: :my_framework, provider_options: {namespace: true}) do + start do + register "config", "db_config_here" + end + end + + # Note no `namespace:` option when registering provider + container.register_provider :db, from: :my_framework + + # Also works when using a different name for the provider + container.register_provider :my_db, from: :my_framework, source: :db + + container.start :db + container.start :my_db + + expect(container["db.config"]).to eq "db_config_here" + expect(container["my_db.config"]).to eq "db_config_here" + end + + specify "provider source provider_options can be overridden" do + Dry::System.register_provider_source(:db, group: :my_framework, provider_options: {namespace: true}) do + start do + register "config", "db_config_here" + end + end + + container.register_provider :db, from: :my_framework, namespace: "custom_db" + + container.start :db + + expect(container["custom_db.config"]).to eq "db_config_here" + + end +end