anyway_config 2.0.5 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](
|
1
|
+
[![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/anyway-config-options-parse.html#task)
|
2
2
|
[![Gem Version](https://badge.fury.io/rb/anyway_config.svg)](https://rubygems.org/gems/anyway_config) [![Build](https://github.com/palkan/anyway_config/workflows/Build/badge.svg)](https://github.com/palkan/anyway_config/actions)
|
3
3
|
[![JRuby Build](https://github.com/palkan/anyway_config/workflows/JRuby%20Build/badge.svg)](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).
|