diff --git a/lib/dry/system/provider.rb b/lib/dry/system/provider.rb index 6a5d0c0a..5bfc335a 100644 --- a/lib/dry/system/provider.rb +++ b/lib/dry/system/provider.rb @@ -127,7 +127,8 @@ class Provider attr_reader :source # @api private - def initialize(name:, namespace: nil, target_container:, source_class:, &block) # rubocop:disable Style/KeywordParametersOrder + # rubocop:disable Layout/LineLength, Style/KeywordParametersOrder + def initialize(name:, namespace: nil, target_container:, source_class:, source_options: {}, &block) @name = name @namespace = namespace @target_container = target_container @@ -137,11 +138,13 @@ def initialize(name:, namespace: nil, target_container:, source_class:, &block) @step_running = nil @source = source_class.new( + **source_options, provider_container: provider_container, target_container: target_container, &block ) end + # rubocop:enable Layout/LineLength, Style/KeywordParametersOrder # Runs the `prepare` lifecycle step. # diff --git a/lib/dry/system/provider/source.rb b/lib/dry/system/provider/source.rb index 42ad58b1..796d3b33 100644 --- a/lib/dry/system/provider/source.rb +++ b/lib/dry/system/provider/source.rb @@ -37,10 +37,20 @@ class << self # @see Dry::System::Provider::SourceDSL # # @api private - def for(name:, group: nil, &block) - Class.new(self) { |klass| + def for(name:, group: nil, superclass: nil, &block) + superclass ||= self + + Class.new(superclass) { |klass| klass.source_name name klass.source_group group + + name_with_group = group ? "#{group}->#{name}" : name + klass.instance_eval <<~RUBY, __FILE__, __LINE__ + 1 + def name + "#{superclass.name}[#{name_with_group}]" + end + RUBY + SourceDSL.evaluate(klass, &block) if block } end @@ -58,14 +68,6 @@ def inherited(subclass) end end - # @api private - def name - source_str = source_name - source_str = "#{source_group}->#{source_str}" if source_group - - "Dry::System::Provider::Source[#{source_str}]" - end - # @api private def to_s "#<#{name}>" diff --git a/lib/dry/system/provider_registrar.rb b/lib/dry/system/provider_registrar.rb index 6f078735..8086c9f5 100644 --- a/lib/dry/system/provider_registrar.rb +++ b/lib/dry/system/provider_registrar.rb @@ -136,6 +136,14 @@ def provider_files }.first end + # Extension point for subclasses + # @api private + def provider_source_class = Dry::System::Provider::Source + + # Extension point for subclasses + # @api private + def provider_source_options = {} + # @api private def finalize! provider_files.each do |path| @@ -196,13 +204,18 @@ def provider_paths end def build_provider(name, options:, source: nil, &block) - source_class = source || Provider::Source.for(name: name, &block) + source_class = source || Provider::Source.for( + name: name, + superclass: provider_source_class, + &block + ) Provider.new( **options, name: name, target_container: target_container, - source_class: source_class + source_class: source_class, + source_options: provider_source_options ) end @@ -215,6 +228,7 @@ def build_provider_from_source(name, source:, group:, options:, &block) name: name, target_container: target_container, source_class: provider_source.source, + source_options: provider_source_options, &block ) end diff --git a/spec/integration/container/providers/custom_provider_superclass_spec.rb b/spec/integration/container/providers/custom_provider_superclass_spec.rb new file mode 100644 index 00000000..7bd723d9 --- /dev/null +++ b/spec/integration/container/providers/custom_provider_superclass_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +RSpec.describe "Providers / Custom provider superclass" do + let!(:custom_superclass) do + module Test + class CustomSource < Dry::System::Provider::Source + attr_reader :custom_setting + + def initialize(custom_setting:, **options, &block) + super(**options, &block) + @custom_setting = custom_setting + end + end + end + + Test::CustomSource + end + + let!(:custom_registrar) do + module Test + class CustomRegistrar < Dry::System::ProviderRegistrar + def provider_source_class = Test::CustomSource + def provider_source_options = {custom_setting: "hello"} + end + end + + Test::CustomRegistrar + end + + subject(:system) do + module Test + class Container < Dry::System::Container + configure do |config| + config.root = SPEC_ROOT.join("fixtures/app").realpath + config.provider_registrar = Test::CustomRegistrar + end + end + end + + Test::Container + end + + it "overrides the default Provider Source base class" do + system.register_provider(:test) {} + + provider_source = system.providers[:test].source + + expect(provider_source.class).to be < custom_superclass + expect(provider_source.class.name).to eq "Test::CustomSource[test]" + expect(provider_source.custom_setting).to eq "hello" + end +end