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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +5 -1
  3. data/.travis.yml +6 -6
  4. data/CHANGELOG.md +17 -0
  5. data/README.md +630 -49
  6. data/gemfiles/with_external_deps.gemfile +2 -2
  7. data/lib/qonfig/commands/{add_nested_option.rb → definition/add_nested_option.rb} +1 -1
  8. data/lib/qonfig/commands/{add_option.rb → definition/add_option.rb} +1 -1
  9. data/lib/qonfig/commands/{compose.rb → definition/compose.rb} +1 -1
  10. data/lib/qonfig/commands/{expose_json.rb → definition/expose_json.rb} +1 -1
  11. data/lib/qonfig/commands/{expose_self.rb → definition/expose_self.rb} +1 -1
  12. data/lib/qonfig/commands/{expose_yaml.rb → definition/expose_yaml.rb} +1 -1
  13. data/lib/qonfig/commands/definition/load_from_env/value_converter.rb +82 -0
  14. data/lib/qonfig/commands/{load_from_env.rb → definition/load_from_env.rb} +1 -1
  15. data/lib/qonfig/commands/{load_from_json.rb → definition/load_from_json.rb} +1 -1
  16. data/lib/qonfig/commands/{load_from_self.rb → definition/load_from_self.rb} +1 -1
  17. data/lib/qonfig/commands/{load_from_yaml.rb → definition/load_from_yaml.rb} +1 -1
  18. data/lib/qonfig/commands/definition.rb +16 -0
  19. data/lib/qonfig/commands/instantiation/values_file.rb +171 -0
  20. data/lib/qonfig/commands/instantiation.rb +7 -0
  21. data/lib/qonfig/commands.rb +2 -10
  22. data/lib/qonfig/data_set/lock.rb +61 -4
  23. data/lib/qonfig/data_set.rb +151 -3
  24. data/lib/qonfig/dsl.rb +56 -16
  25. data/lib/qonfig/errors.rb +38 -11
  26. data/lib/qonfig/loaders/dynamic.rb +52 -0
  27. data/lib/qonfig/loaders/json.rb +6 -0
  28. data/lib/qonfig/loaders/yaml.rb +13 -0
  29. data/lib/qonfig/loaders.rb +3 -0
  30. data/lib/qonfig/plugins/toml/data_set.rb +13 -0
  31. data/lib/qonfig/plugins/toml/dsl.rb +6 -2
  32. data/lib/qonfig/plugins/toml/errors.rb +12 -0
  33. data/lib/qonfig/plugins/toml/loaders/dynamic.rb +31 -0
  34. data/lib/qonfig/plugins/toml/loaders/toml.rb +6 -0
  35. data/lib/qonfig/plugins/toml.rb +2 -0
  36. data/lib/qonfig/settings/builder.rb +1 -1
  37. data/lib/qonfig/settings.rb +21 -0
  38. data/lib/qonfig/validator/basic.rb +9 -1
  39. data/lib/qonfig/validator/builder/attribute_consistency.rb +29 -0
  40. data/lib/qonfig/validator/builder.rb +39 -14
  41. data/lib/qonfig/validator/dsl.rb +9 -1
  42. data/lib/qonfig/validator/method_based.rb +4 -2
  43. data/lib/qonfig/validator/predefined/common.rb +4 -2
  44. data/lib/qonfig/validator/predefined/registry.rb +0 -2
  45. data/lib/qonfig/validator/predefined/registry_control_mixin.rb +3 -2
  46. data/lib/qonfig/validator/proc_based.rb +4 -2
  47. data/lib/qonfig/version.rb +1 -1
  48. data/qonfig.gemspec +1 -1
  49. metadata +21 -15
  50. 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](#introdaction)
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
- - [Load from YAML file](#load-from-yaml-file)
53
- - [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
54
- - [Load from JSON file](#load-from-json-file)
55
- - [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)
56
- - [Load from ENV](#load-from-env)
57
- - [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
58
- - [Expose \_\_END\_\_](#expose-__end__) (aka `expose_self`)
59
- - [Save to JSON file](#save-to-json-file) (`save_to_json`)
60
- - [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
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
- # configure via proc
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
- # configure via settings object (by option name)
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
- # configure via settings object (by setting key)
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
- # instant configuration via proc
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
- # using a hash
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
- # using both hash and proc (proc has higher priority)
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 'db.user' do |value|
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
- - validators:
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
- config.settings.enabled = nil # => Qonfig::ValidationError (should be a boolean)
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
- - [Load from YAML file](#load-from-yaml-file)
952
- - [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
953
- - [Load from JSON file](#load-from-json-file)
954
- - [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)
955
- - [Load from ENV](#load-from-env)
956
- - [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
957
- - [Expose \_\_END\_\_](#expose-__end__) (aka `expose_self`)
958
- - [Save to JSON file](#save-to-json-file) (`save_to_json`)
959
- - [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
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.4.0` format (dependency lock);
1656
- - provides `load_from_toml` (works in `load_from_yaml` manner ([doc](#load-from-yaml-file)));
1657
- - provides `save_to_toml` (works in `save_to_yaml` manner ([doc](#save-to-yaml-file))) (`toml-rb` has no native options);
1658
- - provides `expose_toml` (works in `expose_yaml` manner ([doc](#expose-yaml)));
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
- - distributed configuration server;
1674
- - support for Rails-like secrets;
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