Skip to content

Commit 866f68c

Browse files
committed
Allow env variable prefix and setting namespace to be configured in settings provider
1 parent 1f6f1a5 commit 866f68c

File tree

4 files changed

+162
-4
lines changed

4 files changed

+162
-4
lines changed

docsite/source/settings.html.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ Application.register_provider(:settings, from: :dry_system) do
1919
require "your/types/module"
2020
end
2121

22+
configure do |config|
23+
config.prefix = 'SOME_PREFIX_'
24+
end
25+
2226
settings do
2327
setting :database_url, constructor: Types::String.constrained(filled: true)
2428

@@ -29,7 +33,7 @@ Application.register_provider(:settings, from: :dry_system) do
2933
end
3034
```
3135

32-
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:
36+
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:
3337

3438
```ruby
3539
Application[:settings].database_url # => "postgres://..."
@@ -65,3 +69,56 @@ Or as an injected dependency in your classes:
6569
end
6670
end
6771
```
72+
73+
## Multiple Settings Providers
74+
75+
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:
76+
77+
```ruby
78+
# system/providers/database_settings.rb:
79+
80+
require "dry/system/provider_sources"
81+
82+
Application.register_provider(:database_settings, from: :dry_system, source: :settings) do
83+
before :prepare do
84+
require "your/types/module"
85+
end
86+
87+
configure do |config|
88+
config.register_as = 'config.database'
89+
end
90+
91+
settings do
92+
setting :url, constructor: Types::String.constrained(filled: true)
93+
end
94+
end
95+
```
96+
97+
```ruby
98+
# system/providers/api_settings.rb:
99+
100+
require "dry/system/provider_sources"
101+
102+
Application.register_provider(:api_settings, from: :dry_system, source: :settings) do
103+
before :prepare do
104+
require "your/types/module"
105+
end
106+
107+
configure do |config|
108+
config.register_as = 'config.api'
109+
end
110+
111+
settings do
112+
setting :base_url, constructor: Types::String.constrained(filled: true)
113+
end
114+
end
115+
```
116+
117+
The individual settings namespaces can then be accessed from the container seperately:
118+
119+
```ruby
120+
Application.start(:database_settings)
121+
Application.start(:api_settings)
122+
Application['config.database'].url # => "postgres://..."
123+
Application['config.api'].base_url # => "https://..."
124+
```

lib/dry/system/provider_sources/settings.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ module ProviderSources
66
module Settings
77
class Source < Dry::System::Provider::Source
88
setting :store
9+
setting :register_as, default: :settings
10+
setting :prefix, default: ""
911

1012
def prepare
1113
require "dry/system/provider_sources/settings/config"
1214
end
1315

1416
def start
15-
register(:settings, settings.load(root: target.root, env: target.config.env))
17+
register(config.register_as,
18+
settings.load(root: target.root, env: target.config.env,
19+
prefix: config.prefix))
1620
end
1721

1822
def settings(&block)

lib/dry/system/provider_sources/settings/config.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ def setting_errors(errors)
3131
# @api private
3232
class Config
3333
# @api private
34-
def self.load(root:, env:, loader: Loader)
34+
def self.load(root:, env:, prefix: "", loader: Loader)
3535
loader = loader.new(root: root, env: env)
3636

3737
new.tap do |settings_obj|
3838
errors = {}
3939

4040
settings.to_a.each do |setting_name|
41-
value = loader[setting_name.to_s.upcase]
41+
value = loader[prefix + setting_name.to_s.upcase]
4242

4343
begin
4444
settings_obj.config.public_send(:"#{setting_name}=", value) if value

spec/integration/settings_component_spec.rb

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,101 @@
130130
end
131131
end
132132
end
133+
134+
context "With a custom prefix" do
135+
subject(:system) do
136+
Class.new(Dry::System::Container) do
137+
setting :env
138+
139+
configure do |config|
140+
config.root = SPEC_ROOT.join("fixtures").join("settings_test")
141+
config.env = :test
142+
end
143+
144+
register_provider(:settings, from: :dry_system) do
145+
configure do |config|
146+
config.prefix = "CUSTOM_PREFIX_"
147+
end
148+
149+
before(:prepare) do
150+
target_container.require_from_root "types"
151+
end
152+
153+
settings do
154+
setting :string_value, constructor: SettingsTest::Types::String
155+
end
156+
end
157+
end
158+
end
159+
160+
before do
161+
ENV["CUSTOM_PREFIX_STRING_VALUE"] = "foo"
162+
end
163+
164+
after do
165+
ENV.delete("CUSTOM_PREFIX_STRING_VALUE")
166+
end
167+
168+
it "sets up system settings component via ENV and .env" do
169+
expect(settings.string_value).to eql("foo")
170+
end
171+
end
172+
173+
context "With multiple settings providers" do
174+
subject(:system) do
175+
Class.new(Dry::System::Container) do
176+
setting :env
177+
178+
configure do |config|
179+
config.root = SPEC_ROOT.join("fixtures").join("settings_test")
180+
config.env = :test
181+
end
182+
183+
register_provider(:settings_provider1, from: :dry_system, source: :settings) do
184+
configure do |config|
185+
config.register_as = "database_settings"
186+
end
187+
188+
before(:prepare) do
189+
target_container.require_from_root "types"
190+
end
191+
192+
settings do
193+
setting :example_port, constructor: SettingsTest::Types::Coercible::Integer
194+
end
195+
end
196+
197+
register_provider(:settings_provider2, from: :dry_system, source: :settings) do
198+
configure do |config|
199+
config.register_as = "api_settings"
200+
end
201+
202+
before(:prepare) do
203+
target_container.require_from_root "types"
204+
end
205+
206+
settings do
207+
setting :example_token, constructor: SettingsTest::Types::String
208+
end
209+
end
210+
end
211+
end
212+
213+
before do
214+
ENV["EXAMPLE_PORT"] = "19"
215+
ENV["EXAMPLE_TOKEN"] = "abc123"
216+
system.start(:settings_provider1)
217+
system.start(:settings_provider2)
218+
end
219+
220+
after do
221+
ENV.delete("EXAMPLE_PORT")
222+
ENV.delete("EXAMPLE_TOKENT")
223+
end
224+
225+
it "sets up system settings component via ENV and .env" do
226+
expect(system["database_settings"].example_port).to eql(19)
227+
expect(system["api_settings"].example_token).to eql("abc123")
228+
end
229+
end
133230
end

0 commit comments

Comments
 (0)