anyway_config 2.0.5 → 2.2.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +241 -181
- data/README.md +238 -13
- data/lib/.rbnext/1995.next/anyway/config.rb +438 -0
- data/lib/.rbnext/1995.next/anyway/dynamic_config.rb +31 -0
- data/lib/.rbnext/1995.next/anyway/env.rb +56 -0
- data/lib/.rbnext/1995.next/anyway/loaders/base.rb +21 -0
- data/lib/.rbnext/1995.next/anyway/tracing.rb +181 -0
- data/lib/.rbnext/2.7/anyway/auto_cast.rb +39 -19
- data/lib/.rbnext/2.7/anyway/config.rb +61 -16
- data/lib/.rbnext/2.7/anyway/rails/loaders/yaml.rb +30 -0
- data/lib/.rbnext/2.7/anyway/rbs.rb +92 -0
- data/lib/.rbnext/2.7/anyway/settings.rb +79 -0
- data/lib/.rbnext/2.7/anyway/tracing.rb +6 -6
- data/lib/.rbnext/2.7/anyway/type_casting.rb +143 -0
- data/lib/.rbnext/3.0/anyway/auto_cast.rb +53 -0
- data/lib/.rbnext/{2.8 → 3.0}/anyway/config.rb +61 -16
- data/lib/.rbnext/{2.8 → 3.0}/anyway/loaders/base.rb +0 -0
- data/lib/.rbnext/{2.8 → 3.0}/anyway/loaders.rb +0 -0
- data/lib/.rbnext/{2.8 → 3.0}/anyway/tracing.rb +6 -6
- data/lib/anyway/auto_cast.rb +39 -19
- data/lib/anyway/config.rb +75 -30
- data/lib/anyway/dynamic_config.rb +6 -2
- data/lib/anyway/env.rb +1 -1
- data/lib/anyway/ext/deep_dup.rb +12 -0
- data/lib/anyway/ext/hash.rb +10 -12
- data/lib/anyway/loaders/base.rb +1 -1
- data/lib/anyway/loaders/env.rb +3 -1
- data/lib/anyway/loaders/yaml.rb +9 -5
- data/lib/anyway/option_parser_builder.rb +1 -3
- data/lib/anyway/optparse_config.rb +5 -7
- data/lib/anyway/rails/loaders/credentials.rb +4 -4
- data/lib/anyway/rails/loaders/secrets.rb +6 -8
- data/lib/anyway/rails/loaders/yaml.rb +11 -0
- data/lib/anyway/rails/settings.rb +9 -2
- data/lib/anyway/rbs.rb +92 -0
- data/lib/anyway/settings.rb +52 -2
- data/lib/anyway/tracing.rb +9 -9
- data/lib/anyway/type_casting.rb +134 -0
- data/lib/anyway/utils/deep_merge.rb +21 -0
- data/lib/anyway/version.rb +1 -1
- data/lib/anyway_config.rb +4 -0
- data/sig/anyway_config.rbs +129 -0
- metadata +42 -15
- data/lib/.rbnext/2.7/anyway/option_parser_builder.rb +0 -31
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[](
|
1
|
+
[](https://cultofmartians.com/tasks/anyway-config-options-parse.html#task)
|
2
2
|
[](https://rubygems.org/gems/anyway_config) [](https://github.com/palkan/anyway_config/actions)
|
3
3
|
[](https://github.com/palkan/anyway_config/actions)
|
4
4
|
|
@@ -42,12 +42,14 @@ For version 1.x see the [1-4-stable branch](https://github.com/palkan/anyway_con
|
|
42
42
|
- [Generators](#generators)
|
43
43
|
- [Using with Ruby applications](#using-with-ruby)
|
44
44
|
- [Environment variables](#environment-variables)
|
45
|
+
- [Type coercion](#type-coercion)
|
45
46
|
- [Local configuration](#local-files)
|
46
47
|
- [Data loaders](#data-loaders)
|
47
48
|
- [Source tracing](#tracing)
|
48
49
|
- [Pattern matching](#pattern-matching)
|
49
50
|
- [Test helpers](#test-helpers)
|
50
51
|
- [OptionParser integration](#optionparser-integration)
|
52
|
+
- [RBS support](#rbs-support)
|
51
53
|
|
52
54
|
## Main concepts
|
53
55
|
|
@@ -94,7 +96,7 @@ Or adding to your project:
|
|
94
96
|
|
95
97
|
```ruby
|
96
98
|
# Gemfile
|
97
|
-
gem "anyway_config", "~> 2.0
|
99
|
+
gem "anyway_config", "~> 2.0"
|
98
100
|
```
|
99
101
|
|
100
102
|
### Supported Ruby versions
|
@@ -269,6 +271,7 @@ This feature is similar to `Rails.application.config_for` but more powerful:
|
|
269
271
|
| Load data from environment | ❌ | ✅ |
|
270
272
|
| Load data from [custom sources](#data-loaders) | ❌ | ✅ |
|
271
273
|
| Local config files | ❌ | ✅ |
|
274
|
+
| Type coercion | ❌ | ✅ |
|
272
275
|
| [Source tracing](#tracing) | ❌ | ✅ |
|
273
276
|
| Return Hash with indifferent access | ❌ | ✅ |
|
274
277
|
| Support ERB\* within `config/app.yml` | ✅ | ✅ |
|
@@ -329,7 +332,9 @@ and then use [Rails generators](#generators) to make your application Anyway Con
|
|
329
332
|
|
330
333
|
Your config is filled up with values from the following sources (ordered by priority from low to high):
|
331
334
|
|
332
|
-
|
335
|
+
1) **YAML configuration files**: `RAILS_ROOT/config/my_cool_gem.yml`.
|
336
|
+
|
337
|
+
Rails environment is used as the namespace (required); supports `ERB`:
|
333
338
|
|
334
339
|
```yml
|
335
340
|
test:
|
@@ -341,9 +346,51 @@ development:
|
|
341
346
|
port: 3000
|
342
347
|
```
|
343
348
|
|
344
|
-
|
349
|
+
### Multi-env configuration
|
350
|
+
|
351
|
+
_⚡️ This feature will be turned on by default in the future releases. You can turn it on now via `config.anyway_config.future.use :unwrap_known_environments`._
|
345
352
|
|
346
|
-
|
353
|
+
If the YML does not have keys that are one of the "known" Rails environments (development, production, test)—the same configuration will be available in all environments, similar to non-Rails behavior:
|
354
|
+
|
355
|
+
```yml
|
356
|
+
host: localhost
|
357
|
+
port: 3002
|
358
|
+
# These values will be active in all environments
|
359
|
+
```
|
360
|
+
|
361
|
+
To extend the list of known environments, use the setting in the relevant part of your Rails code:
|
362
|
+
|
363
|
+
```ruby
|
364
|
+
Rails.application.config.anyway_config.known_environments << "staging"
|
365
|
+
```
|
366
|
+
|
367
|
+
If your YML defines at least a single "environmental" top-level, you _have_ to separate all your settings per-environment. You can't mix and match:
|
368
|
+
|
369
|
+
```yml
|
370
|
+
staging:
|
371
|
+
host: localhost # This value will be loaded when Rails.env.staging? is true
|
372
|
+
|
373
|
+
port: 3002 # This value will not be loaded at all
|
374
|
+
```
|
375
|
+
|
376
|
+
You can specify the lookup path for YAML files in one of the following ways:
|
377
|
+
|
378
|
+
- By setting `config.anyway_config.default_config_path` to a target directory path:
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
config.anyway_config.default_config_path = "/etc/configs"
|
382
|
+
config.anyway_config.default_config_path = Rails.root.join("etc", "configs")
|
383
|
+
```
|
384
|
+
|
385
|
+
- By setting `config.anyway_config.default_config_path` to a Proc, which accepts a config name and returns the path:
|
386
|
+
|
387
|
+
```ruby
|
388
|
+
config.anyway_config.default_config_path = ->(name) { Rails.root.join("data", "configs", "#{name}.yml") }
|
389
|
+
```
|
390
|
+
|
391
|
+
- By overriding a specific config YML file path via the `<NAME>_CONF` env variable, e.g., `MYCOOLGEM_CONF=path/to/cool.yml`
|
392
|
+
|
393
|
+
2) **Rails secrets**: `Rails.application.secrets.my_cool_gem` (if `secrets.yml` present).
|
347
394
|
|
348
395
|
```yml
|
349
396
|
# config/secrets.yml
|
@@ -352,7 +399,7 @@ development:
|
|
352
399
|
port: 4444
|
353
400
|
```
|
354
401
|
|
355
|
-
|
402
|
+
3) **Rails credentials**: `Rails.application.credentials.my_cool_gem` (if supported):
|
356
403
|
|
357
404
|
```yml
|
358
405
|
my_cool_gem:
|
@@ -361,7 +408,7 @@ my_cool_gem:
|
|
361
408
|
|
362
409
|
**NOTE:** You can backport Rails 6 per-environment credentials to Rails 5.2 app using [this patch](https://gist.github.com/palkan/e27e4885535ff25753aefce45378e0cb).
|
363
410
|
|
364
|
-
|
411
|
+
4) **Environment variables**: `ENV['MYCOOLGEM_*']`.
|
365
412
|
|
366
413
|
See [environment variables](#environment-variables).
|
367
414
|
|
@@ -407,6 +454,22 @@ config.anyway_config.autoload_static_config_path = "path/to/configs"
|
|
407
454
|
**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`.
|
408
455
|
Or you can store everything in `app/configs` by setting `config.anyway_config.autoload_static_config_path = "app/configs"`.
|
409
456
|
|
457
|
+
**NOTE 2**: Since _static_ configs are loaded before initializers, it's not possible to use custom inflection Rules (usually defined in `config/initializers/inflections.rb`) to resolve constant names from files. If you rely on custom inflection rules (see, for example, [#81](https://github.com/palkan/anyway_config/issues/81)), we recommend configuration Rails inflector before initialization as well:
|
458
|
+
|
459
|
+
```ruby
|
460
|
+
# config/application.rb
|
461
|
+
|
462
|
+
# ...
|
463
|
+
|
464
|
+
require_relative "initializers/inflections"
|
465
|
+
|
466
|
+
module SomeApp
|
467
|
+
class Application < Rails::Application
|
468
|
+
# ...
|
469
|
+
end
|
470
|
+
end
|
471
|
+
```
|
472
|
+
|
410
473
|
### Generators
|
411
474
|
|
412
475
|
Anyway Config provides Rails generators to create new config classes:
|
@@ -444,7 +507,7 @@ Alternatively, you can call `rails g anyway:app_config name param1 param2 ...`.
|
|
444
507
|
|
445
508
|
The default data loading mechanism for non-Rails applications is the following (ordered by priority from low to high):
|
446
509
|
|
447
|
-
|
510
|
+
1) **YAML configuration files**: `./config/<config-name>.yml`.
|
448
511
|
|
449
512
|
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:
|
450
513
|
|
@@ -453,9 +516,25 @@ host: localhost
|
|
453
516
|
port: 3000
|
454
517
|
```
|
455
518
|
|
456
|
-
|
519
|
+
`ERB` is supported if `erb` is loaded (thus, you need to call `require "erb"` somewhere before loading configuration).
|
520
|
+
|
521
|
+
You can specify the lookup path for YAML files in one of the following ways:
|
522
|
+
|
523
|
+
- By setting `Anyway::Settings.default_config_path` to a target directory path:
|
524
|
+
|
525
|
+
```ruby
|
526
|
+
Anyway::Settings.default_config_path = "/etc/configs"
|
527
|
+
```
|
528
|
+
|
529
|
+
- By setting `Anyway::Settings.default_config_path` to a Proc, which accepts a config name and returns the path:
|
530
|
+
|
531
|
+
```ruby
|
532
|
+
Anyway::Settings.default_config_path = ->(name) { Rails.root.join("data", "configs", "#{name}.yml") }
|
533
|
+
```
|
457
534
|
|
458
|
-
- `
|
535
|
+
- By overriding a specific config YML file path via the `<NAME>_CONF` env variable, e.g., `MYCOOLGEM_CONF=path/to/cool.yml`
|
536
|
+
|
537
|
+
2) **Environment variables**: `ENV['MYCOOLGEM_*']`.
|
459
538
|
|
460
539
|
See [environment variables](#environment-variables).
|
461
540
|
|
@@ -465,13 +544,15 @@ Environmental variables for your config should start with your config name, uppe
|
|
465
544
|
|
466
545
|
For example, if your config name is "mycoolgem", then the env var "MYCOOLGEM_PASSWORD" is used as `config.password`.
|
467
546
|
|
468
|
-
|
547
|
+
By default, environment variables are automatically type cast\*:
|
469
548
|
|
470
549
|
- `"True"`, `"t"` and `"yes"` to `true`;
|
471
550
|
- `"False"`, `"f"` and `"no"` to `false`;
|
472
551
|
- `"nil"` and `"null"` to `nil` (do you really need it?);
|
473
552
|
- `"123"` to 123 and `"3.14"` to 3.14.
|
474
553
|
|
554
|
+
\* See below for coercion customization.
|
555
|
+
|
475
556
|
*Anyway Config* supports nested (_hashed_) env variables—just separate keys with double-underscore.
|
476
557
|
|
477
558
|
For example, "MYCOOLGEM_OPTIONS__VERBOSE" is parsed as `config.options["verbose"]`.
|
@@ -489,6 +570,94 @@ If you want to provide a text-like env variable which contains commas then wrap
|
|
489
570
|
MYCOOLGEM = "Nif-Nif, Naf-Naf and Nouf-Nouf"
|
490
571
|
```
|
491
572
|
|
573
|
+
## Type coercion
|
574
|
+
|
575
|
+
> 🆕 v2.2.0
|
576
|
+
|
577
|
+
You can define custom type coercion rules to convert string data to config values. To do that, use `.coerce_types` method:
|
578
|
+
|
579
|
+
```ruby
|
580
|
+
class CoolConfig < Anyway::Config
|
581
|
+
config_name :cool
|
582
|
+
attr_config port: 8080,
|
583
|
+
host: "localhost",
|
584
|
+
user: {name: "admin", password: "admin"}
|
585
|
+
|
586
|
+
coerce_types port: :string, user: {dob: :date}
|
587
|
+
end
|
588
|
+
|
589
|
+
ENV["COOL_USER__DOB"] = "1989-07-01"
|
590
|
+
|
591
|
+
config = CoolConfig.new
|
592
|
+
config.port == "8080" # Even though we defined the default value as int, it's converted into a string
|
593
|
+
config.user["dob"] == Date.new(1989, 7, 1) #=> true
|
594
|
+
```
|
595
|
+
|
596
|
+
Type coercion is especially useful to deal with array values:
|
597
|
+
|
598
|
+
```ruby
|
599
|
+
# To define an array type, provide a hash with two keys:
|
600
|
+
# - type — elements type
|
601
|
+
# - array: true — mark the parameter as array
|
602
|
+
coerce_types list: {type: :string, array: true}
|
603
|
+
```
|
604
|
+
|
605
|
+
You can use `type: nil` in case you don't want to coerce values, just convert a value into an array:
|
606
|
+
|
607
|
+
```ruby
|
608
|
+
# From AnyCable config (sentinels could be represented via strings or hashes)
|
609
|
+
coerce_types redis_sentinels: {type: nil, array: true}
|
610
|
+
```
|
611
|
+
|
612
|
+
It's also could be useful to explicitly define non-array types (to avoid confusion):
|
613
|
+
|
614
|
+
```ruby
|
615
|
+
coerce_types non_list: :string
|
616
|
+
```
|
617
|
+
|
618
|
+
Finally, it's possible to disable auto-casting for a particular config completely:
|
619
|
+
|
620
|
+
```ruby
|
621
|
+
class CoolConfig < Anyway::Config
|
622
|
+
attr_config port: 8080,
|
623
|
+
host: "localhost",
|
624
|
+
user: {name: "admin", password: "admin"}
|
625
|
+
|
626
|
+
disable_auto_cast!
|
627
|
+
end
|
628
|
+
|
629
|
+
ENV["COOL_PORT"] = "443"
|
630
|
+
|
631
|
+
CoolConfig.new.port == "443" #=> true
|
632
|
+
```
|
633
|
+
|
634
|
+
**IMPORTANT**: Values provided explicitly (via attribute writers) are not coerced. Coercion is only happening during the load phase.
|
635
|
+
|
636
|
+
The following types are supported out-of-the-box: `:string`, `:integer`, `:float`, `:date`, `:datetime`, `:uri`, `:boolean`.
|
637
|
+
|
638
|
+
You can use custom deserializers by passing a callable object instead of a type name:
|
639
|
+
|
640
|
+
```ruby
|
641
|
+
COLOR_TO_HEX = lambda do |raw|
|
642
|
+
case raw
|
643
|
+
when "red"
|
644
|
+
"#ff0000"
|
645
|
+
when "green"
|
646
|
+
"#00ff00"
|
647
|
+
when "blue"
|
648
|
+
"#0000ff"
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
class CoolConfig < Anyway::Config
|
653
|
+
attr_config :color
|
654
|
+
|
655
|
+
coerce_types color: COLOR_TO_HEX
|
656
|
+
end
|
657
|
+
|
658
|
+
CoolConfig.new({color: "red"}).color #=> "#ff0000"
|
659
|
+
```
|
660
|
+
|
492
661
|
## Local files
|
493
662
|
|
494
663
|
It's useful to have a personal, user-specific configuration in development, which extends the project-wide one.
|
@@ -550,7 +719,7 @@ In order to support [source tracing](#tracing), you need to wrap the resulting H
|
|
550
719
|
|
551
720
|
```ruby
|
552
721
|
def call(name:, **_opts)
|
553
|
-
trace!(
|
722
|
+
trace!(:chamber) do
|
554
723
|
Chamber.env.to_h[name] || {}
|
555
724
|
end
|
556
725
|
end
|
@@ -666,7 +835,7 @@ If you want to delete the env var, pass `nil` as the value.
|
|
666
835
|
|
667
836
|
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/...`.
|
668
837
|
|
669
|
-
You can add it manually by requiring `"anyway/testing/helpers"` and including the `Anyway::
|
838
|
+
You can add it manually by requiring `"anyway/testing/helpers"` and including the `Anyway::Testing::Helpers` module (into RSpec configuration or Minitest test class).
|
670
839
|
|
671
840
|
## OptionParser integration
|
672
841
|
|
@@ -730,6 +899,62 @@ describe_options(
|
|
730
899
|
)
|
731
900
|
```
|
732
901
|
|
902
|
+
## RBS support
|
903
|
+
|
904
|
+
Anyway Config comes with Ruby type signatures (RBS).
|
905
|
+
|
906
|
+
To use them with Steep, add `library "anyway_config"` to your Steepfile.
|
907
|
+
|
908
|
+
We also provide an API to generate a type signature for your config class:
|
909
|
+
|
910
|
+
```ruby
|
911
|
+
class MyGem::Config < Anyway::Config
|
912
|
+
attr_config :host, port: 8080, tags: [], debug: false
|
913
|
+
|
914
|
+
coerce_types host: :string, port: :integer,
|
915
|
+
tags: {type: :string, array: true}
|
916
|
+
|
917
|
+
required :host
|
918
|
+
end
|
919
|
+
```
|
920
|
+
|
921
|
+
Then calling `MyGem::Config.to_rbs` will return the following signature:
|
922
|
+
|
923
|
+
```rbs
|
924
|
+
module MyGem
|
925
|
+
interface _Config
|
926
|
+
def host: () -> String
|
927
|
+
def host=: (String) -> void
|
928
|
+
def port: () -> String?
|
929
|
+
def port=: (String) -> void
|
930
|
+
def tags: () -> Array[String]?
|
931
|
+
def tags=: (Array[String]) -> void
|
932
|
+
def debug: () -> bool
|
933
|
+
def debug?: () -> bool
|
934
|
+
def debug=: (bool) -> void
|
935
|
+
end
|
936
|
+
|
937
|
+
class Config < Anyway::Config
|
938
|
+
include _Config
|
939
|
+
end
|
940
|
+
end
|
941
|
+
```
|
942
|
+
|
943
|
+
### Handling `on_load`
|
944
|
+
|
945
|
+
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:
|
946
|
+
|
947
|
+
```ruby
|
948
|
+
class MyConfig < Anyway::Config
|
949
|
+
on_load do
|
950
|
+
# @type self : MyConfig
|
951
|
+
raise_validation_error("host is invalid") if host.start_with?("localhost")
|
952
|
+
end
|
953
|
+
end
|
954
|
+
```
|
955
|
+
|
956
|
+
Yeah, a lot of annotations 😞 Welcome to the type-safe world!
|
957
|
+
|
733
958
|
## Contributing
|
734
959
|
|
735
960
|
Bug reports and pull requests are welcome on GitHub at [https://github.com/palkan/anyway_config](https://github.com/palkan/anyway_config).
|