anyway_config 2.0.0.pre2 → 2.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +350 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +444 -119
  5. data/lib/.rbnext/2.7/anyway/auto_cast.rb +33 -0
  6. data/lib/.rbnext/2.7/anyway/config.rb +404 -0
  7. data/lib/.rbnext/2.7/anyway/option_parser_builder.rb +31 -0
  8. data/lib/.rbnext/2.7/anyway/tracing.rb +187 -0
  9. data/lib/anyway/auto_cast.rb +33 -0
  10. data/lib/anyway/config.rb +235 -58
  11. data/lib/anyway/dynamic_config.rb +1 -1
  12. data/lib/anyway/env.rb +29 -19
  13. data/lib/anyway/ext/hash.rb +14 -6
  14. data/lib/anyway/loaders.rb +79 -0
  15. data/lib/anyway/loaders/base.rb +23 -0
  16. data/lib/anyway/loaders/env.rb +16 -0
  17. data/lib/anyway/loaders/yaml.rb +46 -0
  18. data/lib/anyway/option_parser_builder.rb +6 -3
  19. data/lib/anyway/optparse_config.rb +10 -6
  20. data/lib/anyway/rails.rb +16 -0
  21. data/lib/anyway/rails/config.rb +2 -69
  22. data/lib/anyway/rails/loaders.rb +5 -0
  23. data/lib/anyway/rails/loaders/credentials.rb +64 -0
  24. data/lib/anyway/rails/loaders/secrets.rb +39 -0
  25. data/lib/anyway/rails/loaders/yaml.rb +19 -0
  26. data/lib/anyway/rails/settings.rb +58 -0
  27. data/lib/anyway/railtie.rb +14 -2
  28. data/lib/anyway/settings.rb +29 -0
  29. data/lib/anyway/tracing.rb +187 -0
  30. data/lib/anyway/version.rb +1 -1
  31. data/lib/anyway_config.rb +27 -21
  32. data/lib/generators/anyway/app_config/USAGE +9 -0
  33. data/lib/generators/anyway/app_config/app_config_generator.rb +17 -0
  34. data/lib/generators/anyway/config/USAGE +13 -0
  35. data/lib/generators/anyway/config/config_generator.rb +46 -0
  36. data/lib/generators/anyway/config/templates/config.rb.tt +9 -0
  37. data/lib/generators/anyway/config/templates/config.yml.tt +13 -0
  38. data/lib/generators/anyway/install/USAGE +4 -0
  39. data/lib/generators/anyway/install/install_generator.rb +43 -0
  40. data/lib/generators/anyway/install/templates/application_config.rb.tt +17 -0
  41. metadata +75 -10
  42. data/lib/anyway/ext/string_serialize.rb +0 -38
  43. data/lib/anyway/loaders/env_loader.rb +0 -0
  44. data/lib/anyway/loaders/secrets_loader.rb +0 -0
  45. data/lib/anyway/loaders/yaml_loader.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 540aa91321939a31eb3982143a12cc17621bdcb23c61d2d24686f999ae28a144
4
- data.tar.gz: 3215663dd3722892f0635d13163dc02521f515ce31f5c310402893f304387b2d
3
+ metadata.gz: 0c4661661a2d8cbce7e35a4eef45520a290855af228b1d20d69367708b11be3f
4
+ data.tar.gz: 97f07abb3e194f490069d6dc1ef7076a88f3775f5b06a3cc99d15314da845c54
5
5
  SHA512:
