qonfig 0.16.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
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