Skip to content

Commit

Permalink
Allow provider sources to specify provider options (#271)
Browse files Browse the repository at this point in the history
When registering a provider source, you can now provider a `provider_options:` containing a hash of default options for providers to be registered using that source.

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`.
  • Loading branch information
timriley authored May 17, 2024
1 parent f553c22 commit c510ede
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 20 deletions.
16 changes: 13 additions & 3 deletions lib/dry/system.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,25 @@ 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

Expand Down
24 changes: 15 additions & 9 deletions lib/dry/system/provider_registrar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -205,25 +210,26 @@ def provider_paths
}
end

def build_provider(name, namespace:, source: nil, &block)
def build_provider(name, options:, source: nil, &block)
source_class = source || Provider::Source.for(name: name, &block)

Provider.new(
**options,
name: name,
namespace: namespace,
target_container: target_container,
source_class: source_class
)
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)
provider_source = System.provider_sources.resolve(name: source, group: group)

Provider.new(
**provider_source.provider_options,
**options,
name: name,
namespace: namespace,
target_container: target_container,
source_class: source_class,
source_class: provider_source.source,
&block
)
end
Expand Down
27 changes: 19 additions & 8 deletions lib/dry/system/provider_source_registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 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

0 comments on commit c510ede

Please sign in to comment.