6
- metadata.gz: a629cdb9ba784714cff8df41814a6520745b9f1a6fdce272da2a56fd9d6635d46365767c4ce1289c93fdb0f3c5e4d9167b9b617830ff719c555a2a5a6e19f915
7
- data.tar.gz: '009fd89edf49b814bde0fa2c7d846fa3f086fe606386c067bc447f4f8c990853a580257cb943e07692b57b49674912fd041f29cc9dbbb9eb652aaa3ee3857776'
6
+ metadata.gz: 9897240812d5ae7522b99c0d4ac2ec1c3e2ec5759ef481ff3db6d9fbeb1c46312a895ed64fd94fc6b3c1a416a3ccee9325458c1aeacd0ff7dbc9a10593fabf89
7
+ data.tar.gz: c590c11acb69e99ff0ada09fa7ebb4ae44f777da626f2f9dd053935d9d201fa6b3d6296642271e7d56260b9258d0baca65ade78fd10c9397e5205a03d8551b1a
data/CHANGELOG.md ADDED
@@ -0,0 +1,350 @@
1
+ # Change log
2
+
3
+ ## master
4
+
5
+ ## 2.0.0.rc1 (2020-03-31)
6
+
7
+ - Add predicate methods for attributes with boolean defaults. ([@palkan][])
8
+
9
+ For example:
10
+
11
+ ```ruby
12
+ class MyConfig < Anyway::Config
13
+ attr_config :key, :secret, debug: false
14
+ end
15
+
16
+ MyConfig.new.debug? #=> false
17
+ MyConfig.new(debug: true).debug? #=> true
18
+ ```
19
+
20
+ - Add `Config#deconstruct_keys`. ([@palkan][])
21
+
22
+ Now you can use configs in pattern matching:
23
+
24
+ ```ruby
25
+ case AWSConfig.new
26
+ in bucket:, region: "eu-west-1"
27
+ setup_eu_storage(bucket)
28
+ in bucket:, region: "us-east-1"
29
+ setup_us_storage(bucket)
30
+ end
31
+ ```
32
+
33
+ - Add pretty_print support. ([@palkan][])
34
+
35
+ Whenever you use `pp`, the output would contain pretty formatted config information
36
+ including the sources.
37
+
38
+ Example:
39
+
40
+ ```ruby
41
+ pp CoolConfig.new
42
+
43
+ # #<CoolConfig
44
+ # config_name="cool"
45
+ # env_prefix="COOL"
46
+ # values:
47
+ # port => 3334 (type=load),
48
+ # host => "test.host" (type=yml path=./config/cool.yml),
49
+ # user =>
50
+ # name => "john" (type=env key=COOL_USER__NAME),
51
+ # password => "root" (type=yml path=./config/cool.yml)>
52
+ ```
53
+
54
+ - Add source tracing support. ([@palkan][])
55
+
56
+ You can get the information on where a particular parameter value came from
57
+ (which loader) through via `#to_source_trace` method:
58
+
59
+ ```ruby
60
+ conf = MyConfig.new
61
+ conf.to_source_trace
62
+ # {
63
+ # "host" => {value: "test.host", source: {type: :user, called_from: "/rails/root/config/application.rb:21"}},
64
+ # "user" => {
65
+ # "name" => {value: "john", source: {type: :env, key: "COOL_USER__NAME"}},
66
+ # "password" => {value: "root", source: {type: :yml, path: "config/cool.yml"}}
67
+ # },
68
+ # "port" => {value: 9292, source: {type: :defaults}}
69
+ # }
70
+ ```
71
+
72
+ - Change the way Rails configs autoloading works. ([@palkan][])
73
+
74
+ In Rails 6, autoloading before initialization is [deprecated](https://github.com/rails/rails/commit/3e66ba91d511158e22f90ff96b594d61f40eda01). We can still
75
+ make it work by using our own autoloading mechanism (custom Zeitwerk loader).
76
+
77
+ This forces us to use a custom directory (not `app/`) for configs required at the boot time.
78
+ By default, we put _static_ configs into `config/configs` but you can still use `app/configs` for
79
+ _dynamic_ (runtime) configs.
80
+
81
+ **NOTE:** if you used `app/configs` with 2.0.0.preX and relied on configs during initialization,
82
+ you can set static configs path to `app/configs`:
83
+
84
+ ```ruby
85
+ config.anyway_config.autoload_static_config_path = "app/configs"
86
+ ```
87
+
88
+ You can do this by running the generator:
89
+
90
+ ```sh
91
+ rails g anyway:install --configs-path=app/configs
92
+ ```
93
+
94
+ - Add Rails generators. ([@palkan][])
95
+
96
+ You can create config classes with the predefined attributes like this:
97
+
98
+ ```sh
99
+ rails generate config aws access_key_id secret_access_key region
100
+ ```
101
+
102
+ - **BREAKING** The accessors generated by `attr_config` are not longer `attr_accessor`-s. ([@palkan][])
103
+
104
+ You cannot rely on instance variables anymore. Instead, you can use `super` when overriding accessors or
105
+ `values[name]`:
106
+
107
+ ```ruby
108
+ attr_config :host, :port, :url, :meta
109
+
110
+ # override writer to handle type coercion
111
+ def meta=(val)
112
+ super JSON.parse(val)
113
+ end
114
+
115
+ # or override reader to handle missing values
116
+ def url
117
+ values[:url] ||= "#{host}:#{port}"
118
+ end
119
+
120
+ # in <2.1 it's still possible to read instance variables,
121
+ # i.e. the following would also work
122
+ def url
123
+ @url ||= "#{host}:#{port}"
124
+ end
125
+ ```
126
+
127
+ **NOTE**: we still set instance variables in writers (for backward compatibility), but that would
128
+ be removed in 2.1.
129
+
130
+ - Add `Config#dig` method. ([@palkan][])
131
+
132
+ - Add ability to specify types for OptionParser options. ([@palkan][])
133
+
134
+ ```ruby
135
+ describe_options(
136
+ concurrency: {
137
+ desc: "number of threads to use",
138
+ type: String
139
+ }
140
+ )
141
+ ```
142
+
143
+ - Add param presence validation. ([@palkan][])
144
+
145
+ You can specify some params as required, and the validation
146
+ error would be raised if they're missing or empty (only for strings):
147
+
148
+ ```ruby
149
+ class MyConfig < Anyway::Config
150
+ attr_config :api_key, :api_secret, :debug
151
+
152
+ required :api_key, :api_secret
153
+ end
154
+
155
+ MyConfig.new(api_secret: "") #=> raises Anyway::Config::ValidationError
156
+ ```
157
+
158
+ You can change the validation behaviour by overriding the `#validate!` method in your config class.
159
+
160
+ - Validate config attribute names. ([@palkan][])
161
+
162
+ Do not allow using reserved names (`Anyway::Config` method names).
163
+ Allow only alphanumeric names (matching `/^[a-z_]([\w]+)?$/`).
164
+
165
+ - Add Loaders API. ([@palkan][])
166
+
167
+ All config sources have standardized via _loaders_ API. It's possible to define
168
+ custom loaders or change the sources order.
169
+
170
+ ## 2.0.0.pre2 (2019-04-26)
171
+
172
+ - Fix bug with loading from credentials when local credentials are missing. ([@palkan][])
173
+
174
+ ## 2.0.0.pre (2019-04-26)
175
+
176
+ - **BREAKING** Changed the way of providing explicit values. ([@palkan][])
177
+
178
+ ```ruby
179
+ # BEFORE
180
+ Config.new(overrides: data)
181
+
182
+ # AFTER
183
+ Config.new(data)
184
+ ```
185
+
186
+ - Add Railtie. ([@palkan][])
187
+
188
+ `Anyway::Railtie` provides `Anyway::Settings` access via `Rails.applicaiton.configuration.anyway_config`.
189
+
190
+ It also adds `app/configs` path to autoload paths (low-level, `ActiveSupport::Dependencies`) to
191
+ make it possible to use configs in the app configuration files.
192
+
193
+ - Add test helpers. ([@palkan][])
194
+
195
+ Added `with_env` helper to test code in the context of the specified
196
+ environment variables.
197
+
198
+ Included automatically in RSpec for examples with the `type: :config` meta
199
+ or with the `spec/configs` path.
200
+
201
+ - Add support for _local_ files. ([@palkan][])
202
+
203
+ Now users can store their personal configurations in _local_ files:
204
+ - `<config_name>.local.yml`
205
+ - `config/credentials/local.yml.enc` (for Rails 6).
206
+
207
+ Local configs are meant for using in development and only loaded if
208
+ `Anyway::Settings.use_local_files` is `true` (which is true by default if
209
+ `RACK_ENV` or `RAILS_ENV` env variable is equal to `"development"`).
210
+
211
+ - Add Rails credentials support. ([@palkan][])
212
+
213
+ The data from credentials is loaded after the data from YAML config and secrets,
214
+ but before environmental variables (i.e. env variables are _stronger_)
215
+
216
+ - Update config name inference logic. ([@palkan][])
217
+
218
+ Config name is automatically inferred only if:
219
+ - the class name has a form of `<Module>::Config` (`SomeModule::Config => "some_module"`)
220
+ - the class name has a form of `<Something>Config` (`SomeConfig => "some"`)
221
+
222
+ - Fix config classes inheritance. ([@palkan][])
223
+
224
+ Previously, inheritance didn't work due to the lack of proper handling of class-level
225
+ configuration (naming, option parses settings, defaults).
226
+
227
+ Now it's possible to extend config classes without breaking the original classes functionality.
228
+
229
+ Noticeable features:
230
+ - if `config_name` is explicitly defined on class, it's inherited by subclasses:
231
+
232
+ ```ruby
233
+ class MyAppBaseConfig < Anyway::Config
234
+ config_name :my_app
235
+ end
236
+
237
+ class MyServiceConfig < MyAppBaseConfig
238
+ end
239
+
240
+ MyServiceConfig.config_name #=> "my_app"
241
+ ```
242
+
243
+ - defaults are merged leaving the parent class defaults unchanged
244
+ - option parse extension are not overriden, but added to the parent class extensions
245
+
246
+ - **Require Ruby >= 2.5.0.**
247
+
248
+ ## 1.4.3 (2019-02-04)
249
+
250
+ - Add a temporary fix for JRuby regression [#5550](https://github.com/jruby/jruby/issues/5550). ([@palkan][])
251
+
252
+ ## 1.4.2 (2018-01-05)
253
+
254
+ - Fix: detect Rails by presence of `Rails::VERSION` (instead of just `Rails`). ([@palkan][])
255
+
256
+ ## 1.4.1 (2018-10-30)
257
+
258
+ - Add `.flag_options` to mark some params as flags (value-less) for OptionParse. ([@palkan][])
259
+
260
+ ## 1.4.0 (2018-10-29)
261
+
262
+ - Add OptionParse integration ([@jastkand][])
263
+
264
+ See more [PR#18](https://github.com/palkan/anyway_config/pull/18).
265
+
266
+ - Use underscored config name as an env prefix. ([@palkan][])
267
+
268
+ For a config class:
269
+
270
+ ```ruby
271
+ class MyApp < Anyway::Config
272
+ end
273
+ ```
274
+
275
+ Before this change we use `MYAPP_` prefix, now it's `MY_APP_`.
276
+
277
+ You can specify the prefix explicitly:
278
+
279
+ ```ruby
280
+ class MyApp < Anyway::Config
281
+ env_prefix "MYAPP_"
282
+ end
283
+ ```
284
+
285
+ ## 1.3.0 (2018-06-15)
286
+
287
+ - Ruby 2.2 is no longer supported.
288
+
289
+ - `Anyway::Config.env_prefix` method is introduced. ([@charlie-wasp][])
290
+
291
+ ## 1.2.0 (2018-02-19)
292
+
293
+ Now works on JRuby 9.1+.
294
+
295
+ ## 1.1.3 (2017-12-20)
296
+
297
+ - Allow to pass raw hash with explicit values to `Config.new`. ([@dsalahutdinov][])
298
+
299
+ Example:
300
+
301
+ ```ruby
302
+ Sniffer::Config.new(
303
+ overrides: {
304
+ enabled: true,
305
+ storage: {capacity: 500}
306
+ }
307
+ )
308
+ ```
309
+
310
+ See more [PR#10](https://github.com/palkan/anyway_config/pull/10).
311
+
312
+ ## 1.1.2 (2017-11-19)
313
+
314
+ - Enable aliases for YAML. ([@onemanstartup][])
315
+
316
+ ## 1.1.1 (2017-10-21)
317
+
318
+ - Return deep duplicate of a Hash in `Env#fetch`. ([@palkan][])
319
+
320
+ ## 1.1.0 (2017-10-06)
321
+
322
+ - Add `#to_h` method. ([@palkan][])
323
+
324
+ See [#4](https://github.com/palkan/anyway_config/issues/4).
325
+
326
+ - Make it possible to extend configuration parameters. ([@palkan][])
327
+
328
+ ## 1.0.0 (2017-06-20)
329
+
330
+ - Lazy load and parse ENV configuration. ([@palkan][])
331
+
332
+ - Add support for ERB in YML configuration. ([@palkan][])
333
+
334
+ ## 0.5.0 (2017-01-20)
335
+
336
+ - Drop `active_support` dependency. ([@palkan][])
337
+
338
+ Use custom refinements instead of requiring `active_support`.
339
+
340
+ No we're dependency-free!
341
+
342
+ ## 0.1.0 (2015-01-20)
343
+
344
+ Initial version.
345
+
346
+ [@palkan]: https://github.com/palkan
347
+ [@onemanstartup]: https://github.com/onemanstartup
348
+ [@dsalahutdinov]: https://github.com/dsalahutdinov
349
+ [@charlie-wasp]: https://github.com/charlie-wasp
350
+ [@jastkand]: https://github.com/jastkand
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015-2019 Vladimir Dementyev
1
+ Copyright (c) 2015-2020 Vladimir Dementyev
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -4,23 +4,70 @@
4
4
 
5
5
  # Anyway Config
6
6
 
7
- **NOTE:** this readme shows doc for the upcoming 2.0 version (`2.0.0.pre` is available on RubyGems).
7
+ > One configuration to rule all data sources
8
+
9
+ Anyway Config is a configuration library for Ruby gems and applications.
10
+
11
+ As a library author, you can benefit from using Anyway Config by providing a better UX for your end-users:
12
+
13
+ - **Zero-code configuration** — no more boilerplate initializers.
14
+ - **Per-environment and local** settings support out-of-the-box.
15
+
16
+ For application developers, Anyway Config could be useful to:
17
+
18
+ - **Keep configuration organized** and use _named configs_ instead of bloated `.env`/`settings.yml`/whatever.
19
+ - **Free code of ENV/credentials/secrets dependency** and use configuration classes instead—your code should not rely on configuration data sources.
20
+
21
+ **NOTE:** this readme shows documentation for the upcoming 2.0 version (`2.0.0.rc1` is available on RubyGems).
8
22
  For version 1.x see [1-4-stable branch](https://github.com/palkan/anyway_config/tree/1-4-stable).
9
23
 
10
- Rails/Ruby plugin/application configuration tool which allows you to load parameters from different sources: YAML, Rails secrets/credentials, environment.
24
+ ## Table of contents
25
+
26
+ - [Main concepts](#main-concepts)
27
+ - [Installation](#installation)
28
+ - [Usage](#usage)
29
+ - [Configuration classes](#configuration-classes)
30
+ - [Dynamic configuration](#dynamic-configuration)
31
+ - [Validation & Callbacks](#validation-and-callbacks)
32
+ - [Using with Rails applications](#using-with-rails)
33
+ - [Data population](#data-population)
34
+ - [Organizing configs](#organizing-configs)
35
+ - [Generators](#generators)
36
+ - [Using with Ruby applications](#using-with-ruby)
37
+ - [Environment variables](#environment-variables)
38
+ - [Local configuration](#local-files)
39
+ - [Data loaders](#data-loaders)
40
+ - [Source tracing](#tracing)
41
+ - [Pattern matching](#pattern-matching)
42
+ - [Test helpers](#test-helpers)
43
+ - [OptionParser integration](#optionparser-integration)
44
+
45
+ ## Main concepts
46
+
47
+ Anyway 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):
11
48
 
12
- Allows you to easily follow the [twelve-factor application](https://12factor.net/config) principles and adds zero complexity to your development process.
49
+ ```ruby
50
+ module Influxer
51
+ class Config < Anyway::Config
52
+ attr_config(
53
+ host: "localhost",
54
+ username: "root",
55
+ password: "root"
56
+ )
57
+ end
58
+ end
59
+ ```
13
60
 
14
- Libraries using Anyway Config:
61
+ Using Ruby classes to represent configuration allows you to add helper methods and computed parameters easily, makes the configuration **testable**.
15
62
 
16
- - [Influxer](https://github.com/palkan/influxer)
63
+ The `anyway_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.
17
64
 
18
- - [AnyCable](https://github.com/anycable/anycable)
65
+ Check out the libraries using Anyway Config for more examples:
19
66
 
67
+ - [Influxer](https://github.com/palkan/influxer)
68
+ - [AnyCable](https://github.com/anycable/anycable)
20
69
  - [Sniffer](https://github.com/aderyabin/sniffer)
21
-
22
70
  - [Blood Contracts](https://github.com/sclinede/blood_contracts)
23
-
24
71
  - [and others](https://github.com/palkan/anyway_config/network/dependents).
25
72
 
26
73
  ## Installation
@@ -31,7 +78,7 @@ Adding to a gem:
31
78
  # my-cool-gem.gemspec
32
79
  Gem::Specification.new do |spec|
33
80
  # ...
34
- spec.add_dependency "anyway_config", "2.0.0.pre"
81
+ spec.add_dependency "anyway_config", "2.0.0.rc1"
35
82
  # ...
36
83
  end
37
84
  ```
@@ -40,23 +87,25 @@ Or adding to your project:
40
87
 
41
88
  ```ruby
42
89
  # Gemfile
43
- gem "anyway_config", "2.0.0.pre"
90
+ gem "anyway_config", "2.0.0.rc1"
44
91
  ```
45
92
 
46
- ## Supported Ruby versions
93
+ ### Supported Ruby versions
47
94
 
48
95
  - Ruby (MRI) >= 2.5.0
49
-
50
- - JRuby >= 9.2.7
96
+ - JRuby >= 9.2.9
51
97
 
52
98
  ## Usage
53
99
 
54
- ### Pre-defined configuration
100
+ ### Configuration classes
101
+
102
+ Using configuration classes allows you to make configuration data a bit more than a bag of values:
103
+ you can define a schema for your configuration, provide defaults, add validations and additional helper methods.
55
104
 
56
- Create configuration class:
105
+ Anyway Config provides a base class to inherit from with a few DSL methods:
57
106
 
58
107
  ```ruby
59
- require "anyway"
108
+ require "anyway_config"
60
109
 
61
110
  module MyCoolGem
62
111
  class Config < Anyway::Config
@@ -65,22 +114,66 @@ module MyCoolGem
65
114
  end
66
115
  ```
67
116
 
68
- `attr_config` creates accessors and default values. If you don't need default values just write:
117
+ Here `attr_config` creates accessors and populates the default values. If you don't need default values you can write:
69
118
 
70
119
  ```ruby
71
120
  attr_config :user, :password, host: "localhost", options: {}
72
121
  ```
73
122
 
74
- Then create an instance of the config class and use it:
123
+ **NOTE**: it's safe to use non-primitive default values (like Hashes or Arrays) without worrying about their mutation: the values would be deeply duplicated for each config instance.
124
+
125
+ Then, create an instance of the config class and use it:
75
126
 
76
127
  ```ruby
77
- module MyCoolGem
78
- def self.config
79
- @config ||= Config.new
128
+ MyCoolGem::Config.new.user #=> "root"
129
+ ```
130
+
131
+ **Bonus:**: if you define attributes with boolean default values (`false` or `true`), Anyway Config would automatically add a corresponding predicate method. For example:
132
+
133
+ ```ruby
134
+ attr_config :user, :password, debug: false
135
+
136
+ MyCoolGem::Config.new.debug? #=> false
137
+ MyCoolGem::Config.new(debug: true).debug? #=> true
138
+ ```
139
+
140
+ **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:
141
+
142
+ ```ruby
143
+ class MyConfig < Anyway::Config
144
+ attr_config :host, :port, :url, :meta
145
+
146
+ # override writer to handle type coercion
147
+ def meta=(val)
148
+ super JSON.parse(val)
149
+ end
150
+
151
+ # or override reader to handle missing values
152
+ def url
153
+ super || (self.url = "#{host}:#{port}")
154
+ end
155
+
156
+ # untill v2.1, it will still be possible to read instance variables,
157
+ # i.e. the following code would also work
158
+ def url
159
+ @url ||= "#{host}:#{port}"
80
160
  end
81
161
  end
162
+ ```
163
+
164
+ We recommend to add a feature check and support both v1.x and v2.0 in gems for the time being:
82
165
 
83
- MyCoolGem.config.user #=> "root"
166
+ ```ruby
167
+ # Check for the class method added in 2.0, e.g., `.on_load`
168
+ if respond_to?(:on_load)
169
+ def url
170
+ super || (self.url = "#{host}:#{port}")
171
+ end
172
+ else
173
+ def url
174
+ @url ||= "#{host}:#{port}"
175
+ end
176
+ end
84
177
  ```
85
178
 
86
179
  #### Config name
@@ -92,9 +185,9 @@ By default, Anyway Config uses the config class name to infer the config name us
92
185
  - if the class name has a form of `<Module>::Config` then use the module name (`SomeModule::Config => "somemodule"`)
93
186
  - if the class name has a form of `<Something>Config` then use the class name prefix (`SomeConfig => "some"`)
94
187
 
95
- **NOTE:** in both cases the config name is a **downcased** module/class prefix, not underscored.
188
+ **NOTE:** in both cases, the config name is a **downcased** module/class prefix, not underscored.
96
189
 
97
- You can also specify the config name explicitly (it's required in cases when you class name doesn't match any of the patterns above):
190
+ You can also specify the config name explicitly (it's required in cases when your class name doesn't match any of the patterns above):
98
191
 
99
192
  ```ruby
100
193
  module MyCoolGem
@@ -122,7 +215,7 @@ module MyCoolGem
122
215
  end
123
216
  ```
124
217
 
125
- #### Provide explicit values
218
+ #### Explicit values
126
219
 
127
220
  Sometimes it's useful to set some parameters explicitly during config initialization.
128
221
  You can do that by passing a Hash into `.new` method:
@@ -133,17 +226,25 @@ config = MyCoolGem::Config.new(
133
226
  password: "rubyisnotdead"
134
227
  )
135
228
 
136
- # The value would not be overriden from other sources (such as YML file, env)
229
+ # The value would not be overridden from other sources (such as YML file, env)
137
230
  config.user == "john"
138
231
  ```
139
232
 
233
+ #### Reload configuration
234
+
235
+ There are `#clear` and `#reload` methods that do exactly what they state.
236
+
237
+ **NOTE**: `#reload` also accepts an optional Hash for [explicit values](#explicit-values).
238
+
140
239
  ### Dynamic configuration
141
240
 
142
- You can also create configuration objects without pre-defined schema (just like `Rails.application.config_for` but more [powerful](#railsapplicationconfig_for-vs-anywayconfigfor)):
241
+ You can also fetch configuration without pre-defined schema:
143
242
 
144
243
  ```ruby
145
- # load data from config/my_app.yml, secrets.my_app (if using Rails), ENV["MY_APP_*"]
146
- # MY_APP_VALUE=42
244
+ # load data from config/my_app.yml,
245
+ # credentials.my_app, secrets.my_app (if using Rails), ENV["MY_APP_*"]
246
+ #
247
+ # Given MY_APP_VALUE=42
147
248
  config = Anyway::Config.for(:my_app)
148
249
  config["value"] #=> 42
149
250
 
@@ -151,11 +252,75 @@ config["value"] #=> 42
151
252
  config = Anyway::Config.for(:my_app, config_path: "my_config.yml", env_prefix: "MYAPP")
152
253
  ```
153
254
 
154
- ### Using with Rails
255
+ This feature is similar to `Rails.application.config_for` but more powerful:
256
+
257
+ | Feature | Rails | Anyway Config |
258
+ | ------------- |-------------:| -----:|
259
+ | Load data from `config/app.yml` | ✅ | ✅ |
260
+ | Load data from `secrets` | ❌ | ✅ |
261
+ | Load data from `credentials` | ❌ | ✅ |
262
+ | Load data from environment | ❌ | ✅ |
263
+ | Load data from [custom sources](#data-loaders) | ❌ | ✅ |
264
+ | Local config files | ❌ | ✅ |
265
+ | [Source tracing](#tracing) | ❌ | ✅ |
266
+ | Return Hash with indifferent access | ❌ | ✅ |
267
+ | Support ERB\* within `config/app.yml` | ✅ | ✅ |
268
+ | Raise if file doesn't exist | ✅ | ❌ |
269
+ | Works without Rails | 😀 | ✅ |
270
+
271
+ \* Make sure that ERB is loaded
272
+
273
+ ### Validation and callbacks
274
+
275
+ Anyway Config provides basic ways of ensuring that the configuration is valid.
276
+
277
+ There is a built-in `required` class method to define the list of parameters that must be present in the
278
+ configuration after loading (where present means non-`nil` and non-empty for strings):
279
+
280
+ ```ruby
281
+ class MyConfig < Anyway::Config
282
+ attr_config :api_key, :api_secret, :debug
283
+
284
+ required :api_key, :api_secret
285
+ end
286
+
287
+ MyConfig.new(api_secret: "") #=> raises Anyway::Config::ValidationError
288
+ ```
289
+
290
+ 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:
291
+
292
+ ```ruby
293
+ class MyConfig < Anyway::Config
294
+ attr_config :api_key, :api_secret, :mode
295
+
296
+ # on_load macro accepts symbol method names
297
+ on_load :ensure_mode_is_valid
298
+
299
+ # or block
300
+ on_load do
301
+ # the block is evaluated in the context of the config
302
+ raise_validation_error("API key and/or secret could be blank") if
303
+ api_key.blank? || api_secret.blank?
304
+ end
305
+
306
+ def ensure_mode_is_valid
307
+ unless %w[production test].include?(mode)
308
+ raise_validation_error "Unknown mode; #{mode}"
309
+ end
310
+ end
311
+ end
312
+ ```
313
+
314
+ ## Using with Rails
155
315
 
156
316
  **NOTE:** version 2.x supports Rails >= 5.0; for Rails 4.x use version 1.x of the gem.
157
317
 
158
- Your config will be filled up with values from the following sources (ordered by priority from low to high):
318
+ We recommend going through [Data population](#data-population) and [Organizing configs](#organizing-configs) sections first,
319
+ and then use [Rails generators](#generators) to make your application Anyway Config-ready.
320
+
321
+ ### Data population
322
+
323
+ Your config is filled up with values from the following sources (ordered by priority from low to high):
159
324
 
160
325
  - `RAILS_ROOT/config/my_cool_gem.yml` (for the current `RAILS_ENV`, supports `ERB`):
161
326
 
@@ -180,7 +345,7 @@ development:
180
345
  port: 4444
181
346
  ```
182
347
 
183
- - `Rails.application.credentials` (if supported):
348
+ - `Rails.application.credentials.my_cool_gem` (if supported):
184
349
 
185
350
  ```yml
186
351
  my_cool_gem:
@@ -191,14 +356,18 @@ my_cool_gem:
191
356
 
192
357
  - `ENV['MYCOOLGEM_*']`.
193
358
 
194
- #### `app/configs`
359
+ See [environment variables](#environment-variables).
360
+
361
+ ### Organizing configs
362
+
363
+ You can store application-level config classes in `app/configs` folder just like any other Rails entities.
195
364
 
196
- You can store application-level config classes in `app/configs` folder.
365
+ However, in that case you won't be able to use them during the application initialization (i.e., in `config/**/*.rb` files).
197
366
 
198
- Anyway Config automatically adds this folder to Rails autoloading system to make it possible to
199
- autoload configs even during the configuration phase.
367
+ Since that's a pretty common scenario, we provide a way to do that via a custom autoloader for `config/configs` folder.
368
+ That means, that you can put your configuration classes into `config/configs` folder, use them anywhere in your code without explicitly requiring them.
200
369
 
201
- Consider an example: setting the Action Mailer host name for Heroku review apps.
370
+ Consider an example: setting the Action Mailer hostname for Heroku review apps.
202
371
 
203
372
  We have the following config to fetch the Heroku provided [metadata](https://devcenter.heroku.com/articles/dyno-metadata):
204
373
 
@@ -221,32 +390,108 @@ Then in `config/application.rb` you can do the following:
221
390
  config.action_mailer.default_url_options = {host: HerokuConfig.new.hostname}
222
391
  ```
223
392
 
224
- ### Using with Ruby
393
+ You can configure the configs folder path:
394
+
395
+ ```ruby
396
+ # The path must be relative to Rails root
397
+ config.anyway_config.autoload_static_config_path = "path/to/configs"
398
+ ```
399
+
400
+ **NOTE:** Configs loaded from the `autoload_static_config_path` are **not reloaded in development**. We call them _static_. So, it makes sense to keep only configs necessary for initialization in this folder. Other configs, _dynamic_, could be stored in `app/configs`.
401
+ Or you can store everything in `app/configs` by setting `config.anyway_config.autoload_static_config_path = "app/configs"`.
402
+
403
+ ### Generators
404
+
405
+ Anyway Config provides Rails generators to create new config classes:
406
+
407
+ - `rails g anyway:install`—creates an `ApplicationConfig` class (the base class for all config classes) and updates `.gitignore`
408
+
409
+ You can specify the static configs path via the `--configs-path` option:
410
+
411
+ ```sh
412
+ rails g anyway:install --configs-path=config/settings
413
+
414
+ # or to keep everything in app/configs
415
+ rails g anyway:install --configs-path=app/configs
416
+ ```
417
+
418
+ - `rails g anyway:config <name> param1 param2 ...`—creates a named configuration class and optionally the corresponding YAML file; creates `application_config.rb` is missing.
419
+
420
+ The generator command for the Heroku example above would be:
421
+
422
+ ```sh
423
+ $ rails g anyway:config heroku app_id app_name dyno_id release_version slug_commit
424
+
425
+ generate anyway:install
426
+ rails generate anyway:install
427
+ create config/configs/application_config.rb
428
+ append .gitignore
429
+ create config/configs/heroku_config.rb
430
+ Would you like to generate a heroku.yml file? (Y/n) n
431
+ ```
432
+
433
+ You can also specify the `--app` option to put the newly created class into `app/configs` folder.
434
+ Alternatively, you can call `rails g anyway:app_config name param1 param2 ...`.
225
435
 
226
- When you're using Anyway Config in non-Rails environment, we're looking for a YAML config file
227
- at `./config/<config-name>.yml`.
436
+ ## Using with Ruby
228
437
 
229
- You can override this setting through special environment variable – 'MYCOOLGEM_CONF' containing the path to the YAML file.
438
+ The default data loading mechanism for non-Rails applications is the following (ordered by priority from low to high):
230
439
 
231
- **NOTE:** in pure Ruby apps we have no knowledge of _environments_ (`test`, `development`, `production`, etc.); thus we assume that the YAML contains values for a single environment:
440
+ - `./config/<config-name>.yml` (`ERB` is supported if `erb` is loaded)
441
+
442
+ In pure Ruby apps, we do not know about _environments_ (`test`, `development`, `production`, etc.); thus, we assume that the YAML contains values for a single environment:
232
443
 
233
444
  ```yml
234
445
  host: localhost
235
446
  port: 3000
236
447
  ```
237
448
 
238
- Environmental variables work the same way as with Rails.
449
+ **NOTE:** you can override the default YML lookup path by setting `MYCOOLGEM_CONF` env variable.
450
+
451
+ - `ENV['MYCOOLGEM_*']`.
452
+
453
+ See [environment variables](#environment-variables).
454
+
455
+ ## Environment variables
456
+
457
+ Environmental variables for your config should start with your config name, upper-cased.
458
+
459
+ For example, if your config name is "mycoolgem", then the env var "MYCOOLGEM_PASSWORD" is used as `config.password`.
460
+
461
+ Environment variables are automatically type cast:
462
+
463
+ - `"True"`, `"t"` and `"yes"` to `true`;
464
+ - `"False"`, `"f"` and `"no"` to `false`;
465
+ - `"nil"` and `"null"` to `nil` (do you really need it?);
466
+ - `"123"` to 123 and `"3.14"` to 3.14.
467
+
468
+ *Anyway Config* supports nested (_hashed_) env variables—just separate keys with double-underscore.
239
469
 
240
- ### Local files
470
+ For example, "MYCOOLGEM_OPTIONS__VERBOSE" is parsed as `config.options["verbose"]`.
241
471
 
242
- It's useful to have personal, user-specific configuration in development, which extends the project-wide one.
472
+ Array values are also supported:
473
+
474
+ ```ruby
475
+ # Suppose ENV["MYCOOLGEM_IDS"] = '1,2,3'
476
+ config.ids #=> [1,2,3]
477
+ ```
478
+
479
+ If you want to provide a text-like env variable which contains commas then wrap it into quotes:
480
+
481
+ ```ruby
482
+ MYCOOLGEM = "Nif-Nif, Naf-Naf and Nouf-Nouf"
483
+ ```
484
+
485
+ ## Local files
486
+
487
+ It's useful to have a personal, user-specific configuration in development, which extends the project-wide one.
243
488
 
244
489
  We support this by looking at _local_ files when loading the configuration data:
245
490
 
246
491
  - `<config_name>.local.yml` files (next to\* the _global_ `<config_name>.yml`)
247
492
  - `config/credentials/local.yml.enc` (for Rails >= 6, generate it via `rails credentials:edit --environment local`).
248
493
 
249
- \* If the YAML config path is not default (i.e. set via `<CONFIG_NAME>_CONF`), we lookup the local
494
+ \* If the YAML config path is not a default one (i.e., set via `<CONFIG_NAME>_CONF`), we look up the local
250
495
  config at this location, too.
251
496
 
252
497
  Local configs are meant for using in development and only loaded if `Anyway::Settings.use_local_files` is `true` (which is true by default if `RACK_ENV` or `RAILS_ENV` env variable is equal to `"development"`).
@@ -255,110 +500,128 @@ Local configs are meant for using in development and only loaded if `Anyway::Set
255
500
 
256
501
  Don't forget to add `*.local.yml` (and `config/credentials/local.*`) to your `.gitignore`.
257
502
 
258
- **NOTE:** local YAML configs for Rails app must be environment-free (i.e. you shouldn't have top-level `development:` key).
259
-
260
- ### Reload configuration
503
+ **NOTE:** local YAML configs for a Rails app must be environment-free (i.e., you shouldn't have top-level `development:` key).
261
504
 
262
- There are `#clear` and `#reload` methods which do exactly what they state.
505
+ ## Data loaders
263
506
 
264
- Note: `#reload` also accepts `overrides` key to provide explicit values (see above).
265
-
266
- ### OptionParser integration
267
-
268
- It's possible to use config as option parser (e.g. for CLI apps/libraries). It uses
269
- [`optparse`](https://ruby-doc.org/stdlib-2.5.1/libdoc/optparse/rdoc/OptionParser.html) under the hood.
270
-
271
- Example usage:
507
+ You can provide your own data loaders or change the existing ones using the Loaders API (which is very similar to Rack middleware builder):
272
508
 
273
509
  ```ruby
274
- class MyConfig < Anyway::Config
275
- attr_config :host, :log_level, :concurrency, :debug, server_args: {}
510
+ # remove env loader => do not load params from ENV
511
+ Anyway.loaders.delete :env
276
512
 
277
- # specify which options shouldn't be handled by option parser
278
- ignore_options :server_args
279
-
280
- # provide description for options
281
- describe_options(
282
- concurrency: "number of threads to use"
283
- )
513
+ # add custom loader before :env (it's better to keep the ENV loader the last one)
514
+ Anyway.loaders.insert_before :env, :my_loader, MyLoader
515
+ ```
284
516
 
285
- # mark some options as flag
286
- flag_options :debug
517
+ Loader is a _callable_ Ruby object (module/class responding to `.call` or lambda/proc), which `call` method
518
+ accepts the following keyword arguments:
287
519
 
288
- # extend an option parser object (i.e. add banner or version/help handlers)
289
- extend_options do |parser, config|
290
- parser.banner = "mycli [options]"
520
+ ```ruby
521
+ def call(
522
+ name:, # config name
523
+ env_prefix:, # prefix for env vars if any
524
+ config_path:, # path to YML config
525
+ local: # true|false, whether to load local configuration
526
+ )
527
+ #=> must return Hash with configuration data
528
+ end
529
+ ```
291
530
 
292
- parser.on("--server-args VALUE") do |value|
293
- config.server_args = JSON.parse(value)
294
- end
531
+ You can use `Anyway::Loaders::Base` as a base class for your loader and define a `#call` method.
532
+ For example, the [Chamber](https://github.com/thekompanee/chamber) loader could be written as follows:
295
533
 
296
- parser.on_tail "-h", "--help" do
297
- puts parser
298
- end
534
+ ```ruby
535
+ class ChamberConfigLoader < Anyway::Loaders::Base
536
+ def call(name:, **_opts)
537
+ Chamber.env.to_h[name] || {}
299
538
  end
300
539
  end
540
+ ```
301
541
 
302
- config = MyConfig.new
303
-
304
- config.parse_options!(%w[--host localhost --port 3333 --log-level debug])
305
-
306
- config.host # => "localhost"
307
- config.port # => 3333
308
- config.log_level # => "debug"
542
+ In order to support [source tracing](#tracing), you need to wrap the resulting Hash via the `#trace!` method with metadata:
309
543
 
310
- # Get the instance of OptionParser
311
- config.option_parser
544
+ ```ruby
545
+ def call(name:, **_opts)
546
+ trace!(source: :chamber) do
547
+ Chamber.env.to_h[name] || {}
548
+ end
549
+ end
312
550
  ```
313
551
 
314
- ## `Rails.application.config_for` vs `Anyway::Config.for`
315
-
316
- Rails 4.2 introduced new feature: `Rails.application.config_for`. It looks very similar to
317
- `Anyway::Config.for`, but there are some differences:
552
+ ## Tracing
318
553
 
319
- | Feature | Rails | Anyway Config |
320
- | ------------- |-------------:| -----:|
321
- | load data from `config/app.yml` | yes | yes |
322
- | load data from `secrets` | no | yes |
323
- | load data from `credentials` | no | yes |
324
- | load data from environment | no | yes |
325
- | local config files | no | yes |
326
- | return Hash with indifferent access | no | yes |
327
- | support ERB within `config/app.yml` | yes | yes* |
328
- | raise errors if file doesn't exist | yes | no |
554
+ Since Anyway Config loads data from multiple source, it could be useful to know where a particular value came from.
329
555
 
330
- <sub><sup>*</sup>make sure that ERB is loaded</sub>
556
+ Each `Anyway::Config` instance contains _tracing information_ which you can access via `#to_source_trace` method:
331
557
 
332
- But the main advantage of Anyway::Config is that it can be used [without Rails](#using-with-ruby)!)
558
+ ```ruby
559
+ conf = ExampleConfig.new
560
+ conf.to_source_trace
561
+
562
+ # returns the following hash
563
+ {
564
+ "host" => {value: "test.host", source: {type: :yml, path: "config/example.yml"}},
565
+ "user" => {
566
+ "name" => {value: "john", source: {type: :env, key: "EXAMPLE_USER__NAME"}},
567
+ "password" => {value: "root", source: {type: :credentials, store: "config/credentials/production.enc.yml"}}
568
+ },
569
+ "port" => {value: 9292, source: {type: :defaults}}
570
+ }
571
+
572
+ # if you change the value manually in your code,
573
+ # that would be reflected in the trace
574
+
575
+ conf.host = "anyway.host"
576
+ conf.to_source_trace["host"]
577
+ #=> {type: :user, called_from: "/path/to/caller.rb:15"}
578
+ ```
333
579
 
334
- ## How to set env vars
580
+ You can disable tracing functionality by setting `Anyway::Settings.tracing_enabled = false` or `config.anyway_config.tracing_enabled = false` in Rails.
335
581
 
336
- Environmental variables for your config should start with your config name, upper-cased.
582
+ ### Pretty print
337
583
 
338
- For example, if your config name is "mycoolgem" then the env var "MYCOOLGEM_PASSWORD" is used as `config.password`.
584
+ You can use `pp` to print a formatted information about the config including the sources trace.
339
585
 
340
- Environment variables are automatically serialized:
586
+ Example:
341
587
 
342
- - `"True"`, `"t"` and `"yes"` to `true`;
343
- - `"False"`, `"f"` and `"no"` to `false`;
344
- - `"nil"` and `"null"` to `nil` (do you really need it?);
345
- - `"123"` to 123 and `"3.14"` to 3.14.
588
+ ```ruby
589
+ pp CoolConfig.new
590
+
591
+ # #<CoolConfig
592
+ # config_name="cool"
593
+ # env_prefix="COOL"
594
+ # values:
595
+ # port => 3334 (type=load),
596
+ # host => "test.host" (type=yml path=./config/cool.yml),
597
+ # user =>
598
+ # name => "john" (type=env key=COOL_USER__NAME),
599
+ # password => "root" (type=yml path=./config/cool.yml)>
600
+ ```
346
601
 
347
- *Anyway Config* supports nested (_hashed_) env variables. Just separate keys with double-underscore.
602
+ ## Pattern matching
348
603
 
349
- For example, "MYCOOLGEM_OPTIONS__VERBOSE" is parsed as `config.options["verbose"]`.
350
-
351
- Array values are also supported:
604
+ You can use config instances in Ruby 2.7+ pattern matching:
352
605
 
353
606
  ```ruby
354
- # Suppose ENV["MYCOOLGEM_IDS"] = '1,2,3'
355
- config.ids #=> [1,2,3]
607
+ case AWSConfig.new
608
+ in bucket:, region: "eu-west-1"
609
+ setup_eu_storage(bucket)
610
+ in bucket:, region: "us-east-1"
611
+ setup_us_storage(bucket)
612
+ end
356
613
  ```
357
614
 
358
- If you want to provide a text-like env variable which contains commas then wrap it into quotes:
615
+ If the attribute wasn't populated, the key won't be returned for pattern matching, i.e. you can do something line:
359
616
 
360
617
  ```ruby
361
- MYCOOLGEM = "Nif-Nif, Naf-Naf and Nouf-Nouf"
618
+ aws_configured =
619
+ case AWSConfig.new
620
+ in access_key_id:, secret_access_key:
621
+ true
622
+ else
623
+ false
624
+ end
362
625
  ```
363
626
 
364
627
  ## Test helpers
@@ -398,6 +661,68 @@ This helper is automatically included to RSpec if `RAILS_ENV` or `RACK_ENV` env
398
661
 
399
662
  You can add it manually by requiring `"anyway/testing/helpers"` and including the `Anyway::Test::Helpers` module (into RSpec configuration or Minitest test class).
400
663
 
664
+ ## OptionParser integration
665
+
666
+ It's possible to use config as option parser (e.g., for CLI apps/libraries). It uses
667
+ [`optparse`](https://ruby-doc.org/stdlib-2.5.1/libdoc/optparse/rdoc/OptionParser.html) under the hood.
668
+
669
+ Example usage:
670
+
671
+ ```ruby
672
+ class MyConfig < Anyway::Config
673
+ attr_config :host, :log_level, :concurrency, :debug, server_args: {}
674
+
675
+ # specify which options shouldn't be handled by option parser
676
+ ignore_options :server_args
677
+
678
+ # provide description for options
679
+ describe_options(
680
+ concurrency: "number of threads to use"
681
+ )
682
+
683
+ # mark some options as flag
684
+ flag_options :debug
685
+
686
+ # extend an option parser object (i.e. add banner or version/help handlers)
687
+ extend_options do |parser, config|
688
+ parser.banner = "mycli [options]"
689
+
690
+ parser.on("--server-args VALUE") do |value|
691
+ config.server_args = JSON.parse(value)
692
+ end
693
+
694
+ parser.on_tail "-h", "--help" do
695
+ puts parser
696
+ end
697
+ end
698
+ end
699
+
700
+ config = MyConfig.new
701
+
702
+ config.parse_options!(%w[--host localhost --port 3333 --log-level debug])
703
+
704
+ config.host # => "localhost"
705
+ config.port # => 3333
706
+ config.log_level # => "debug"
707
+
708
+ # Get the instance of OptionParser
709
+ config.option_parser
710
+ ```
711
+
712
+ **NOTE:** values are automatically type cast using the same rules as for [environment variables](#environment-variables).
713
+ If you want to specify the type explicitly, you can do that using `describe_options`:
714
+
715
+ ```ruby
716
+ describe_options(
717
+ # In this case, you should specify a hash with `type`
718
+ # and (optionally) `desc` keys
719
+ concurrency: {
720
+ desc: "number of threads to use",
721
+ type: String
722
+ }
723
+ )
724
+ ```
725
+
401
726
  ## Contributing
402
727
 
403
728
  Bug reports and pull requests are welcome on GitHub at [https://github.com/palkan/anyway_config](https://github.com/palkan/anyway_config).