runger_config 3.0.1 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +44 -35
- data/README.md +83 -83
- data/lib/generators/runger/app_config/app_config_generator.rb +13 -0
- data/lib/generators/runger/config/config_generator.rb +49 -0
- data/lib/generators/runger/install/install_generator.rb +45 -0
- data/lib/generators/{anyway → runger}/install/templates/application_config.rb.tt +1 -1
- data/lib/{anyway → runger}/auto_cast.rb +4 -4
- data/lib/{anyway → runger}/config.rb +124 -104
- data/lib/runger/dynamic_config.rb +29 -0
- data/lib/runger/ejson_parser.rb +40 -0
- data/lib/runger/env.rb +70 -0
- data/lib/runger/ext/deep_dup.rb +45 -0
- data/lib/runger/ext/deep_freeze.rb +40 -0
- data/lib/runger/ext/flatten_names.rb +33 -0
- data/lib/runger/ext/hash.rb +37 -0
- data/lib/runger/ext/string_constantize.rb +21 -0
- data/lib/runger/loaders/base.rb +17 -0
- data/lib/runger/loaders/doppler.rb +57 -0
- data/lib/runger/loaders/ejson.rb +91 -0
- data/lib/runger/loaders/env.rb +12 -0
- data/lib/runger/loaders/yaml.rb +86 -0
- data/lib/runger/loaders.rb +75 -0
- data/lib/runger/option_parser_builder.rb +27 -0
- data/lib/{anyway → runger}/optparse_config.rb +15 -14
- data/lib/runger/rails/autoload.rb +38 -0
- data/lib/runger/rails/config.rb +19 -0
- data/lib/runger/rails/loaders/credentials.rb +58 -0
- data/lib/runger/rails/loaders/secrets.rb +31 -0
- data/lib/runger/rails/loaders/yaml.rb +4 -0
- data/lib/runger/rails/loaders.rb +5 -0
- data/lib/runger/rails/settings.rb +83 -0
- data/lib/runger/rails.rb +22 -0
- data/lib/{anyway → runger}/railtie.rb +9 -8
- data/lib/{anyway → runger}/rbs.rb +30 -30
- data/lib/runger/settings.rb +109 -0
- data/lib/runger/testing/helpers.rb +34 -0
- data/lib/{anyway → runger}/testing.rb +3 -3
- data/lib/runger/tracing.rb +195 -0
- data/lib/{anyway → runger}/type_casting.rb +19 -14
- data/lib/{anyway → runger}/utils/deep_merge.rb +2 -2
- data/lib/runger/utils/which.rb +16 -0
- data/lib/runger/version.rb +5 -0
- data/lib/{anyway.rb → runger.rb} +1 -1
- data/lib/runger_config.rb +56 -0
- data/sig/{anyway_config.rbs → runger_config.rbs} +1 -1
- metadata +68 -68
- data/lib/anyway/dynamic_config.rb +0 -31
- data/lib/anyway/ejson_parser.rb +0 -40
- data/lib/anyway/env.rb +0 -72
- data/lib/anyway/ext/deep_dup.rb +0 -48
- data/lib/anyway/ext/deep_freeze.rb +0 -44
- data/lib/anyway/ext/flatten_names.rb +0 -37
- data/lib/anyway/ext/hash.rb +0 -40
- data/lib/anyway/ext/string_constantize.rb +0 -24
- data/lib/anyway/loaders/base.rb +0 -21
- data/lib/anyway/loaders/doppler.rb +0 -61
- data/lib/anyway/loaders/ejson.rb +0 -89
- data/lib/anyway/loaders/env.rb +0 -16
- data/lib/anyway/loaders/yaml.rb +0 -83
- data/lib/anyway/loaders.rb +0 -77
- data/lib/anyway/option_parser_builder.rb +0 -29
- data/lib/anyway/rails/autoload.rb +0 -40
- data/lib/anyway/rails/config.rb +0 -23
- data/lib/anyway/rails/loaders/credentials.rb +0 -62
- data/lib/anyway/rails/loaders/secrets.rb +0 -35
- data/lib/anyway/rails/loaders/yaml.rb +0 -9
- data/lib/anyway/rails/loaders.rb +0 -5
- data/lib/anyway/rails/settings.rb +0 -83
- data/lib/anyway/rails.rb +0 -24
- data/lib/anyway/settings.rb +0 -111
- data/lib/anyway/testing/helpers.rb +0 -36
- data/lib/anyway/tracing.rb +0 -188
- data/lib/anyway/utils/which.rb +0 -18
- data/lib/anyway/version.rb +0 -5
- data/lib/anyway_config.rb +0 -49
- data/lib/generators/anyway/app_config/app_config_generator.rb +0 -17
- data/lib/generators/anyway/config/config_generator.rb +0 -46
- data/lib/generators/anyway/install/install_generator.rb +0 -47
- /data/lib/generators/{anyway → runger}/app_config/USAGE +0 -0
- /data/lib/generators/{anyway → runger}/config/USAGE +0 -0
- /data/lib/generators/{anyway → runger}/config/templates/config.rb.tt +0 -0
- /data/lib/generators/{anyway → runger}/config/templates/config.yml.tt +0 -0
- /data/lib/generators/{anyway → runger}/install/USAGE +0 -0
data/README.md
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
[![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/
|
1
|
+
[![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/runger-config-options-parse.html#task)
|
2
2
|
[![Gem Version](https://badge.fury.io/rb/runger_config.svg)](https://rubygems.org/gems/runger_config) [![Build](https://github.com/davidrunger/runger_config/workflows/Build/badge.svg)](https://github.com/davidrunger/runger_config/actions)
|
3
3
|
[![JRuby Build](https://github.com/davidrunger/runger_config/workflows/JRuby%20Build/badge.svg)](https://github.com/davidrunger/runger_config/actions)
|
4
4
|
[![TruffleRuby Build](https://github.com/davidrunger/runger_config/workflows/TruffleRuby%20Build/badge.svg)](https://github.com/davidrunger/runger_config/actions)
|
5
5
|
|
6
|
-
#
|
6
|
+
# Runger Config
|
7
7
|
|
8
8
|
> One configuration to rule all data sources
|
9
9
|
|
10
|
-
|
10
|
+
Runger Config is a configuration library for Ruby gems and applications.
|
11
11
|
|
12
|
-
As a library author, you can benefit from using
|
12
|
+
As a library author, you can benefit from using Runger Config by providing a better UX for your end-users:
|
13
13
|
|
14
14
|
- **Zero-code configuration** — no more boilerplate initializers.
|
15
15
|
- **Per-environment and local** settings support out-of-the-box.
|
16
16
|
|
17
|
-
For application developers,
|
17
|
+
For application developers, Runger Config could be useful to:
|
18
18
|
|
19
19
|
- **Keep configuration organized** and use _named configs_ instead of bloated `.env`/`settings.yml`/whatever.
|
20
20
|
- **Free code of ENV/credentials/secrets dependency** and use configuration classes instead—your code should not rely on configuration data sources.
|
@@ -27,7 +27,7 @@ For version 1.x see the [1-4-stable branch](https://github.com/davidrunger/runge
|
|
27
27
|
|
28
28
|
## Links
|
29
29
|
|
30
|
-
- [
|
30
|
+
- [Runger Config: Keep your Ruby configuration sane](https://evilmartians.com/chronicles/runger-config-keep-your-ruby-configuration-sane?utm_source=runger_config)
|
31
31
|
|
32
32
|
## Table of contents
|
33
33
|
|
@@ -57,11 +57,11 @@ For version 1.x see the [1-4-stable branch](https://github.com/davidrunger/runge
|
|
57
57
|
|
58
58
|
## Main concepts
|
59
59
|
|
60
|
-
|
60
|
+
Runger Config abstractize the configuration layer by introducing **configuration classes** which describe available parameters and their defaults. For [example](https://github.com/palkan/influxer/blob/master/lib/influxer/config.rb):
|
61
61
|
|
62
62
|
```ruby
|
63
63
|
module Influxer
|
64
|
-
class Config <
|
64
|
+
class Config < Runger::Config
|
65
65
|
attr_config(
|
66
66
|
host: "localhost",
|
67
67
|
username: "root",
|
@@ -75,7 +75,7 @@ Using Ruby classes to represent configuration allows you to add helper methods a
|
|
75
75
|
|
76
76
|
The `runger_config` gem takes care of loading parameters from **different sources** (YAML, credentials/secrets, environment variables, etc.). Internally, we use a _pipeline pattern_ and provide the [Loaders API](#data-loaders) to manage and [extend](#custom-loaders) its functionality.
|
77
77
|
|
78
|
-
Check out the libraries using
|
78
|
+
Check out the libraries using Runger Config for more examples:
|
79
79
|
|
80
80
|
- [Influxer](https://github.com/palkan/influxer)
|
81
81
|
- [AnyCable](https://github.com/anycable/anycable)
|
@@ -115,13 +115,13 @@ gem "runger_config", "~> 2.0"
|
|
115
115
|
Using configuration classes allows you to make configuration data a bit more than a bag of values:
|
116
116
|
you can define a schema for your configuration, provide defaults, add validations and additional helper methods.
|
117
117
|
|
118
|
-
|
118
|
+
Runger Config provides a base class to inherit from with a few DSL methods:
|
119
119
|
|
120
120
|
```ruby
|
121
121
|
require "runger_config"
|
122
122
|
|
123
123
|
module MyCoolGem
|
124
|
-
class Config <
|
124
|
+
class Config < Runger::Config
|
125
125
|
attr_config user: "root", password: "root", host: "localhost"
|
126
126
|
end
|
127
127
|
end
|
@@ -141,7 +141,7 @@ Then, create an instance of the config class and use it:
|
|
141
141
|
MyCoolGem::Config.new.user #=> "root"
|
142
142
|
```
|
143
143
|
|
144
|
-
**Bonus:**: if you define attributes with boolean default values (`false` or `true`),
|
144
|
+
**Bonus:**: if you define attributes with boolean default values (`false` or `true`), Runger Config would automatically add a corresponding predicate method. For example:
|
145
145
|
|
146
146
|
```ruby
|
147
147
|
attr_config :user, :password, debug: false
|
@@ -153,7 +153,7 @@ MyCoolGem::Config.new(debug: true).debug? #=> true
|
|
153
153
|
**NOTE**: since v2.0 accessors created by `attr_config` are not `attr_accessor`, i.e. they do not populate instance variables. If you used instance variables before to override readers, you must switch to using `super` or `values` store:
|
154
154
|
|
155
155
|
```ruby
|
156
|
-
class MyConfig <
|
156
|
+
class MyConfig < Runger::Config
|
157
157
|
attr_config :host, :port, :url, :meta
|
158
158
|
|
159
159
|
# override writer to handle type coercion
|
@@ -191,9 +191,9 @@ end
|
|
191
191
|
|
192
192
|
#### Config name
|
193
193
|
|
194
|
-
|
194
|
+
Runger Config relies on the notion of _config name_ to populate data.
|
195
195
|
|
196
|
-
By default,
|
196
|
+
By default, Runger Config uses the config class name to infer the config name using the following rules:
|
197
197
|
|
198
198
|
- if the class name has a form of `<Module>::Config` then use the module name (`SomeModule::Config => "somemodule"`)
|
199
199
|
- if the class name has a form of `<Something>Config` then use the class name prefix (`SomeConfig => "some"`)
|
@@ -204,7 +204,7 @@ You can also specify the config name explicitly (it's required in cases when you
|
|
204
204
|
|
205
205
|
```ruby
|
206
206
|
module MyCoolGem
|
207
|
-
class Config <
|
207
|
+
class Config < Runger::Config
|
208
208
|
config_name :cool
|
209
209
|
attr_config user: "root", password: "root", host: "localhost", options: {}
|
210
210
|
end
|
@@ -213,14 +213,14 @@ end
|
|
213
213
|
|
214
214
|
#### Customize env variable names prefix
|
215
215
|
|
216
|
-
By default,
|
216
|
+
By default, Runger Config uses upper-cased config name as a prefix for env variable names (e.g.
|
217
217
|
`config_name :my_app` will result to parsing `MY_APP_` prefix).
|
218
218
|
|
219
219
|
You can set env prefix explicitly:
|
220
220
|
|
221
221
|
```ruby
|
222
222
|
module MyCoolGem
|
223
|
-
class Config <
|
223
|
+
class Config < Runger::Config
|
224
224
|
config_name :cool_gem
|
225
225
|
env_prefix :really_cool # now variables, starting wih `REALLY_COOL_`, will be parsed
|
226
226
|
attr_config user: "root", password: "root", host: "localhost", options: {}
|
@@ -258,16 +258,16 @@ You can also fetch configuration without pre-defined schema:
|
|
258
258
|
# credentials.my_app, secrets.my_app (if using Rails), ENV["MY_APP_*"]
|
259
259
|
#
|
260
260
|
# Given MY_APP_VALUE=42
|
261
|
-
config =
|
261
|
+
config = Runger::Config.for(:my_app)
|
262
262
|
config["value"] #=> 42
|
263
263
|
|
264
264
|
# you can specify the config file path or env prefix
|
265
|
-
config =
|
265
|
+
config = Runger::Config.for(:my_app, config_path: "my_config.yml", env_prefix: "MYAPP")
|
266
266
|
```
|
267
267
|
|
268
268
|
This feature is similar to `Rails.application.config_for` but more powerful:
|
269
269
|
|
270
|
-
| Feature | Rails |
|
270
|
+
| Feature | Rails | Runger Config |
|
271
271
|
| ------------- |-------------:| -----:|
|
272
272
|
| Load data from `config/app.yml` | ✅ | ✅ |
|
273
273
|
| Load data from `secrets` | ❌ | ✅ |
|
@@ -286,26 +286,26 @@ This feature is similar to `Rails.application.config_for` but more powerful:
|
|
286
286
|
|
287
287
|
### Validation and callbacks
|
288
288
|
|
289
|
-
|
289
|
+
Runger Config provides basic ways of ensuring that the configuration is valid.
|
290
290
|
|
291
291
|
There is a built-in `required` class method to define the list of parameters that must be present in the
|
292
292
|
configuration after loading (where present means non-`nil` and non-empty for strings):
|
293
293
|
|
294
294
|
```ruby
|
295
|
-
class MyConfig <
|
295
|
+
class MyConfig < Runger::Config
|
296
296
|
attr_config :api_key, :api_secret, :debug
|
297
297
|
|
298
298
|
required :api_key, :api_secret
|
299
299
|
end
|
300
300
|
|
301
|
-
MyConfig.new(api_secret: "") #=> raises
|
301
|
+
MyConfig.new(api_secret: "") #=> raises Runger::Config::ValidationError
|
302
302
|
```
|
303
303
|
|
304
304
|
`Required` method supports additional `env` parameter which indicates necessity to run validations under specified
|
305
305
|
environments. `Env` parameter could be present in symbol, string, array or hash formats:
|
306
306
|
|
307
307
|
```ruby
|
308
|
-
class EnvConfig <
|
308
|
+
class EnvConfig < Runger::Config
|
309
309
|
required :password, env: "production"
|
310
310
|
required :maps_api_key, env: :production
|
311
311
|
required :smtp_host, env: %i[production staging]
|
@@ -316,13 +316,13 @@ class EnvConfig < Anyway::Config
|
|
316
316
|
end
|
317
317
|
```
|
318
318
|
|
319
|
-
If your current `
|
320
|
-
`
|
319
|
+
If your current `Runger::Settings.current_environment` is mismatch keys that specified
|
320
|
+
`Runger::Config::ValidationError` error will be raised.
|
321
321
|
|
322
322
|
If you need more complex validation or need to manipulate with config state right after it has been loaded, you can use _on load callbacks_ and `#raise_validation_error` method:
|
323
323
|
|
324
324
|
```ruby
|
325
|
-
class MyConfig <
|
325
|
+
class MyConfig < Runger::Config
|
326
326
|
attr_config :api_key, :api_secret, :mode
|
327
327
|
|
328
328
|
# on_load macro accepts symbol method names
|
@@ -348,7 +348,7 @@ end
|
|
348
348
|
**NOTE:** version 2.x supports Rails >= 5.0; for Rails 4.x use version 1.x of the gem.
|
349
349
|
|
350
350
|
We recommend going through [Data population](#data-population) and [Organizing configs](#organizing-configs) sections first,
|
351
|
-
and then use [Rails generators](#generators) to make your application
|
351
|
+
and then use [Rails generators](#generators) to make your application Runger Config-ready.
|
352
352
|
|
353
353
|
### Data population
|
354
354
|
|
@@ -368,7 +368,7 @@ development:
|
|
368
368
|
port: 3000
|
369
369
|
```
|
370
370
|
|
371
|
-
**NOTE:** You can override the environment name for configuration files via the `
|
371
|
+
**NOTE:** You can override the environment name for configuration files via the `RUNGER_ENV` environment variable or by setting it explicitly in the code: `Runger::Settings.current_environment = "some_other_env"`.
|
372
372
|
|
373
373
|
### Multi-env configuration
|
374
374
|
|
@@ -397,13 +397,13 @@ staging:
|
|
397
397
|
port: 3002 # This value will not be loaded at all
|
398
398
|
```
|
399
399
|
|
400
|
-
To provide default values you can use YAML anchors, but they do not deep-merge settings, so
|
400
|
+
To provide default values you can use YAML anchors, but they do not deep-merge settings, so Runger Config provides a way to define a special top-level key for default values like this:
|
401
401
|
|
402
402
|
```ruby
|
403
403
|
config.runger_config.default_environmental_key = "default"
|
404
404
|
```
|
405
405
|
|
406
|
-
After that,
|
406
|
+
After that, Runger Config will start reading settings under the `"default"` key and then merge environmental settings into them.
|
407
407
|
|
408
408
|
```yml
|
409
409
|
default:
|
@@ -443,7 +443,7 @@ development:
|
|
443
443
|
port: 4444
|
444
444
|
```
|
445
445
|
|
446
|
-
**NOTE:** If you want to use secrets with Rails 7.1 (still supported, but deprecated) you must add the corresponding loader manually: `
|
446
|
+
**NOTE:** If you want to use secrets with Rails 7.1 (still supported, but deprecated) you must add the corresponding loader manually: `Runger.loaders.insert_after :yml, :secrets, Runger::Rails::Loaders::Secrets`.
|
447
447
|
|
448
448
|
3) **Rails credentials**: `Rails.application.credentials.my_cool_gem` (if supported):
|
449
449
|
|
@@ -473,7 +473,7 @@ We have the following config to fetch the Heroku provided [metadata](https://dev
|
|
473
473
|
|
474
474
|
```ruby
|
475
475
|
# This data is provided by Heroku Dyno Metadadata add-on.
|
476
|
-
class HerokuConfig <
|
476
|
+
class HerokuConfig < Runger::Config
|
477
477
|
attr_config :app_id, :app_name,
|
478
478
|
:dyno_id, :release_version,
|
479
479
|
:slug_commit
|
@@ -518,28 +518,28 @@ end
|
|
518
518
|
|
519
519
|
### Generators
|
520
520
|
|
521
|
-
|
521
|
+
Runger Config provides Rails generators to create new config classes:
|
522
522
|
|
523
|
-
- `rails g
|
523
|
+
- `rails g runger:install`—creates an `ApplicationConfig` class (the base class for all config classes) and updates `.gitignore`
|
524
524
|
|
525
525
|
You can specify the static configs path via the `--configs-path` option:
|
526
526
|
|
527
527
|
```sh
|
528
|
-
rails g
|
528
|
+
rails g runger:install --configs-path=config/settings
|
529
529
|
|
530
530
|
# or to keep everything in app/configs
|
531
|
-
rails g
|
531
|
+
rails g runger:install --configs-path=app/configs
|
532
532
|
```
|
533
533
|
|
534
|
-
- `rails g
|
534
|
+
- `rails g runger:config <name> param1 param2 ...`—creates a named configuration class and optionally the corresponding YAML file; creates `application_config.rb` is missing.
|
535
535
|
|
536
536
|
The generator command for the Heroku example above would be:
|
537
537
|
|
538
538
|
```sh
|
539
|
-
$ rails g
|
539
|
+
$ rails g runger:config heroku app_id app_name dyno_id release_version slug_commit
|
540
540
|
|
541
|
-
generate
|
542
|
-
rails generate
|
541
|
+
generate runger:install
|
542
|
+
rails generate runger:install
|
543
543
|
create config/configs/application_config.rb
|
544
544
|
append .gitignore
|
545
545
|
create config/configs/heroku_config.rb
|
@@ -547,17 +547,17 @@ Would you like to generate a heroku.yml file? (Y/n) n
|
|
547
547
|
```
|
548
548
|
|
549
549
|
You can also specify the `--app` option to put the newly created class into `app/configs` folder.
|
550
|
-
Alternatively, you can call `rails g
|
550
|
+
Alternatively, you can call `rails g runger:app_config name param1 param2 ...`.
|
551
551
|
|
552
|
-
**NOTE:** The generated `ApplicationConfig` class uses a singleton pattern along with `delegate_missing_to` to re-use the same instance across the application. However, the delegation can lead to unexpected behaviour and break
|
552
|
+
**NOTE:** The generated `ApplicationConfig` class uses a singleton pattern along with `delegate_missing_to` to re-use the same instance across the application. However, the delegation can lead to unexpected behaviour and break Runger Config internals if you have attributes named as `Runger::Config` class methods. See [#120](https://github.com/davidrunger/runger_config/issues/120).
|
553
553
|
|
554
|
-
### Loading
|
554
|
+
### Loading Runger Config before Rails
|
555
555
|
|
556
|
-
|
556
|
+
Runger Config activates Rails-specific features automatically on the gem load only if Rails has been already required (we check for the `Rails::VERSION` constant presence). However, in some cases you may want to use Runger Config before Rails initialization (e.g., in `config/puma.rb` when starting a Puma web server).
|
557
557
|
|
558
|
-
By default,
|
558
|
+
By default, Runger Config sets up a hook (via TracePoint API) and waits for Rails to be loaded to require the Rails extensions (`require "runger/rails"`). In case you load Rails after Runger Config, you will see a warning telling you about that. Note that config classes loaded before Rails are not populated from Rails-specific data sources (e.g., credentials).
|
559
559
|
|
560
|
-
You can disable the warning by setting `
|
560
|
+
You can disable the warning by setting `Runger::Rails.disable_postponed_load_warning = true` in your application. Also, you can disable the _hook_ completely by calling `Runger::Rails.tracer.disable`.
|
561
561
|
|
562
562
|
## Using with Ruby
|
563
563
|
|
@@ -566,13 +566,13 @@ The default data loading mechanism for non-Rails applications is the following (
|
|
566
566
|
1) **YAML configuration files**: `./config/<config-name>.yml`.
|
567
567
|
|
568
568
|
In pure Ruby apps, we also can load data under specific _environments_ (`test`, `development`, `production`, etc.).
|
569
|
-
If you want to enable this feature you must specify `
|
569
|
+
If you want to enable this feature you must specify `Runger::Settings.current_environment` variable for load config under specific environment.
|
570
570
|
|
571
571
|
```ruby
|
572
|
-
|
572
|
+
Runger::Settings.current_environment = "development"
|
573
573
|
```
|
574
574
|
|
575
|
-
You can also specify the `
|
575
|
+
You can also specify the `RUNGER_ENV=development` environment variable to set the current environment for configuration.
|
576
576
|
|
577
577
|
YAML files should be in this format:
|
578
578
|
|
@@ -582,7 +582,7 @@ development:
|
|
582
582
|
port: 3000
|
583
583
|
```
|
584
584
|
|
585
|
-
If `
|
585
|
+
If `Runger::Settings.current_environment` is missed we assume that the YAML contains values for a single environment:
|
586
586
|
|
587
587
|
```yml
|
588
588
|
host: localhost
|
@@ -593,16 +593,16 @@ port: 3000
|
|
593
593
|
|
594
594
|
You can specify the lookup path for YAML files in one of the following ways:
|
595
595
|
|
596
|
-
- By setting `
|
596
|
+
- By setting `Runger::Settings.default_config_path` to a target directory path:
|
597
597
|
|
598
598
|
```ruby
|
599
|
-
|
599
|
+
Runger::Settings.default_config_path = "/etc/configs"
|
600
600
|
```
|
601
601
|
|
602
|
-
- By setting `
|
602
|
+
- By setting `Runger::Settings.default_config_path` to a Proc, which accepts a config name and returns the path:
|
603
603
|
|
604
604
|
```ruby
|
605
|
-
|
605
|
+
Runger::Settings.default_config_path = ->(name) { Rails.root.join("data", "configs", "#{name}.yml") }
|
606
606
|
```
|
607
607
|
|
608
608
|
- By overriding a specific config YML file path via the `<NAME>_CONF` env variable, e.g., `MYCOOLGEM_CONF=path/to/cool.yml`
|
@@ -626,7 +626,7 @@ By default, environment variables are automatically type cast (rules are case-in
|
|
626
626
|
|
627
627
|
Type coercion can be [customized or disabled](#type-coercion).
|
628
628
|
|
629
|
-
*
|
629
|
+
*Runger Config* supports nested (_hashed_) env variables—just separate keys with double-underscore.
|
630
630
|
|
631
631
|
For example, "MYCOOLGEM_OPTIONS__VERBOSE" is parsed as `config.options["verbose"]`.
|
632
632
|
|
@@ -650,7 +650,7 @@ MYCOOLGEM = "Nif-Nif, Naf-Naf and Nouf-Nouf"
|
|
650
650
|
You can define custom type coercion rules to convert string data to config values. To do that, use `.coerce_types` method:
|
651
651
|
|
652
652
|
```ruby
|
653
|
-
class CoolConfig <
|
653
|
+
class CoolConfig < Runger::Config
|
654
654
|
config_name :cool
|
655
655
|
attr_config port: 8080,
|
656
656
|
host: "localhost",
|
@@ -691,7 +691,7 @@ coerce_types non_list: :string
|
|
691
691
|
Finally, it's possible to disable auto-casting for a particular config completely:
|
692
692
|
|
693
693
|
```ruby
|
694
|
-
class CoolConfig <
|
694
|
+
class CoolConfig < Runger::Config
|
695
695
|
attr_config port: 8080,
|
696
696
|
host: "localhost",
|
697
697
|
user: {name: "admin", password: "admin"}
|
@@ -722,7 +722,7 @@ COLOR_TO_HEX = lambda do |raw|
|
|
722
722
|
end
|
723
723
|
end
|
724
724
|
|
725
|
-
class CoolConfig <
|
725
|
+
class CoolConfig < Runger::Config
|
726
726
|
attr_config :color
|
727
727
|
|
728
728
|
coerce_types color: COLOR_TO_HEX
|
@@ -743,7 +743,7 @@ We support this by looking at _local_ files when loading the configuration data:
|
|
743
743
|
\* If the YAML config path is not a default one (i.e., set via `<CONFIG_NAME>_CONF`), we look up the local
|
744
744
|
config at this location, too.
|
745
745
|
|
746
|
-
Local configs are meant for using in development and only loaded if `
|
746
|
+
Local configs are meant for using in development and only loaded if `Runger::Settings.use_local_files` is `true` (which is true by default if `RACK_ENV` or `RAILS_ENV` env variable is equal to `"development"`).
|
747
747
|
|
748
748
|
**NOTE:** in Rails apps you can use `Rails.application.configuration.runger_config.use_local_files`.
|
749
749
|
|
@@ -755,24 +755,24 @@ Don't forget to add `*.local.yml` (and `config/credentials/local.*`) to your `.g
|
|
755
755
|
|
756
756
|
### Doppler integration
|
757
757
|
|
758
|
-
|
758
|
+
Runger Config can pull configuration data from [Doppler](https://www.doppler.com/). All you need is to specify the `DOPPLER_TOKEN` environment variable with the **service token**, associated with the specific content (read more about [service tokens](https://docs.doppler.com/docs/service-tokens)).
|
759
759
|
|
760
760
|
You can also configure Doppler loader manually if needed:
|
761
761
|
|
762
762
|
```ruby
|
763
763
|
# Add loader
|
764
|
-
|
764
|
+
Runger.loaders.append :Doppler, Runger::Loaders::Doppler
|
765
765
|
|
766
766
|
# Configure API URL and token (defaults are shown)
|
767
|
-
|
768
|
-
|
767
|
+
Runger::Loaders::Doppler.download_url = "https://api.doppler.com/v3/configs/config/secrets/download"
|
768
|
+
Runger::Loaders::Doppler.token = ENV["DOPPLER_TOKEN"]
|
769
769
|
```
|
770
770
|
|
771
|
-
**NOTE:** You can opt-out from Doppler loader by specifying the`
|
771
|
+
**NOTE:** You can opt-out from Doppler loader by specifying the`RUNGER_CONFIG_DISABLE_DOPPLER=true` env var (in case you have the `DOPPLER_TOKEN` env var, but don't want to use it with Runger Config).
|
772
772
|
|
773
773
|
### EJSON support
|
774
774
|
|
775
|
-
|
775
|
+
Runger Config allows you to keep your configuration also in encrypted `.ejson` files. More information
|
776
776
|
about EJSON format you can read [here](https://github.com/Shopify/ejson).
|
777
777
|
|
778
778
|
Configuration will be loaded only if you have `ejson` executable in your PATH. Easiest way to do this - install `ejson` as a gem into project:
|
@@ -810,7 +810,7 @@ ejson decrypt config/secrets.ejson
|
|
810
810
|
You can customize the JSON namespace under which a loader searches for configuration via `loader_options`:
|
811
811
|
|
812
812
|
```ruby
|
813
|
-
class MyConfig <
|
813
|
+
class MyConfig < Runger::Config
|
814
814
|
# To look under the key "foo" instead of the default key of "my"
|
815
815
|
loader_options ejson_namespace: "foo"
|
816
816
|
|
@@ -825,10 +825,10 @@ You can provide your own data loaders or change the existing ones using the Load
|
|
825
825
|
|
826
826
|
```ruby
|
827
827
|
# remove env loader => do not load params from ENV
|
828
|
-
|
828
|
+
Runger.loaders.delete :env
|
829
829
|
|
830
830
|
# add custom loader before :env (it's better to keep the ENV loader the last one)
|
831
|
-
|
831
|
+
Runger.loaders.insert_before :env, :my_loader, MyLoader
|
832
832
|
```
|
833
833
|
|
834
834
|
Loader is a _callable_ Ruby object (module/class responding to `.call` or lambda/proc), which `call` method
|
@@ -840,13 +840,13 @@ def call(
|
|
840
840
|
env_prefix:, # prefix for env vars if any
|
841
841
|
config_path:, # path to YML config
|
842
842
|
local:, # true|false, whether to load local configuration
|
843
|
-
**options # custom options can be passed via
|
843
|
+
**options # custom options can be passed via Runger::Config.loader_options example: "custom", option: "blah"
|
844
844
|
)
|
845
845
|
#=> must return Hash with configuration data
|
846
846
|
end
|
847
847
|
```
|
848
848
|
|
849
|
-
You can use `
|
849
|
+
You can use `Runger::Loaders::Base` as a base class for your loader and define a `#call` method.
|
850
850
|
For example, the [Chamber](https://github.com/thekompanee/chamber) loader could be written as follows:
|
851
851
|
|
852
852
|
```ruby
|
@@ -860,7 +860,7 @@ class ChamberConfigLoader < Base
|
|
860
860
|
end
|
861
861
|
|
862
862
|
# Don't forget to register it
|
863
|
-
|
863
|
+
Runger.loaders.insert_before :env, :chamber, ChamberConfigLoader
|
864
864
|
```
|
865
865
|
|
866
866
|
In order to support [source tracing](#tracing), you need to wrap the resulting Hash via the `#trace!` method with metadata:
|
@@ -878,9 +878,9 @@ end
|
|
878
878
|
|
879
879
|
## Tracing
|
880
880
|
|
881
|
-
Since
|
881
|
+
Since Runger Config loads data from multiple source, it could be useful to know where a particular value came from.
|
882
882
|
|
883
|
-
Each `
|
883
|
+
Each `Runger::Config` instance contains _tracing information_ which you can access via `#to_source_trace` method:
|
884
884
|
|
885
885
|
```ruby
|
886
886
|
conf = ExampleConfig.new
|
@@ -899,12 +899,12 @@ conf.to_source_trace
|
|
899
899
|
# if you change the value manually in your code,
|
900
900
|
# that would be reflected in the trace
|
901
901
|
|
902
|
-
conf.host = "
|
902
|
+
conf.host = "runger.host"
|
903
903
|
conf.to_source_trace["host"]
|
904
904
|
#=> {type: :user, called_from: "/path/to/caller.rb:15"}
|
905
905
|
```
|
906
906
|
|
907
|
-
You can disable tracing functionality by setting `
|
907
|
+
You can disable tracing functionality by setting `Runger::Settings.tracing_enabled = false` or `config.runger_config.tracing_enabled = false` in Rails.
|
908
908
|
|
909
909
|
### Pretty print
|
910
910
|
|
@@ -986,7 +986,7 @@ If you want to delete the env var, pass `nil` as the value.
|
|
986
986
|
|
987
987
|
This helper is automatically included to RSpec if `RAILS_ENV` or `RACK_ENV` env variable is equal to "test". It's only available for the example with the tag `type: :config` or with the path `spec/configs/...`.
|
988
988
|
|
989
|
-
You can add it manually by requiring `"
|
989
|
+
You can add it manually by requiring `"runger/testing/helpers"` and including the `Runger::Testing::Helpers` module (into RSpec configuration or Minitest test class).
|
990
990
|
|
991
991
|
## OptionParser integration
|
992
992
|
|
@@ -996,7 +996,7 @@ It's possible to use config as option parser (e.g., for CLI apps/libraries). It
|
|
996
996
|
Example usage:
|
997
997
|
|
998
998
|
```ruby
|
999
|
-
class MyConfig <
|
999
|
+
class MyConfig < Runger::Config
|
1000
1000
|
attr_config :host, :log_level, :concurrency, :debug, server_args: {}
|
1001
1001
|
|
1002
1002
|
# specify which options shouldn't be handled by option parser
|
@@ -1052,7 +1052,7 @@ describe_options(
|
|
1052
1052
|
|
1053
1053
|
## RBS support
|
1054
1054
|
|
1055
|
-
|
1055
|
+
Runger Config comes with Ruby type signatures (RBS).
|
1056
1056
|
|
1057
1057
|
To use them with Steep, add the following your `Steepfile`:
|
1058
1058
|
|
@@ -1065,7 +1065,7 @@ library "runger_config"
|
|
1065
1065
|
We also provide an API to generate a type signature for your config class:
|
1066
1066
|
|
1067
1067
|
```ruby
|
1068
|
-
class MyGem::Config <
|
1068
|
+
class MyGem::Config < Runger::Config
|
1069
1069
|
attr_config :host, port: 8080, tags: [], debug: false
|
1070
1070
|
|
1071
1071
|
coerce_types host: :string, port: :integer,
|
@@ -1091,7 +1091,7 @@ module MyGem
|
|
1091
1091
|
def debug=: (bool) -> void
|
1092
1092
|
end
|
1093
1093
|
|
1094
|
-
class Config <
|
1094
|
+
class Config < Runger::Config
|
1095
1095
|
include _Config
|
1096
1096
|
end
|
1097
1097
|
end
|
@@ -1102,7 +1102,7 @@ end
|
|
1102
1102
|
When we use `on_load` callback with a block, we switch the context (via `instance_eval`), and we need to provide type hints for the type checker. Here is an example:
|
1103
1103
|
|
1104
1104
|
```ruby
|
1105
|
-
class MyConfig <
|
1105
|
+
class MyConfig < Runger::Config
|
1106
1106
|
on_load do
|
1107
1107
|
# @type self : MyConfig
|
1108
1108
|
raise_validation_error("host is invalid") if host.start_with?("localhost")
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'generators/runger/config/config_generator'
|
4
|
+
|
5
|
+
class Runger::Generators::AppConfigGenerator < Runger::Generators::ConfigGenerator
|
6
|
+
source_root ::Runger::Generators::ConfigGenerator.source_root
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def config_root
|
11
|
+
'app/configs'
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
|
5
|
+
module Runger::Generators ; end
|
6
|
+
|
7
|
+
class Runger::Generators::ConfigGenerator < Rails::Generators::NamedBase
|
8
|
+
source_root File.expand_path('templates', __dir__)
|
9
|
+
|
10
|
+
class_option :yml, type: :boolean
|
11
|
+
class_option :app, type: :boolean, default: false
|
12
|
+
argument :parameters, type: :array, default: [], banner: 'param1 param2'
|
13
|
+
|
14
|
+
# check_class_collision suffix: "Config"
|
15
|
+
|
16
|
+
def run_install_if_needed
|
17
|
+
return if ::Rails.root.join(static_config_root, 'application_config.rb').exist?
|
18
|
+
|
19
|
+
generate('runger:install')
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_config
|
23
|
+
template('config.rb', File.join(config_root, class_path, "#{file_name}_config.rb"))
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_yml
|
27
|
+
create_yml =
|
28
|
+
options.fetch(:yml) {
|
29
|
+
yes?("Would you like to generate a #{file_name}.yml file?")
|
30
|
+
}
|
31
|
+
return unless create_yml
|
32
|
+
|
33
|
+
template('config.yml', File.join('config', "#{file_name}.yml"))
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def static_config_root
|
39
|
+
Runger::Settings.autoload_static_config_path || Runger::DEFAULT_CONFIGS_PATH
|
40
|
+
end
|
41
|
+
|
42
|
+
def config_root
|
43
|
+
if options[:app]
|
44
|
+
'app/configs'
|
45
|
+
else
|
46
|
+
static_config_root
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
|
5
|
+
module Runger::Generators ; end
|
6
|
+
|
7
|
+
class Runger::Generators::InstallGenerator < Rails::Generators::Base
|
8
|
+
source_root File.expand_path('templates', __dir__)
|
9
|
+
|
10
|
+
class_option :configs_path, type: :string
|
11
|
+
|
12
|
+
def copy_application_config
|
13
|
+
template('application_config.rb', File.join(static_config_root, 'application_config.rb'))
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_local_files_to_gitignore
|
17
|
+
if File.exist?(File.join(destination_root, '.gitignore'))
|
18
|
+
append_to_file('.gitignore', "\n/config/*.local.yml\n/config/credentials/local.*\n")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# rubocop:disable Layout/HeredocIndentation
|
23
|
+
def add_setup_autoload_to_config
|
24
|
+
maybe_comment_indented = default_configs_path? ? ' # ' : ' '
|
25
|
+
inject_into_file('config/application.rb', after: %r{< Rails::Application\n}) do
|
26
|
+
<<-RUBY
|
27
|
+
# Configure the path for configuration classes that should be used before initialization
|
28
|
+
# NOTE: path should be relative to the project root (Rails.root)
|
29
|
+
#{maybe_comment_indented}config.runger_config.autoload_static_config_path = "#{static_config_root}"
|
30
|
+
#{maybe_comment_indented.sub(/\s+$/, '')}
|
31
|
+
RUBY
|
32
|
+
end
|
33
|
+
end
|
34
|
+
# rubocop:enable Layout/HeredocIndentation
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def static_config_root
|
39
|
+
options[:configs_path] || Runger::Settings.autoload_static_config_path || Runger::DEFAULT_CONFIGS_PATH
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_configs_path?
|
43
|
+
static_config_root == (Runger::Settings.autoload_static_config_path || Runger::DEFAULT_CONFIGS_PATH)
|
44
|
+
end
|
45
|
+
end
|