qonfig 0.16.0 → 0.17.0
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/.rubocop.yml +5 -1
- data/.travis.yml +6 -6
- data/CHANGELOG.md +17 -0
- data/README.md +630 -49
- data/gemfiles/with_external_deps.gemfile +2 -2
- data/lib/qonfig/commands/{add_nested_option.rb → definition/add_nested_option.rb} +1 -1
- data/lib/qonfig/commands/{add_option.rb → definition/add_option.rb} +1 -1
- data/lib/qonfig/commands/{compose.rb → definition/compose.rb} +1 -1
- data/lib/qonfig/commands/{expose_json.rb → definition/expose_json.rb} +1 -1
- data/lib/qonfig/commands/{expose_self.rb → definition/expose_self.rb} +1 -1
- data/lib/qonfig/commands/{expose_yaml.rb → definition/expose_yaml.rb} +1 -1
- data/lib/qonfig/commands/definition/load_from_env/value_converter.rb +82 -0
- data/lib/qonfig/commands/{load_from_env.rb → definition/load_from_env.rb} +1 -1
- data/lib/qonfig/commands/{load_from_json.rb → definition/load_from_json.rb} +1 -1
- data/lib/qonfig/commands/{load_from_self.rb → definition/load_from_self.rb} +1 -1
- data/lib/qonfig/commands/{load_from_yaml.rb → definition/load_from_yaml.rb} +1 -1
- data/lib/qonfig/commands/definition.rb +16 -0
- data/lib/qonfig/commands/instantiation/values_file.rb +171 -0
- data/lib/qonfig/commands/instantiation.rb +7 -0
- data/lib/qonfig/commands.rb +2 -10
- data/lib/qonfig/data_set/lock.rb +61 -4
- data/lib/qonfig/data_set.rb +151 -3
- data/lib/qonfig/dsl.rb +56 -16
- data/lib/qonfig/errors.rb +38 -11
- data/lib/qonfig/loaders/dynamic.rb +52 -0
- data/lib/qonfig/loaders/json.rb +6 -0
- data/lib/qonfig/loaders/yaml.rb +13 -0
- data/lib/qonfig/loaders.rb +3 -0
- data/lib/qonfig/plugins/toml/data_set.rb +13 -0
- data/lib/qonfig/plugins/toml/dsl.rb +6 -2
- data/lib/qonfig/plugins/toml/errors.rb +12 -0
- data/lib/qonfig/plugins/toml/loaders/dynamic.rb +31 -0
- data/lib/qonfig/plugins/toml/loaders/toml.rb +6 -0
- data/lib/qonfig/plugins/toml.rb +2 -0
- data/lib/qonfig/settings/builder.rb +1 -1
- data/lib/qonfig/settings.rb +21 -0
- data/lib/qonfig/validator/basic.rb +9 -1
- data/lib/qonfig/validator/builder/attribute_consistency.rb +29 -0
- data/lib/qonfig/validator/builder.rb +39 -14
- data/lib/qonfig/validator/dsl.rb +9 -1
- data/lib/qonfig/validator/method_based.rb +4 -2
- data/lib/qonfig/validator/predefined/common.rb +4 -2
- data/lib/qonfig/validator/predefined/registry.rb +0 -2
- data/lib/qonfig/validator/predefined/registry_control_mixin.rb +3 -2
- data/lib/qonfig/validator/proc_based.rb +4 -2
- data/lib/qonfig/version.rb +1 -1
- data/qonfig.gemspec +1 -1
- metadata +21 -15
- data/lib/qonfig/commands/load_from_env/value_converter.rb +0 -84
data/README.md
CHANGED
@@ -25,12 +25,18 @@ require 'qonfig'
|
|
25
25
|
- [Definition](#definition)
|
26
26
|
- [Definition and Settings Access](#definition-and-access)
|
27
27
|
- [access via method](#access-via-method)
|
28
|
-
- [index-method](#index-method)
|
28
|
+
- [access via index-method \[\]](#access-via-index-method-)
|
29
29
|
- [.dig](#dig)
|
30
30
|
- [.slice](#slice)
|
31
31
|
- [.slice_value](#slice_value)
|
32
32
|
- [.subset](#subset)
|
33
33
|
- [Configuration](#configuration)
|
34
|
+
- [configure via proc](#configure-via-proc)
|
35
|
+
- [configure via settings object (by option name)](#configure-via-settings-object-by-option-name)
|
36
|
+
- [configure via settings object (by setting key)](#configure-via-settings-object-by-setting-key)
|
37
|
+
- [instant configuration via proc](#instant-configuration-via-proc)
|
38
|
+
- [using a hash](#using-a-hash)
|
39
|
+
- [using both hash and proc](#using-both-hash-and-proc-proc-has-higher-priority)
|
34
40
|
- [Inheritance](#inheritance)
|
35
41
|
- [Composition](#composition)
|
36
42
|
- [Hash representation](#hash-representation)
|
@@ -39,25 +45,35 @@ require 'qonfig'
|
|
39
45
|
- [Interaction](#interaction)
|
40
46
|
- [Iteration over setting keys](#iteration-over-setting-keys) (`#each_setting`, `#deep_each_setting`)
|
41
47
|
- [Config reloading](#config-reloading) (reload config definitions and option values)
|
42
|
-
- [Clear options](#clear-options) (set to nil)
|
48
|
+
- [Clear options](#clear-options) (set to `nil`)
|
43
49
|
- [State freeze](#state-freeze)
|
44
50
|
- [Settings as Predicates](#settings-as-predicates)
|
51
|
+
- [Setting key existence](#setting-key-existence) (`#key?`/`#option?`/`#setting?`)
|
52
|
+
- [Run arbitary code with temporary settings](#run-arbitary-code-with-temporary-settings) (`#with(configs = {}, &arbitary_code)`)
|
45
53
|
- [Validation](#validation)
|
46
|
-
- [Introduction](#
|
54
|
+
- [Introduction](#introduction)
|
47
55
|
- [Key search pattern](#key-search-pattern)
|
48
56
|
- [Proc-based validation](#proc-based-validation)
|
49
57
|
- [Method-based validation](#method-based-validation)
|
50
58
|
- [Predefined validations](#predefined-validations)
|
51
59
|
- [Work with files](#work-with-files)
|
52
|
-
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
-
|
60
|
+
- **Setting keys definition**
|
61
|
+
- [Load from YAML file](#load-from-yaml-file)
|
62
|
+
- [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
|
63
|
+
- [Load from JSON file](#load-from-json-file)
|
64
|
+
- [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)
|
65
|
+
- [Load from ENV](#load-from-env)
|
66
|
+
- [Load from \_\_END\_\_](#load-from-__end__) (aka `.load_from_self`)
|
67
|
+
- [Expose \_\_END\_\_](#expose-__end__) (aka `.expose_self`)
|
68
|
+
- **Setting values**
|
69
|
+
- [Default setting values file](#default-setting-values-file)
|
70
|
+
- [Load setting values from YAML file](#load-setting-values-from-yaml-file-by-instance)
|
71
|
+
- [Load setting values from JSON file](#load-setting-values-from-json-file-by-instance)
|
72
|
+
- [Load setting values from \_\_END\_\_](#load-setting-values-from-__end__-by-instance)
|
73
|
+
- [Load setting values from file manually](#load-setting-values-from-file-manually-by-instance)
|
74
|
+
- **Daily work**
|
75
|
+
- [Save to JSON file](#save-to-json-file) (`#save_to_json`)
|
76
|
+
- [Save to YAML file](#save-to-yaml-file) (`#save_to_yaml`)
|
61
77
|
- [Plugins](#plugins)
|
62
78
|
- [toml](#plugins-toml) (provides `load_from_toml`, `save_to_toml`, `expose_toml`)
|
63
79
|
- [Roadmap](#roadmap)
|
@@ -116,7 +132,7 @@ config.settings.vendor_api.user # => 'test_user'
|
|
116
132
|
config.settings.enable_graphql # => false
|
117
133
|
```
|
118
134
|
|
119
|
-
#### index-method []
|
135
|
+
#### access via index-method []
|
120
136
|
|
121
137
|
```ruby
|
122
138
|
# get option value via index (with indifferent (string / symbol / mixed) access)
|
@@ -200,40 +216,58 @@ class Config < Qonfig::DataSet
|
|
200
216
|
end
|
201
217
|
|
202
218
|
config = Config.new
|
219
|
+
```
|
203
220
|
|
204
|
-
|
221
|
+
#### configure via proc
|
222
|
+
|
223
|
+
```ruby
|
205
224
|
config.configure do |conf|
|
206
225
|
conf.enable_middlewares = true
|
207
226
|
conf.geo_api.provider = :yandex_maps
|
208
227
|
conf.testing.engine = :mini_test
|
209
228
|
end
|
229
|
+
```
|
210
230
|
|
211
|
-
|
231
|
+
#### configure via settings object (by option name)
|
232
|
+
|
233
|
+
```ruby
|
212
234
|
config.settings.enable_middlewares = false
|
213
235
|
config.settings.geo_api.provider = :apple_maps
|
214
236
|
config.settings.testing.engine = :ultra_test
|
237
|
+
```
|
238
|
+
|
239
|
+
#### configure via settings object (by setting key)
|
215
240
|
|
216
|
-
|
241
|
+
```ruby
|
217
242
|
config.settings[:enable_middlewares] = true
|
218
243
|
config.settings[:geo_api][:provider] = :rambler_maps
|
219
244
|
config.settings[:testing][:engine] = :mega_test
|
245
|
+
```
|
220
246
|
|
221
|
-
|
247
|
+
#### instant configuration via proc
|
248
|
+
|
249
|
+
```ruby
|
222
250
|
config = Config.new do |conf|
|
223
251
|
conf.enable_middlewares = false
|
224
252
|
conf.geo_api.provider = :amazon_maps
|
225
253
|
conf.testing.engine = :crypto_test
|
226
254
|
end
|
255
|
+
```
|
227
256
|
|
228
|
-
|
257
|
+
#### using a hash
|
258
|
+
|
259
|
+
```ruby
|
229
260
|
config = Config.new(
|
230
261
|
testing: { engine: :mini_test, parallel: false },
|
231
262
|
geo_api: { provider: :rambler_maps },
|
232
263
|
enable_middlewares: true
|
233
264
|
)
|
234
265
|
config.configure(enable_middlewares: false)
|
266
|
+
```
|
267
|
+
|
268
|
+
#### using both hash and proc (proc has higher priority)
|
235
269
|
|
236
|
-
|
270
|
+
```ruby
|
237
271
|
config = Config.new(enable_middlewares: true) do |conf|
|
238
272
|
conf.testing.parallel = true
|
239
273
|
end
|
@@ -455,18 +489,25 @@ GeneralApplication.config.to_h
|
|
455
489
|
# and etc... (all Qonfig-related features)
|
456
490
|
```
|
457
491
|
|
492
|
+
---
|
493
|
+
|
458
494
|
### Instantiation without class definition
|
459
495
|
|
460
496
|
```ruby
|
461
497
|
config = Qonfig::DataSet.build do
|
462
498
|
setting :user, 'D@iVeR'
|
463
499
|
setting :password, 'test123'
|
500
|
+
|
501
|
+
def custom_method
|
502
|
+
'custom_result'
|
503
|
+
end
|
464
504
|
end
|
465
505
|
|
466
506
|
config.is_a?(Qonfig::DataSet) # => true
|
467
507
|
|
468
508
|
config.settings.user # => 'D@iVeR'
|
469
509
|
config.settings.password # => 'test123'
|
510
|
+
config.custom_method # => 'custom_result'
|
470
511
|
```
|
471
512
|
|
472
513
|
---
|
@@ -478,6 +519,8 @@ config.settings.password # => 'test123'
|
|
478
519
|
- [Clear options](#clear-options) (set to nil)
|
479
520
|
- [State freeze](#state-freeze)
|
480
521
|
- [Settings as Predicates](#settings-as-predicates)
|
522
|
+
- [Setting key existence](#setting-key-existence) (`#key?`/`#option?`/`#setting?`)
|
523
|
+
- [Run arbitary code with temporary settings](#run-arbitary-code-with-temporary-settings)
|
481
524
|
|
482
525
|
---
|
483
526
|
|
@@ -682,6 +725,72 @@ config.settings.database.engine.driver? # => true (true => true)
|
|
682
725
|
|
683
726
|
---
|
684
727
|
|
728
|
+
### Setting key existence
|
729
|
+
|
730
|
+
- `#key?(*key_path)` / `#option?(*key_path)` / `#setting?(*key_path)`
|
731
|
+
- `*key_path` - an array of symbols and strings that represents a path to the concrete setting key;
|
732
|
+
- (for example, `config.key?(:credentials, :user)` tries to check that `config.settings.credentials.user` is exist);
|
733
|
+
- returns `true` if the concrete key is exist;
|
734
|
+
- returns `false` if the concrete key does not exist;
|
735
|
+
|
736
|
+
```ruby
|
737
|
+
class Config < Qonfig::DataSet
|
738
|
+
setting :credentials do
|
739
|
+
setting :user, 'D@iVeR'
|
740
|
+
setting :password, 'test123'
|
741
|
+
end
|
742
|
+
end
|
743
|
+
|
744
|
+
config = Config.new
|
745
|
+
|
746
|
+
config.key?('credentials', 'user') # => true
|
747
|
+
config.key?('credentials', 'token') # => false (key does not exist)
|
748
|
+
|
749
|
+
config.key?('credentials') # => true
|
750
|
+
config.key?('que_adapter') # => false (key does not exist)
|
751
|
+
|
752
|
+
# aliases
|
753
|
+
config.setting?('credentials') # => true
|
754
|
+
config.option?(:credentials, :password) # => true
|
755
|
+
```
|
756
|
+
|
757
|
+
---
|
758
|
+
|
759
|
+
### Run arbitary code with temporary settings
|
760
|
+
|
761
|
+
- provides a way to run an arbitary code with temporarily specified settings;
|
762
|
+
- your arbitary code can temporary change any setting too - all settings will be returned to the original state;
|
763
|
+
- (it is convenient to run code samples by this way in tests (with substitued configs));
|
764
|
+
- it is fully thread-safe `:)`;
|
765
|
+
|
766
|
+
```ruby
|
767
|
+
class Config < Qonfig::DataSet
|
768
|
+
setting :queue do
|
769
|
+
setting :adapter, :sidekiq
|
770
|
+
setting :options, {}
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
config = Config.new
|
775
|
+
|
776
|
+
# run a block of code with temporary queue.adapter setting
|
777
|
+
config.with(queue: { adapter: 'que' }) do
|
778
|
+
# your changed settings
|
779
|
+
config.settings.queue.adapter # => 'que'
|
780
|
+
|
781
|
+
# you can temporary change settings by your code too
|
782
|
+
config.settings.queue.options = { concurrency: 10 }
|
783
|
+
|
784
|
+
# ...your another code...
|
785
|
+
end
|
786
|
+
|
787
|
+
# original settings has not changed :)
|
788
|
+
config.settings.queue.adapter # => :sidekiq
|
789
|
+
config.settings.queue.options # => {}
|
790
|
+
```
|
791
|
+
|
792
|
+
---
|
793
|
+
|
685
794
|
## Validation
|
686
795
|
|
687
796
|
- [Introduction](#introduction)
|
@@ -705,34 +814,38 @@ If you want to check the config object completely you can define a custom valida
|
|
705
814
|
- when assigning new values;
|
706
815
|
- when calling `#reload!`;
|
707
816
|
- when calling `#clear!`;
|
817
|
+
- provides `strict` and `non-strict` behavior (`strict: true` and `strict: false` respectively):
|
818
|
+
- `strict: false` ignores validations for settings with `nil` (allows `nil` value);
|
819
|
+
- `strict: true` does not ignores validations for settings with `nil`;
|
820
|
+
- `strict: false` is used by default;
|
708
821
|
- provides special [key search pattern](#key-search-pattern) for matching setting key names;
|
709
822
|
- uses the [key search pattern](#key-search-pattern) for definging what the setting key should be validated;
|
710
823
|
- you can define your own custom validation logic and validate dataset instance completely;
|
711
824
|
- validation logic should return **truthy** or **falsy** value;
|
712
825
|
- supprots two validation techniques (**proc-based** ([doc](#proc-based-validation)) and **dataset-method-based** ([doc](#method-based-validation))):
|
713
|
-
- **proc-based** (`setting validation`)
|
826
|
+
- **proc-based** (`setting validation`) ([doc](#proc-based-validation))
|
714
827
|
```ruby
|
715
|
-
validate
|
828
|
+
validate('db.user', strict: true) do |value|
|
716
829
|
value.is_a?(String)
|
717
830
|
end
|
718
831
|
```
|
719
|
-
- **proc-based** (`dataset validation`)
|
832
|
+
- **proc-based** (`dataset validation`) ([doc](#proc-based-validation))
|
720
833
|
```ruby
|
721
|
-
validate do
|
834
|
+
validate(strict: false) do
|
722
835
|
settings.user == User[1]
|
723
836
|
end
|
724
837
|
```
|
725
|
-
- **dataset-method-based** (`setting validation`)
|
838
|
+
- **dataset-method-based** (`setting validation`) ([doc](#method-based-validation))
|
726
839
|
```ruby
|
727
|
-
validate 'db.user', by: :check_user
|
840
|
+
validate 'db.user', by: :check_user, strict: true
|
728
841
|
|
729
842
|
def check_user(value)
|
730
843
|
value.is_a?(String)
|
731
844
|
end
|
732
845
|
```
|
733
|
-
- **dataset-method-based** (`dataset validation`)
|
846
|
+
- **dataset-method-based** (`dataset validation`) ([doc](#method-based-validation))
|
734
847
|
```ruby
|
735
|
-
validate by: :check_config
|
848
|
+
validate by: :check_config, strict: false
|
736
849
|
|
737
850
|
def check_config
|
738
851
|
settings.user == User[1]
|
@@ -740,7 +853,8 @@ If you want to check the config object completely you can define a custom valida
|
|
740
853
|
```
|
741
854
|
- provides a **set of standard validations** ([doc](#predefined-validations)):
|
742
855
|
- DSL: `validate 'key.pattern', :predefned_validator`;
|
743
|
-
-
|
856
|
+
- supports `strict` behavior;
|
857
|
+
- realized validators:
|
744
858
|
- `integer`
|
745
859
|
- `float`
|
746
860
|
- `numeric`
|
@@ -782,11 +896,13 @@ If you want to check the config object completely you can define a custom valida
|
|
782
896
|
### Proc-based validation
|
783
897
|
|
784
898
|
- your proc should return truthy value or falsy value;
|
899
|
+
- `nil` values are ignored by default;
|
900
|
+
- set `strict: true` to disable `nil` ignorance (`strict: false` is used by default);
|
785
901
|
- how to validate setting keys:
|
786
902
|
- define proc with attribute: `validate 'your.setting.path' do |value|; end`
|
787
903
|
- proc will receive setting value;
|
788
904
|
- how to validate dataset instance:
|
789
|
-
- define proc without setting key pattern: `validate do; end
|
905
|
+
- define proc without setting key pattern: `validate do; end`;
|
790
906
|
|
791
907
|
```ruby
|
792
908
|
class Config < Qonfig::DataSet
|
@@ -805,6 +921,7 @@ class Config < Qonfig::DataSet
|
|
805
921
|
end
|
806
922
|
|
807
923
|
setting :enabled, false
|
924
|
+
setting :token, '1a2a3a', strict: true
|
808
925
|
|
809
926
|
# validates:
|
810
927
|
# - db.password
|
@@ -825,6 +942,11 @@ class Config < Qonfig::DataSet
|
|
825
942
|
validate do # NOTE: no setting key pattern
|
826
943
|
settings.enabled == false
|
827
944
|
end
|
945
|
+
|
946
|
+
# do not ignore `nil` (strict: true)
|
947
|
+
validate(:token, strict: true) do
|
948
|
+
value.is_a?(String)
|
949
|
+
end
|
828
950
|
end
|
829
951
|
|
830
952
|
config = Config.new
|
@@ -833,6 +955,9 @@ config.settings.service.address = 123 # => Qonfig::ValidationError (should be a
|
|
833
955
|
config.settings.service.protocol = :http # => Qonfig::ValidationError (should be a string)
|
834
956
|
config.settings.service.creds.admin = :billikota # => Qonfig::ValidationError (should be a string)
|
835
957
|
config.settings.enabled = true # => Qonfig::ValidationError (isnt `true`)
|
958
|
+
|
959
|
+
config.settings.db.password = nil # ok, nil is ignored (non-strict behavior)
|
960
|
+
config.settings.token = nil # => Qonfig::ValidationError (nil is not ignored, strict behavior) (should be a type of string)
|
836
961
|
```
|
837
962
|
|
838
963
|
---
|
@@ -840,6 +965,8 @@ config.settings.enabled = true # => Qonfig::ValidationError (isnt `true`)
|
|
840
965
|
### Method-based validation
|
841
966
|
|
842
967
|
- method should return truthy value or falsy value;
|
968
|
+
- `nil` values are ignored by default;
|
969
|
+
- set `strict: true` to disable `nil` ignorance (`strict: false` is used by default);
|
843
970
|
- how to validate setting keys:
|
844
971
|
- define validation: `validate 'db.*.user', by: :your_custom_method`;
|
845
972
|
- define your method with attribute: `def your_custom_method(setting_value); end`
|
@@ -862,6 +989,7 @@ class Config < Qonfig::DataSet
|
|
862
989
|
end
|
863
990
|
|
864
991
|
setting :enabled, true
|
992
|
+
setting :timeout, 12345, strict: true
|
865
993
|
|
866
994
|
# validates:
|
867
995
|
# - services.counts.google
|
@@ -874,6 +1002,9 @@ class Config < Qonfig::DataSet
|
|
874
1002
|
# - dataset instance
|
875
1003
|
validate by: :check_state # NOTE: no setting key pattern
|
876
1004
|
|
1005
|
+
# do not ignore `nil` (strict: true)
|
1006
|
+
validate :timeout, strict: true, by: :check_timeout
|
1007
|
+
|
877
1008
|
def check_presence(value)
|
878
1009
|
value.is_a?(Numeric) && value > 0
|
879
1010
|
end
|
@@ -881,15 +1012,21 @@ class Config < Qonfig::DataSet
|
|
881
1012
|
def check_state
|
882
1013
|
settings.enabled.is_a?(TrueClass) || settings.enabled.is_a?(FalseClass)
|
883
1014
|
end
|
1015
|
+
|
1016
|
+
def check_timeout(value)
|
1017
|
+
value.is_a?(Numeric)
|
1018
|
+
end
|
884
1019
|
end
|
885
1020
|
|
886
1021
|
config = Config.new
|
887
1022
|
|
888
1023
|
config.settings.counts.google = 0 # => Qonfig::ValidationError (< 0)
|
889
|
-
config.settings.counts.rambler = nil # => Qonfig::ValidationError (should be a numeric)
|
890
1024
|
config.settings.minimals.google = -1 # => Qonfig::ValidationError (< 0)
|
891
1025
|
config.settings.minimals.rambler = 'no' # => Qonfig::ValidationError (should be a numeric)
|
892
|
-
|
1026
|
+
|
1027
|
+
config.settings.counts.rambler = nil # ok, nil is ignored (default non-strict behavior)
|
1028
|
+
config.settings.enabled = nil # ok, nil is ignored (default non-strict behavior)
|
1029
|
+
config.settings.timeout = nil # => Qonfig::ValidationError (nil is not ignored, strict behavior) (should be a type of numeric)
|
893
1030
|
```
|
894
1031
|
|
895
1032
|
---
|
@@ -897,6 +1034,8 @@ config.settings.enabled = nil # => Qonfig::ValidationError (should be a boolean)
|
|
897
1034
|
### Predefined validations
|
898
1035
|
|
899
1036
|
- DSL: `validate 'key.pattern', :predefned_validator`
|
1037
|
+
- `nil` values are ignored by default;
|
1038
|
+
- set `strict: true` to disable `nil` ignorance (`strict: false` is used by default);
|
900
1039
|
- predefined validators:
|
901
1040
|
- `:not_nil`
|
902
1041
|
- `:integer`
|
@@ -948,15 +1087,23 @@ config.settings.ignorance = nil # => Qonfig::ValidationError (cant be nil)
|
|
948
1087
|
|
949
1088
|
## Work with files
|
950
1089
|
|
951
|
-
-
|
952
|
-
- [
|
953
|
-
- [
|
954
|
-
- [
|
955
|
-
- [
|
956
|
-
- [Load from
|
957
|
-
- [
|
958
|
-
- [
|
959
|
-
-
|
1090
|
+
- **Setting keys definition**
|
1091
|
+
- [Load from YAML file](#load-from-yaml-file)
|
1092
|
+
- [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
|
1093
|
+
- [Load from JSON file](#load-from-json-file)
|
1094
|
+
- [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)
|
1095
|
+
- [Load from ENV](#load-from-env)
|
1096
|
+
- [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
|
1097
|
+
- [Expose \_\_END\_\_](#expose-__end__) (aka `expose_self`)
|
1098
|
+
- **Setting values**
|
1099
|
+
- [Default setting values file](#default-setting-values-file)
|
1100
|
+
- [Load setting values from YAML file](#load-setting-values-from-yaml-file-by-instance)
|
1101
|
+
- [Load setting values from JSON file](#load-setting-values-from-json-file-by-instance)
|
1102
|
+
- [Load setting values from \_\_END\_\_](#load-setting-values-from-__end__-by-instance)
|
1103
|
+
- [Load setting values from file manually](#load-setting-values-from-file-manually-by-instance)
|
1104
|
+
- **Daily work**
|
1105
|
+
- [Save to JSON file](#save-to-json-file) (`save_to_json`)
|
1106
|
+
- [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
|
960
1107
|
|
961
1108
|
---
|
962
1109
|
|
@@ -1488,6 +1635,430 @@ production:
|
|
1488
1635
|
|
1489
1636
|
---
|
1490
1637
|
|
1638
|
+
### Default setting values file
|
1639
|
+
|
1640
|
+
- defines a file that should be used for setting values initialization for your config object;
|
1641
|
+
- `.values_file(file_path, format: :dynamic, strict: false, expose: nil)`
|
1642
|
+
- `file_path` - full file path or `:self` (`:self` menas "load setting values from __END__ data");
|
1643
|
+
- `:format` - defines the format of file (`:dynamic` means "try to automatically infer the file format") (`:dynamic` by default);
|
1644
|
+
- supports `:yaml`, `:json`, `:toml` (via `Qonfig.plugin(:toml)`), `:dynamic` (automatic format detection);
|
1645
|
+
- `:strict` - rerquires that file (or __END__-data) should exist (`false` by default);
|
1646
|
+
- `:expose` - what the environment-based subset of keys should be used (`nil` means "do not use any subset of keys") (`nil` by default);
|
1647
|
+
- extra keys that does not exist in your config will cause an exception `Qonfig::SettingNotFound` respectively;
|
1648
|
+
- initial values will be rewritten by values defined in your file;
|
1649
|
+
|
1650
|
+
#### Default behavior
|
1651
|
+
|
1652
|
+
```yaml
|
1653
|
+
# sidekiq.yml
|
1654
|
+
|
1655
|
+
adapter: sidekiq
|
1656
|
+
options:
|
1657
|
+
processes: 10
|
1658
|
+
```
|
1659
|
+
|
1660
|
+
```ruby
|
1661
|
+
class Config < Qonfig::DataSet
|
1662
|
+
values_file 'sidekiq.yml', format: :yaml
|
1663
|
+
|
1664
|
+
setting :adapter, 'que'
|
1665
|
+
setting :options do
|
1666
|
+
setting :processes, 2
|
1667
|
+
setting :threads, 5
|
1668
|
+
setting :protected, false
|
1669
|
+
end
|
1670
|
+
end
|
1671
|
+
|
1672
|
+
config = Config.new
|
1673
|
+
|
1674
|
+
config.settings.adapter # => "sidekiq" (from sidekiq.yml)
|
1675
|
+
config.settings.options.processes # => 10 (from sidekiq.yml)
|
1676
|
+
config.settings.options.threads # => 5 (original value)
|
1677
|
+
config.settings.options.protected # => false (original value)
|
1678
|
+
```
|
1679
|
+
|
1680
|
+
#### Load values from \_\_END\_\_-data
|
1681
|
+
|
1682
|
+
```ruby
|
1683
|
+
class Config < Qonfig::DataSet
|
1684
|
+
values_file :self, format: :yaml
|
1685
|
+
|
1686
|
+
setting :user
|
1687
|
+
setting :password
|
1688
|
+
setting :enabled, true
|
1689
|
+
end
|
1690
|
+
|
1691
|
+
config = Config.new
|
1692
|
+
|
1693
|
+
config.settings.user # => "D@iVeR" (from __END__ data)
|
1694
|
+
config.settings.password # => "test123" (from __END__ data)
|
1695
|
+
config.settings.enabled # => true (original value)
|
1696
|
+
|
1697
|
+
__END__
|
1698
|
+
|
1699
|
+
user: 'D@iVeR'
|
1700
|
+
password: 'test123'
|
1701
|
+
```
|
1702
|
+
|
1703
|
+
#### Setting values with environment separation
|
1704
|
+
|
1705
|
+
```yaml
|
1706
|
+
# sidekiq.yml
|
1707
|
+
|
1708
|
+
development:
|
1709
|
+
adapter: :in_memory
|
1710
|
+
options:
|
1711
|
+
threads: 10
|
1712
|
+
|
1713
|
+
production:
|
1714
|
+
adapter: :sidekiq
|
1715
|
+
options:
|
1716
|
+
threads: 150
|
1717
|
+
```
|
1718
|
+
|
1719
|
+
```ruby
|
1720
|
+
class Config < Qonfig::DataSet
|
1721
|
+
values_file 'sidekiq.yml', format: :yaml, expose: :development
|
1722
|
+
|
1723
|
+
setting :adapter
|
1724
|
+
setting :options do
|
1725
|
+
setting :threads
|
1726
|
+
end
|
1727
|
+
end
|
1728
|
+
|
1729
|
+
config = Config.new
|
1730
|
+
|
1731
|
+
config.settings.adapter # => 'in_memory' (development keys subset)
|
1732
|
+
config.settings.options.threads # => 10 (development keys subset)
|
1733
|
+
```
|
1734
|
+
|
1735
|
+
#### File does not exist
|
1736
|
+
|
1737
|
+
```ruby
|
1738
|
+
# strict behavior (default)
|
1739
|
+
class Config < Qonfig::DataSet
|
1740
|
+
values_file 'sidekiq.yml'
|
1741
|
+
end
|
1742
|
+
|
1743
|
+
config = Config.new # => Qonfig::FileNotFoundError
|
1744
|
+
|
1745
|
+
# non-strict behavior (strict: false)
|
1746
|
+
class Config < Qonfig::DataSet
|
1747
|
+
values_file 'sidekiq.yml', strict: false
|
1748
|
+
end
|
1749
|
+
|
1750
|
+
config = Config.new # no error
|
1751
|
+
```
|
1752
|
+
|
1753
|
+
---
|
1754
|
+
|
1755
|
+
### Load setting values from YAML file (by instance)
|
1756
|
+
|
1757
|
+
- prvoides an ability to load predefined setting values from a yaml file;
|
1758
|
+
- `#load_from_yaml(file_path, strict: true, expose: nil)`
|
1759
|
+
- `file_path` - full file path or `:self` (`:self` means "load setting values from __END__ data");
|
1760
|
+
- `:strict` - rerquires that file (or __END__-data) should exist (`true` by default);
|
1761
|
+
- `:expose` - what the environment-based subset of keys should be used (`nil` means "do not use any subset of keys") (`nil` by default);
|
1762
|
+
|
1763
|
+
#### Default behavior
|
1764
|
+
|
1765
|
+
```yaml
|
1766
|
+
# config.yml
|
1767
|
+
|
1768
|
+
domain: google.ru
|
1769
|
+
creds:
|
1770
|
+
auth_token: test123
|
1771
|
+
```
|
1772
|
+
|
1773
|
+
```ruby
|
1774
|
+
class Config < Qonfig::DataSet
|
1775
|
+
seting :domain, 'test.com'
|
1776
|
+
setting :creds do
|
1777
|
+
setting :auth_token, 'test'
|
1778
|
+
end
|
1779
|
+
end
|
1780
|
+
|
1781
|
+
config = Config.new
|
1782
|
+
config.settings.domain # => "test.com"
|
1783
|
+
config.settings.creds.auth_token # => "test"
|
1784
|
+
|
1785
|
+
# load new values
|
1786
|
+
config.load_from_yaml('config.yml')
|
1787
|
+
|
1788
|
+
config.settings.domain # => "google.ru" (from config.yml)
|
1789
|
+
config.settings.creds.auth_token # => "test123" (from config.yml)
|
1790
|
+
```
|
1791
|
+
|
1792
|
+
#### Load from \_\_END\_\_
|
1793
|
+
|
1794
|
+
```ruby
|
1795
|
+
class Config < Qonfig::DataSet
|
1796
|
+
seting :domain, 'test.com'
|
1797
|
+
setting :creds do
|
1798
|
+
setting :auth_token, 'test'
|
1799
|
+
end
|
1800
|
+
end
|
1801
|
+
|
1802
|
+
config = Config.new
|
1803
|
+
config.settings.domain # => "test.com"
|
1804
|
+
config.settings.creds.auth_token # => "test"
|
1805
|
+
|
1806
|
+
# load new values
|
1807
|
+
config.load_from_yaml(:self)
|
1808
|
+
config.settings.domain # => "yandex.ru" (from __END__-data)
|
1809
|
+
config.settings.creds.auth_token # => "CK0sIdA" (from __END__-data)
|
1810
|
+
|
1811
|
+
__END__
|
1812
|
+
|
1813
|
+
domain: yandex.ru
|
1814
|
+
creds:
|
1815
|
+
auth_token: CK0sIdA
|
1816
|
+
```
|
1817
|
+
|
1818
|
+
#### Setting values with environment separation
|
1819
|
+
|
1820
|
+
```yaml
|
1821
|
+
# config.yml
|
1822
|
+
|
1823
|
+
development:
|
1824
|
+
domain: dev.google.ru
|
1825
|
+
creds:
|
1826
|
+
auth_token: kekpek
|
1827
|
+
|
1828
|
+
production:
|
1829
|
+
domain: google.ru
|
1830
|
+
creds:
|
1831
|
+
auth_token: Asod1
|
1832
|
+
```
|
1833
|
+
|
1834
|
+
```ruby
|
1835
|
+
class Config < Qonfig::DataSet
|
1836
|
+
setting :domain, 'test.com'
|
1837
|
+
setting :creds do
|
1838
|
+
setting :auth_token
|
1839
|
+
end
|
1840
|
+
end
|
1841
|
+
|
1842
|
+
config = Config.new
|
1843
|
+
|
1844
|
+
# load new values (expose development settings)
|
1845
|
+
config.load_from_yaml('config.yml', expose: :development)
|
1846
|
+
|
1847
|
+
config.settings.domain # => "dev.google.ru" (from config.yml)
|
1848
|
+
config.settings.creds.auth_token # => "kek.pek" (from config.yml)
|
1849
|
+
```
|
1850
|
+
|
1851
|
+
---
|
1852
|
+
|
1853
|
+
### Load setting values from JSON file (by instance)
|
1854
|
+
|
1855
|
+
- prvoides an ability to load predefined setting values from a json file;
|
1856
|
+
- `#load_from_yaml(file_path, strict: true, expose: nil)`
|
1857
|
+
- `file_path` - full file path or `:self` (`:self` means "load setting values from __END__ data");
|
1858
|
+
- `:strict` - rerquires that file (or __END__-data) should exist (`true` by default);
|
1859
|
+
- `:expose` - what the environment-based subset of keys should be used (`nil` means "do not use any subset of keys") (`nil` by default);
|
1860
|
+
|
1861
|
+
#### Default behavior
|
1862
|
+
|
1863
|
+
```json
|
1864
|
+
// config.json
|
1865
|
+
|
1866
|
+
{
|
1867
|
+
"domain": "google.ru",
|
1868
|
+
"creds": {
|
1869
|
+
"auth_token": "test123"
|
1870
|
+
}
|
1871
|
+
}
|
1872
|
+
```
|
1873
|
+
|
1874
|
+
```ruby
|
1875
|
+
class Config < Qonfig::DataSet
|
1876
|
+
seting :domain, 'test.com'
|
1877
|
+
setting :creds do
|
1878
|
+
setting :auth_token, 'test'
|
1879
|
+
end
|
1880
|
+
end
|
1881
|
+
|
1882
|
+
config = Config.new
|
1883
|
+
config.settings.domain # => "test.com"
|
1884
|
+
config.settings.creds.auth_token # => "test"
|
1885
|
+
|
1886
|
+
# load new values
|
1887
|
+
config.load_from_json('config.json')
|
1888
|
+
|
1889
|
+
config.settings.domain # => "google.ru" (from config.json)
|
1890
|
+
config.settings.creds.auth_token # => "test123" (from config.json)
|
1891
|
+
```
|
1892
|
+
|
1893
|
+
#### Load from \_\_END\_\_
|
1894
|
+
|
1895
|
+
```ruby
|
1896
|
+
class Config < Qonfig::DataSet
|
1897
|
+
seting :domain, 'test.com'
|
1898
|
+
setting :creds do
|
1899
|
+
setting :auth_token, 'test'
|
1900
|
+
end
|
1901
|
+
end
|
1902
|
+
|
1903
|
+
config = Config.new
|
1904
|
+
config.settings.domain # => "test.com"
|
1905
|
+
config.settings.creds.auth_token # => "test"
|
1906
|
+
|
1907
|
+
# load new values
|
1908
|
+
config.load_from_json(:self)
|
1909
|
+
config.settings.domain # => "yandex.ru" (from __END__-data)
|
1910
|
+
config.settings.creds.auth_token # => "CK0sIdA" (from __END__-data)
|
1911
|
+
|
1912
|
+
__END__
|
1913
|
+
|
1914
|
+
{
|
1915
|
+
"domain": "yandex.ru",
|
1916
|
+
"creds": {
|
1917
|
+
"auth_token": "CK0sIdA"
|
1918
|
+
}
|
1919
|
+
}
|
1920
|
+
```
|
1921
|
+
|
1922
|
+
#### Setting values with environment separation
|
1923
|
+
|
1924
|
+
```json
|
1925
|
+
// config.json
|
1926
|
+
|
1927
|
+
{
|
1928
|
+
"development": {
|
1929
|
+
"domain": "dev.google.ru",
|
1930
|
+
"creds": {
|
1931
|
+
"auth_token": "kekpek"
|
1932
|
+
}
|
1933
|
+
},
|
1934
|
+
"production": {
|
1935
|
+
"domain": "google.ru",
|
1936
|
+
"creds": {
|
1937
|
+
"auth_token": "Asod1"
|
1938
|
+
}
|
1939
|
+
}
|
1940
|
+
}
|
1941
|
+
```
|
1942
|
+
|
1943
|
+
```ruby
|
1944
|
+
class Config < Qonfig::DataSet
|
1945
|
+
setting :domain, 'test.com'
|
1946
|
+
setting :creds do
|
1947
|
+
setting :auth_token
|
1948
|
+
end
|
1949
|
+
end
|
1950
|
+
|
1951
|
+
config = Config.new
|
1952
|
+
|
1953
|
+
# load new values (from development subset)
|
1954
|
+
config.load_from_json('config.json', expose: :development)
|
1955
|
+
|
1956
|
+
config.settings.domain # => "dev.google.ru" (from config.json)
|
1957
|
+
config.settings.creds.auth_token # => "kek.pek" (from config.json)
|
1958
|
+
```
|
1959
|
+
---
|
1960
|
+
|
1961
|
+
### Load setting values from \_\_END\_\_ (by instance)
|
1962
|
+
|
1963
|
+
- prvoides an ability to load predefined setting values from `__END__` file section;
|
1964
|
+
- `#load_from_self(strict: true, expose: nil)`
|
1965
|
+
- `:format` - defines the format of file (`:dynamic` means "try to automatically infer the file format") (`:dynamic` by default);
|
1966
|
+
- supports `:yaml`, `:json`, `:toml` (via `Qonfig.plugin(:toml)`), `:dynamic` (automatic format detection);
|
1967
|
+
- `:strict` - requires that __END__-data should exist (`true` by default);
|
1968
|
+
- `:expose` - what the environment-based subset of keys should be used (`nil` means "do not use any subset of keys") (`nil` by default);
|
1969
|
+
|
1970
|
+
#### Default behavior
|
1971
|
+
|
1972
|
+
```ruby
|
1973
|
+
class Config < Qonfig::DataSet
|
1974
|
+
setting :account, 'test'
|
1975
|
+
setting :options do
|
1976
|
+
setting :login, '0exp'
|
1977
|
+
setting :password, 'test123'
|
1978
|
+
end
|
1979
|
+
end
|
1980
|
+
|
1981
|
+
config = Config.new
|
1982
|
+
config.settings.account # => "test" (original value)
|
1983
|
+
config.settings.options.login # => "0exp" (original value)
|
1984
|
+
config.settings.options.password # => "test123" (original value)
|
1985
|
+
|
1986
|
+
# load new values
|
1987
|
+
config.load_from_self(format: :yaml)
|
1988
|
+
# or config.load_from_self
|
1989
|
+
|
1990
|
+
config.settings.account # => "real" (from __END__-data)
|
1991
|
+
config.settings.options.login # => "D@iVeR" (from __END__-data)
|
1992
|
+
config.settings.options.password # => "azaza123" (from __END__-data)
|
1993
|
+
|
1994
|
+
__END__
|
1995
|
+
|
1996
|
+
account: real
|
1997
|
+
options:
|
1998
|
+
login: D@iVeR
|
1999
|
+
password: azaza123
|
2000
|
+
```
|
2001
|
+
|
2002
|
+
#### Setting values with envvironment separation
|
2003
|
+
|
2004
|
+
```ruby
|
2005
|
+
class Config < Qonfig::DataSet
|
2006
|
+
setting :domain, 'test.google.ru'
|
2007
|
+
setting :options do
|
2008
|
+
setting :login, 'test'
|
2009
|
+
setting :password, 'test123'
|
2010
|
+
end
|
2011
|
+
end
|
2012
|
+
|
2013
|
+
config = Config.new
|
2014
|
+
config.settings.domain # => "test.google.ru" (original value)
|
2015
|
+
config.settings.options.login # => "test" (original value)
|
2016
|
+
config.settings.options.password # => "test123" (original value)
|
2017
|
+
|
2018
|
+
# load new values
|
2019
|
+
config.load_from_self(format: :json, expose: :production)
|
2020
|
+
# or config.load_from_self(expose: production)
|
2021
|
+
|
2022
|
+
config.settings.domain # => "prod.google.ru" (from __END__-data)
|
2023
|
+
config.settings.options.login # => "prod" (from __END__-data)
|
2024
|
+
config.settings.options.password # => "prod123" (from __END__-data)
|
2025
|
+
|
2026
|
+
__END__
|
2027
|
+
|
2028
|
+
{
|
2029
|
+
"development": {
|
2030
|
+
"domain": "dev.google.ru",
|
2031
|
+
"options": {
|
2032
|
+
"login": "dev",
|
2033
|
+
"password": "dev123"
|
2034
|
+
}
|
2035
|
+
},
|
2036
|
+
"production": {
|
2037
|
+
"domain": "prod.google.ru",
|
2038
|
+
"options": {
|
2039
|
+
"login": "prod",
|
2040
|
+
"password": "prod123"
|
2041
|
+
}
|
2042
|
+
}
|
2043
|
+
}
|
2044
|
+
```
|
2045
|
+
|
2046
|
+
---
|
2047
|
+
|
2048
|
+
### Load setting values from file manually (by instance)
|
2049
|
+
|
2050
|
+
- prvoides an ability to load predefined setting values from a file;
|
2051
|
+
- works in instance-based `#load_from_yaml` / `#load_from_json` / `#load_from_self` manner;
|
2052
|
+
- signature: `#load_from_file(file_path, format: :dynamic, strict: true, expose: nil)`:
|
2053
|
+
- `file_path` - full file path or `:self` (`:self` means "load setting values from __END__ data");
|
2054
|
+
- `:format` - defines the format of file (`:dynamic` means "try to automatically infer the file format") (`:dynamic` by default);
|
2055
|
+
- supports `:yaml`, `:json`, `:toml` (via `Qonfig.plugin(:toml)`), `:dynamic` (automatic format detection);
|
2056
|
+
- `:strict` - rerquires that file (or __END__-data) should exist (`true` by default);
|
2057
|
+
- `:expose` - what the environment-based subset of keys should be used (`nil` means "do not use any subset of keys") (`nil` by default);
|
2058
|
+
- see examples for instance-based `#load_from_yaml` ([doc](#load-setting-values-from-yaml-by-instance)) / `#load_from_json` ([doc](#load-setting-values-from-json-by-instance)) / `#load_from_self` ([doc](#load-setting-values-from-__end__-by-instance));
|
2059
|
+
|
2060
|
+
---
|
2061
|
+
|
1491
2062
|
### Save to JSON file
|
1492
2063
|
|
1493
2064
|
- `#save_to_json` - represents config object as a json structure and saves it to a file:
|
@@ -1630,8 +2201,6 @@ enabled: true
|
|
1630
2201
|
dynamic: 10
|
1631
2202
|
```
|
1632
2203
|
|
1633
|
-
---
|
1634
|
-
|
1635
2204
|
### Plugins
|
1636
2205
|
|
1637
2206
|
```ruby
|
@@ -1652,10 +2221,12 @@ Provided plugins:
|
|
1652
2221
|
|
1653
2222
|
- adds support for `toml` format ([specification](https://github.com/toml-lang/toml));
|
1654
2223
|
- depends on `toml-rb` gem ([link](https://github.com/emancu/toml-rb));
|
1655
|
-
- supports TOML `0.
|
1656
|
-
- provides
|
1657
|
-
- provides `
|
1658
|
-
- provides `
|
2224
|
+
- supports TOML `0.5.0` format (dependency lock);
|
2225
|
+
- provides `.load_from_toml` (works in `.load_from_yaml` manner ([doc](#load-from-yaml-file)));
|
2226
|
+
- provides `.expose_toml` (works in `.expose_yaml` manner ([doc](#expose-yaml)));
|
2227
|
+
- provides `#save_to_toml` (works in `#save_to_yaml` manner ([doc](#save-to-yaml-file))) (`toml-rb` has no native options);
|
2228
|
+
- provides `format: :toml` for `.values_file` ([doc]());
|
2229
|
+
- provides `#load_from_toml` (work in `#load_from_yaml` manner ([doc](#load-setting-values-from-yaml)));
|
1659
2230
|
|
1660
2231
|
```ruby
|
1661
2232
|
# 1) require external dependency
|
@@ -1670,14 +2241,24 @@ Qonfig.plugin(:toml)
|
|
1670
2241
|
|
1671
2242
|
## Roadmap
|
1672
2243
|
|
1673
|
-
-
|
1674
|
-
-
|
2244
|
+
- **Major**:
|
2245
|
+
- distributed configuration server;
|
2246
|
+
- cli toolchain;
|
2247
|
+
- support for Rails-like secrets;
|
2248
|
+
- support for persistent data storages (we want to store configs in multiple databases and files);
|
2249
|
+
- Rails reload plugin;
|
2250
|
+
- **Minor**:
|
2251
|
+
- custom global (and class-level) validators (with a special Validator Definition DSL);
|
2252
|
+
- support for "dot notation" in `#key?`, `#option?`, `#setting?`, `#dig`, `#subset`, `#slice`, `#slice_value`;
|
2253
|
+
- "load setting values from a file" (at instance level);
|
2254
|
+
- config improts (and exports);
|
2255
|
+
- pretty print :)));
|
1675
2256
|
|
1676
2257
|
## Contributing
|
1677
2258
|
|
1678
2259
|
- Fork it ( https://github.com/0exp/qonfig/fork )
|
1679
2260
|
- Create your feature branch (`git checkout -b feature/my-new-feature`)
|
1680
|
-
- Commit your changes (`git commit -am 'Add some feature'`)
|
2261
|
+
- Commit your changes (`git commit -am '[my-new-featre] Add some feature'`)
|
1681
2262
|
- Push to the branch (`git push origin feature/my-new-feature`)
|
1682
2263
|
- Create new Pull Request
|
1683
2264
|
|