diff --git a/docsite/source/settings.html.md b/docsite/source/settings.html.md index e5c69324..b42425d2 100644 --- a/docsite/source/settings.html.md +++ b/docsite/source/settings.html.md @@ -19,6 +19,10 @@ Application.register_provider(:settings, from: :dry_system) do require "your/types/module" end + configure do |config| + config.prefix = 'SOME_PREFIX_' + end + settings do setting :database_url, constructor: Types::String.constrained(filled: true) @@ -29,7 +33,7 @@ Application.register_provider(:settings, from: :dry_system) do end ``` -Your provider will then map `ENV` variables to a struct object giving access to your settings as their own methods, which you can use throughout your application: +An optional prefix can be specified with the `config.prefix` setting inside a `configure` block. Your provider will then map `ENV` variables with the given prefix to a struct object giving access to your settings as their own methods, which you can use throughout your application: ```ruby Application[:settings].database_url # => "postgres://..." @@ -65,3 +69,56 @@ Or as an injected dependency in your classes: end end ``` + +## Multiple Settings Providers + +In some situations you may wish to have multiple settings providers registered to different namespaces, e.g `config.database` and `config.api`. This can be achieved using the `register_as` configuration option: + +```ruby +# system/providers/database_settings.rb: + +require "dry/system/provider_sources" + +Application.register_provider(:database_settings, from: :dry_system, source: :settings) do + before :prepare do + require "your/types/module" + end + + configure do |config| + config.register_as = 'config.database' + end + + settings do + setting :url, constructor: Types::String.constrained(filled: true) + end +end +``` + +```ruby +# system/providers/api_settings.rb: + +require "dry/system/provider_sources" + +Application.register_provider(:api_settings, from: :dry_system, source: :settings) do + before :prepare do + require "your/types/module" + end + + configure do |config| + config.register_as = 'config.api' + end + + settings do + setting :base_url, constructor: Types::String.constrained(filled: true) + end +end +``` + +The individual settings namespaces can then be accessed from the container seperately: + +```ruby +Application.start(:database_settings) +Application.start(:api_settings) +Application['config.database'].url # => "postgres://..." +Application['config.api'].base_url # => "https://..." +``` diff --git a/lib/dry/system/provider_sources/settings.rb b/lib/dry/system/provider_sources/settings.rb index 21d6a886..809c8b5c 100644 --- a/lib/dry/system/provider_sources/settings.rb +++ b/lib/dry/system/provider_sources/settings.rb @@ -6,13 +6,17 @@ module ProviderSources module Settings class Source < Dry::System::Provider::Source setting :store + setting :register_as, default: :settings + setting :prefix, default: "" def prepare require "dry/system/provider_sources/settings/config" end def start - register(:settings, settings.load(root: target.root, env: target.config.env)) + register(config.register_as, + settings.load(root: target.root, env: target.config.env, + prefix: config.prefix)) end def settings(&block) diff --git a/lib/dry/system/provider_sources/settings/config.rb b/lib/dry/system/provider_sources/settings/config.rb index 2af16e37..de4c9efe 100644 --- a/lib/dry/system/provider_sources/settings/config.rb +++ b/lib/dry/system/provider_sources/settings/config.rb @@ -27,14 +27,14 @@ def setting_errors(errors) # @api private class Config # @api private - def self.load(root:, env:, loader: Loader) + def self.load(root:, env:, prefix: "", loader: Loader) loader = loader.new(root: root, env: env) new.tap do |settings_obj| errors = {} settings.to_a.each do |setting| - value = loader[setting.name.to_s.upcase] + value = loader[prefix + setting.name.to_s.upcase] begin if value diff --git a/spec/integration/settings_component_spec.rb b/spec/integration/settings_component_spec.rb index 12c2592c..2ffce20f 100644 --- a/spec/integration/settings_component_spec.rb +++ b/spec/integration/settings_component_spec.rb @@ -168,4 +168,101 @@ end end end + + context "With a custom prefix" do + subject(:system) do + Class.new(Dry::System::Container) do + setting :env + + configure do |config| + config.root = SPEC_ROOT.join("fixtures").join("settings_test") + config.env = :test + end + + register_provider(:settings, from: :dry_system) do + configure do |config| + config.prefix = "CUSTOM_PREFIX_" + end + + before(:prepare) do + target_container.require_from_root "types" + end + + settings do + setting :string_value, constructor: SettingsTest::Types::String + end + end + end + end + + before do + ENV["CUSTOM_PREFIX_STRING_VALUE"] = "foo" + end + + after do + ENV.delete("CUSTOM_PREFIX_STRING_VALUE") + end + + it "sets up system settings component via ENV and .env" do + expect(settings.string_value).to eql("foo") + end + end + + context "With multiple settings providers" do + subject(:system) do + Class.new(Dry::System::Container) do + setting :env + + configure do |config| + config.root = SPEC_ROOT.join("fixtures").join("settings_test") + config.env = :test + end + + register_provider(:settings_provider1, from: :dry_system, source: :settings) do + configure do |config| + config.register_as = "database_settings" + end + + before(:prepare) do + target_container.require_from_root "types" + end + + settings do + setting :example_port, constructor: SettingsTest::Types::Coercible::Integer + end + end + + register_provider(:settings_provider2, from: :dry_system, source: :settings) do + configure do |config| + config.register_as = "api_settings" + end + + before(:prepare) do + target_container.require_from_root "types" + end + + settings do + setting :example_token, constructor: SettingsTest::Types::String + end + end + end + end + + before do + ENV["EXAMPLE_PORT"] = "19" + ENV["EXAMPLE_TOKEN"] = "abc123" + system.start(:settings_provider1) + system.start(:settings_provider2) + end + + after do + ENV.delete("EXAMPLE_PORT") + ENV.delete("EXAMPLE_TOKENT") + end + + it "sets up system settings component via ENV and .env" do + expect(system["database_settings"].example_port).to eql(19) + expect(system["api_settings"].example_token).to eql("abc123") + end + end end