Skip to content

Commit

Permalink
Allow env variable prefix and setting namespace to be configured in s…
Browse files Browse the repository at this point in the history
…ettings provider
  • Loading branch information
jeremiahrose committed Nov 2, 2022
1 parent b521726 commit 245732a
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 4 deletions.
59 changes: 58 additions & 1 deletion docsite/source/settings.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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://..."
Expand Down Expand Up @@ -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://..."
```
6 changes: 5 additions & 1 deletion lib/dry/system/provider_sources/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions lib/dry/system/provider_sources/settings/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
97 changes: 97 additions & 0 deletions spec/integration/settings_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 245732a

Please sign in to comment.