Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +457 -137
  4. data/lib/qonfig.rb +1 -0
  5. data/lib/qonfig/command_set.rb +7 -1
  6. data/lib/qonfig/commands/add_nested_option.rb +3 -1
  7. data/lib/qonfig/commands/add_option.rb +2 -1
  8. data/lib/qonfig/commands/base.rb +2 -1
  9. data/lib/qonfig/commands/compose.rb +16 -10
  10. data/lib/qonfig/commands/expose_yaml.rb +2 -1
  11. data/lib/qonfig/commands/load_from_env.rb +2 -1
  12. data/lib/qonfig/commands/load_from_json.rb +2 -1
  13. data/lib/qonfig/commands/load_from_self.rb +2 -1
  14. data/lib/qonfig/commands/load_from_yaml.rb +2 -1
  15. data/lib/qonfig/data_set.rb +92 -23
  16. data/lib/qonfig/data_set/lock.rb +46 -0
  17. data/lib/qonfig/data_set/validator.rb +0 -7
  18. data/lib/qonfig/errors.rb +8 -0
  19. data/lib/qonfig/plugins/toml/commands/expose_toml.rb +2 -1
  20. data/lib/qonfig/plugins/toml/commands/load_from_toml.rb +2 -1
  21. data/lib/qonfig/settings.rb +144 -33
  22. data/lib/qonfig/settings/builder.rb +20 -4
  23. data/lib/qonfig/settings/callbacks.rb +43 -0
  24. data/lib/qonfig/settings/key_matcher.rb +175 -0
  25. data/lib/qonfig/settings/lock.rb +3 -3
  26. data/lib/qonfig/validator.rb +58 -0
  27. data/lib/qonfig/validator/basic.rb +53 -0
  28. data/lib/qonfig/validator/builder.rb +169 -0
  29. data/lib/qonfig/validator/builder/attribute_consistency.rb +181 -0
  30. data/lib/qonfig/validator/collection.rb +73 -0
  31. data/lib/qonfig/validator/dsl.rb +51 -0
  32. data/lib/qonfig/validator/method_based.rb +49 -0
  33. data/lib/qonfig/validator/predefined.rb +41 -0
  34. data/lib/qonfig/validator/predefined/common.rb +53 -0
  35. data/lib/qonfig/validator/predefined/registry.rb +83 -0
  36. data/lib/qonfig/validator/predefined/registry_control_mixin.rb +43 -0
  37. data/lib/qonfig/validator/proc_based.rb +53 -0
  38. data/lib/qonfig/version.rb +1 -1
  39. data/qonfig.gemspec +1 -1
  40. metadata +19 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
---
2
2
SHA256:
3
- metadata.gz: 62b74a92c4c74b79f222e029726296a63b51ba3a57886a00911a34ffbcbced25
4
- data.tar.gz: 33fa91631b6554dec3547c9c31faddf805bc4a56becbe7042e1942c19c493f16
3
+ metadata.gz: c7f383ceef309126a60c17c1383cdfd3de9445aa5978ccb80374226dfd17e83c
4
+ data.tar.gz: 6f2f868550a9822b4feb8daff758a2a91d486903f9e57726c1045725fc658e55
5
5
SHA512:
6
- metadata.gz: 152acb89e834fccaaed1b4bfae379f8263d795b2e4e7b7913f6a2b98fd1bfebf984dec1c82a3568ddc08a3e16fdec2be7783f1f583ef09a3dc6b1386f9afb18f
7
- data.tar.gz: c468d5a5c289dc8b4b7b337e62314dbbf9d388b5dd6df1248a14e957a97be9937d7f24c47aa8634ffe415dbd092ed4f3c5b447036770b171fb6d87454eceb4e8
6
+ metadata.gz: bf030ed32f26241ebe6a8717e02e3b705d5f31e666c1096f19776c14e2db92f251e08ee12e668831a69bea51ce2fb40288791dbc4d8dfd19bc9fdd10c0505db8
7
+ data.tar.gz: 67e3f1712357a8de30e651271b8ca2dd2590af18cedd42ee57d48b86a755bfe15a0ea7ae59fea448212ae5fa798b19a12364acaa2fde588fc5778c33ef4d25c1
data/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
# Changelog
2
2
All notable changes to this project will be documented in this file.
3
3
4
+ ## [0.13.0] - 2019-08-13
5
+ ### Added
6
+ - Iteration over setting keys (`#each_setting { |key, value| }`, `#deep_each_setting { |key, value| }`);
7
+ - Brand new `Validation API`;
8
+
9
+ ### Changed
10
+ - Actualized development dependencies;
11
+
4
12
## [0.12.0] - 2019-07-19
5
13
### Added
6
14
- Support for **TOML** (`.toml`) format
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
# Qonfig · [![Gem Version](https://badge.fury.io/rb/qonfig.svg)](https://badge.fury.io/rb/qonfig) [![Build Status](https://travis-ci.org/0exp/qonfig.svg?branch=master)](https://travis-ci.org/0exp/qonfig) [![Coverage Status](https://coveralls.io/repos/github/0exp/qonfig/badge.svg?branch=master)](https://coveralls.io/github/0exp/qonfig?branch=master)
2
2
3
3
Config. Defined as a class. Used as an instance. Support for inheritance and composition.
4
- Lazy instantiation. Thread-safe. Command-style DSL. Extremely simple to define. Extremely simple to use. That's all.
4
+ Lazy instantiation. Thread-safe. Command-style DSL. Validation layer. Support for **YAML**, **TOML**, **JSON**, **\_\_END\_\_**, **ENV**.
5
+ Extremely simple to define. Extremely simple to use. That's all? **NOT** :)
5
6
6
7
## Installation
7
8
@@ -21,27 +22,38 @@ require 'qonfig'
21
22
22
23
## Usage
23
24
24
- - [Definition and Settings Access](#definition-and-access)
25
- - [Configuration](#configuration)
26
- - [Inheritance](#inheritance)
27
- - [Composition](#composition)
28
- - [Hash representation](#hash-representation)
29
- - [Config reloading](#config-reloading) (reload config definitions and option values)
30
- - [Clear options](#clear-options) (set to nil)
31
- - [State freeze](#state-freeze)
32
- - [Settings as Predicates](#settings-as-predicates)
33
- - [Load from YAML file](#load-from-yaml-file)
34
- - [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
35
- - [Load from JSON file](#load-from-json-file)
36
- - [Load from ENV](#load-from-env)
37
- - [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
38
- - [Save to JSON file](#save-to-json-file) (`save_to_json`)
39
- - [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
40
- - [Smart Mixin](#smart-mixin) (`Qonfig::Configurable`)
25
+ - [Definition](#definition)
26
+ - [Definition and Settings Access](#definition-and-access)
27
+ - [Configuration](#configuration)
28
+ - [Inheritance](#inheritance)
29
+ - [Composition](#composition)
30
+ - [Hash representation](#hash-representation)
31
+ - [Smart Mixin](#smart-mixin) (`Qonfig::Configurable`)
32
+ - [Interaction](#interaction)
33
+ - [Iteration over setting keys](#iteration-over-setting-keys) (`#each_setting`, `#deep_each_setting`)
34
+ - [Config reloading](#config-reloading) (reload config definitions and option values)
35
+ - [Clear options](#clear-options) (set to nil)
36
+ - [State freeze](#state-freeze)
37
+ - [Settings as Predicates](#settings-as-predicates)
38
+ - [Validation](#validation)
39
+ - [Key search pattern](#key-search-pattern)
40
+ - [Proc-based validation](#proc-based-validation)
41
+ - [Method-based validation](#method-based-validation)
42
+ - [Predefined validations](#predefined-validations)
43
+ - [Work with files](#work-with-files)
44
+ - [Load from YAML file](#load-from-yaml-file)
45
+ - [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
46
+ - [Load from JSON file](#load-from-json-file)
47
+ - [Load from ENV](#load-from-env)
48
+ - [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
49
+ - [Save to JSON file](#save-to-json-file) (`save_to_json`)
50
+ - [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
41
51
- [Plugins](#plugins)
42
- - [toml](#plugin-toml) (provides `load_from_toml`, `save_to_toml`, `expose_toml`)
52
+ - [toml](#plugins-toml) (provides `load_from_toml`, `save_to_toml`, `expose_toml`)
43
53
---
44
54
55
+ ## Definition
56
+
45
57
### Definition and Access
46
58
47
59
```ruby
@@ -274,6 +286,164 @@ Config.new.to_h
274
286
275
287
---
276
288
289
+ ### Smart Mixin
290
+
291
+ - class-level:
292
+ - `.configuration` - settings definitions;
293
+ - `.configure` - configuration;
294
+ - `.config` - config object;
295
+ - settings definitions are inheritable;
296
+ - instance-level:
297
+ - `#configure` - configuration;
298
+ - `#config` - config object;
299
+ - `#shared_config` - class-level config object;
300
+
301
+ ```ruby
302
+ # --- usage ---
303
+
304
+ class Application
305
+ # make configurable
306
+ include Qonfig::Configurable
307
+
308
+ configuration do
309
+ setting :user
310
+ setting :password
311
+ end
312
+ end
313
+
314
+ app = Application.new
315
+
316
+ # class-level config
317
+ Application.config.settings.user # => nil
318
+ Application.config.settings.password # => nil
319
+
320
+ # instance-level config
321
+ app.config.settings.user # => nil
322
+ app.config.settings.password # => nil
323
+
324
+ # access to the class level config from an instance
325
+ app.shared_config.settings.user # => nil
326
+ app.shared_config.settings.password # => nil
327
+
328
+ # class-level configuration
329
+ Application.configure do |conf|
330
+ conf.user = '0exp'
331
+ conf.password = 'test123'
332
+ end
333
+
334
+ # instance-level configuration
335
+ app.configure do |conf|
336
+ conf.user = 'admin'
337
+ conf.password = '123test'
338
+ end
339
+
340
+ # class has own config object
341
+ Application.config.settings.user # => '0exp'
342
+ Application.config.settings.password # => 'test123'
343
+
344
+ # instance has own config object
345
+ app.config.settings.user # => 'admin'
346
+ app.config.settings.password # => '123test'
347
+
348
+ # access to the class level config from an instance
349
+ app.shared_config.settings.user # => '0exp'
350
+ app.shared_config.settings.password # => 'test123'
351
+
352
+ # and etc... (all Qonfig-related features)
353
+ ```
354
+
355
+ ```ruby
356
+ # --- inheritance ---
357
+
358
+ class BasicApplication
359
+ # make configurable
360
+ include Qonfig::Configurable
361
+
362
+ configuration do
363
+ setting :user
364
+ setting :pswd
365
+ end
366
+
367
+ configure do |conf|
368
+ conf.user = 'admin'
369
+ conf.pswd = 'admin'
370
+ end
371
+ end
372
+
373
+ class GeneralApplication < BasicApplication
374
+ # extend inherited definitions
375
+ configuration do
376
+ setting :db do
377
+ setting :adapter
378
+ end
379
+ end
380
+
381
+ configure do |conf|
382
+ conf.user = '0exp' # .user inherited from BasicApplication
383
+ conf.pswd = '123test' # .pswd inherited from BasicApplication
384
+ conf.db.adapter = 'pg'
385
+ end
386
+ end
387
+
388
+ BasicApplication.config.to_h
389
+ { 'user' => 'admin', 'pswd' => 'admin' }
390
+
391
+ GeneralApplication.config.to_h
392
+ { 'user' => '0exp', 'pswd' => '123test', 'db' => { 'adapter' => 'pg' } }
393
+
394
+ # and etc... (all Qonfig-related features)
395
+ ```
396
+
397
+ ---
398
+
399
+
400
+ ## Interaction
401
+
402
+ ---
403
+
404
+ ### Iteration over setting keys
405
+
406
+ - `#each_setting { |key, value| }`
407
+ - iterates over the root setting keys;
408
+ - `#deep_each_setting { |key, value| }`
409
+ - iterates over all setting keys (deep inside);
410
+ - key object is represented as a string of `.`-joined keys;
411
+
412
+ ```ruby
413
+ class Config < Qonfig::DataSet
414
+ setting :db do
415
+ setting :creds do
416
+ setting :user, 'D@iVeR'
417
+ setting :password, 'test123',
418
+ setting :data, test: false
419
+ end
420
+ end
421
+
422
+ setting :telegraf_url, 'udp://localhost:8094'
423
+ setting :telegraf_prefix, 'test'
424
+ end
425
+
426
+ config = Config.new
427
+
428
+ # 1. #each_setting
429
+ config.each_setting { |key, value| { key => value } }
430
+ # result of each step:
431
+ { 'db' => <Qonfig::Settings:0x00007ff8> }
432
+ { 'telegraf_url' => 'udp://localhost:8094' }
433
+ { 'telegraf_prefix' => 'test' }
434
+
435
+ # 2. #deep_each_setting
436
+ config.deep_each_setting { |key, value| { key => value } }
437
+ # result of each step:
438
+ { 'db.creds.user' => 'D@iveR' }
439
+ { 'db.creds.password' => 'test123' }
440
+ { 'db.creds.data' => { test: false } }
441
+ { 'telegraf_url' => 'udp://localhost:8094' }
442
+ { 'telegraf_prefix' => 'test' }
443
+ ```
444
+
445
+ ---
446
+
277
447
### Config reloading
278
448
279
449
```ruby
@@ -423,6 +593,264 @@ config.settings.database.engine.driver? # => true (true => true)
423
593
424
594
---
425
595
596
+ ## Validation
597
+
598
+ Qonfig provides a lightweight DSL for defining validations and works in all cases when setting values are initialized or mutated.
599
+ Settings are validated as keys (matched with a [specific string pattern](#key-search-patern)).
600
+ You can validate both a set of keys and each key separately.
601
+ If you want to check the config object completely you can define a custom validation.
602
+
603
+ **Features**:
604
+
605
+ - is invoked on any mutation of any setting key
606
+ - during dataset instantiation;
607
+ - when assigning new values;
608
+ - when calling `#reload!`;
609
+ - when calling `#clear!`;
610
+
611
+ - provides special [key search pattern](#key-search-pattern) for matching setting key names;
612
+ - uses the [key search pattern](#key-search-pattern) for definging what the setting key should be validated;
613
+ - you can define your own custom validation logic and validate dataset instance completely;
614
+ - validation logic should return **truthy** or **falsy** value;
615
+
616
+ - supprots two validation techniques (**proc-based** and **dataset-method-based**)
617
+ - **proc-based** (`setting validation`)
618
+ ```ruby
619
+ validate 'db.user' do |value|
620
+ value.is_a?(String)
621
+ end
622
+ ```
623
+ - **proc-based** (`dataset validation`)
624
+ ```ruby
625
+ validate do
626
+ settings.user == User[1]
627
+ end
628
+ ```
629
+ - **dataset-method-based** (`setting validation`)
630
+ ```ruby
631
+ validate 'db.user', by: :check_user
632
+
633
+ def check_user(value)
634
+ value.is_a?(String)
635
+ end
636
+ ```
637
+ - **dataset-method-based** (`dataset validation`)
638
+ ```ruby
639
+ validate by: :check_config
640
+
641
+ def check_config
642
+ settings.user == User[1]
643
+ end
644
+ ```
645
+
646
+ - provides a set of standard validations:
647
+ - `integer`
648
+ - `float`
649
+ - `numeric`
650
+ - `big_decimal`
651
+ - `boolean`
652
+ - `string`
653
+ - `symbol`
654
+ - `text` (string or symbol)
655
+ - `array`
656
+ - `hash`
657
+ - `proc`
658
+ - `class`
659
+ - `module`
660
+ - `not_nil`
661
+
662
+ ---
663
+
664
+ ### Key search pattern
665
+
666
+ **Key search pattern** works according to the following rules:
667
+
668
+ - works in `RabbitMQ`-like key pattern ruleses;
669
+ - has a string format;
670
+ - nested configs are defined by a set of keys separated by `.`-symbol;
671
+ - if the setting key name at the current nesting level does not matter - use `*`;
672
+ - if both the setting key name and nesting level does not matter - use `#`
673
+ - examples:
674
+ - `db.settings.user` - matches to `db.settings.user` setting;
675
+ - `db.settings.*` - matches to all setting keys inside `db.settings` group of settings;
676
+ - `db.*.user` - matches to all `user` setting keys at the first level of `db` group of settings;
677
+ - `#.user` - matches to all `user` setting keys;
678
+ - `service.#.password` - matches to all `password` setting keys at all levels of `service` group of settings;
679
+ - `#` - matches to ALL setting keys;
680
+ - `*` - matches to all setting keys at the root level;
681
+ - and etc;
682
+
683
+ ---
684
+
685
+ ### Proc-based validation
686
+
687
+ - your proc should return truthy value or falsy value;
688
+ - how to validate setting keys:
689
+ - define proc with attribute: `validate 'your.setting.path' do |value|; end`
690
+ - proc will receive setting value;
691
+ - how to validate dataset instance:
692
+ - define proc without setting key pattern: `validate do; end`
693
+
694
+ ```ruby
695
+ class Config < Qonfig::DataSet
696
+ setting :db do
697
+ setting :user, 'D@iVeR'
698
+ setting :password, 'test123'
699
+ end
700
+
701
+ setting :service do
702
+ setting :address, 'google.ru'
703
+ setting :protocol, 'https'
704
+
705
+ setting :creds do
706
+ seting :admin, 'D@iVeR'
707
+ end
708
+ end
709
+
710
+ setting :enabled, false
711
+
712
+ # validates:
713
+ # - db.password
714
+ validate 'db.password' do |value|
715
+ value.is_a?(String)
716
+ end
717
+
718
+ # validates:
719
+ # - service.address
720
+ # - service.protocol
721
+ # - service.creds.user
722
+ validate 'service.#' do |value|
723
+ value.is_a?(String)
724
+ end
725
+
726
+ # validates:
727
+ # - dataset instance
728
+ validate do # NOTE: no setting key pattern
729
+ settings.enabled == false
730
+ end
731
+ end
732
+
733
+ config = Config.new
734
+ config.settings.db.password = 123 # => Qonfig::ValidationError (should be a string)
735
+ config.settings.service.address = 123 # => Qonfig::ValidationError (should be a string)
736
+ config.settings.service.protocol = :http # => Qonfig::ValidationError (should be a string)
737
+ config.settings.service.creds.admin = :billikota # => Qonfig::ValidationError (should be a string)
738
+ config.settings.enabled = true # => Qonfig::ValidationError (isnt `true`)
739
+ ```
740
+
741
+ ---
742
+
743
+ ### Method-based validation
744
+
745
+ - method should return truthy value or falsy value;
746
+ - how to validate setting keys:
747
+ - define validation: `validate 'db.*.user', by: :your_custom_method`;
748
+ - define your method with attribute: `def your_custom_method(setting_value); end`
749
+ - how to validate config instance
750
+ - define validation: `validate by: :your_custom_method`
751
+ - define your method without attributes: `def your_custom_method; end`
752
+
753
+ ```ruby
754
+ class Config < Qonfig::DataSet
755
+ setting :services do
756
+ setting :counts do
757
+ setting :google, 2
758
+ setting :rambler, 3
759
+ end
760
+
761
+ setting :minimals do
762
+ setting :google, 1
763
+ setting :rambler, 0
764
+ end
765
+ end
766
+
767
+ setting :enabled, true
768
+
769
+ # validates:
770
+ # - services.counts.google
771
+ # - services.counts.rambler
772
+ # - services.minimals.google
773
+ # - services.minimals.rambler
774
+ validate 'services.#', by: :check_presence
775
+
776
+ # validates:
777
+ # - dataset instance
778
+ validate by: :check_state # NOTE: no setting key pattern
779
+
780
+ def check_presence(value)
781
+ value.is_a?(Numeric) && value > 0
782
+ end
783
+
784
+ def check_state
785
+ settings.enabled.is_a?(TrueClass) || settings.enabled.is_a?(FalseClass)
786
+ end
787
+ end
788
+
789
+ config = Config.new
790
+
791
+ config.settings.counts.google = 0 # => Qonfig::ValidationError (< 0)
792
+ config.settings.counts.rambler = nil # => Qonfig::ValidationError (should be a numeric)
793
+ config.settings.minimals.google = -1 # => Qonfig::ValidationError (< 0)
794
+ config.settings.minimals.rambler = 'no' # => Qonfig::ValidationError (should be a numeric)
795
+ config.settings.enabled = nil # => Qonfig::ValidationError (should be a boolean)
796
+ ```
797
+
798
+ ---
799
+
800
+ ### Predefined validations
801
+
802
+ - DSL: `validate 'key.pattern', :predefned_validator`
803
+ - predefined validators:
804
+ - `:not_nil`
805
+ - `:integer`
806
+ - `:float`
807
+ - `:numeric`
808
+ - `:big_decimal`
809
+ - `:array`
810
+ - `:hash`
811
+ - `:string`
812
+ - `:symbol`
813
+ - `:text` (`string` or `symbol`)
814
+ - `:boolean`
815
+ - `:class`
816
+ - `:module`
817
+ - `:proc`
818
+
819
+ ```ruby
820
+ class Config < Qonfig::DataSet
821
+ setting :user
822
+ setting :password
823
+
824
+ setting :service do
825
+ setting :provider
826
+ setting :protocol
827
+ setting :on_fail, -> { puts 'atata!' }
828
+ end
829
+
830
+ setting :ignorance, false
831
+
832
+ validate 'user', :string
833
+ validate 'password', :string
834
+ validate 'service.provider', :text
835
+ validate 'service.protocol', :text
836
+ validate 'service.on_fail', :proc
837
+ validate 'ignorance', :not_nil
838
+ end
839
+
840
+ config = Config.new do |conf|
841
+ conf.user = 'D@iVeR'
842
+ conf.password = 'test123'
843
+ conf.service.provider = :google
844
+ conf.service.protocol = :https
845
+ end # NOTE: all right :)
846
+
847
+ config.settings.ignorance = nil # => Qonfig::ValidationError (cant be nil)
848
+ ```
849
+
850
+ ---
851
+
852
+ ## Work with files
853
+
426
854
### Load from YAML file
427
855
428
856
- supports `ERB`;
@@ -922,116 +1350,6 @@ dynamic: 10
922
1350
923
1351
---
924
1352
925
- ### Smart Mixin
926
-
927
- - class-level:
928
- - `.configuration` - settings definitions;
929
- - `.configure` - configuration;
930
- - `.config` - config object;
931
- - settings definitions are inheritable;
932
- - instance-level:
933
- - `#configure` - configuration;
934
- - `#config` - config object;
935
- - `#shared_config` - class-level config object;
936
-
937
- ```ruby
938
- # --- usage ---
939
-
940
- class Application
941
- # make configurable
942
- include Qonfig::Configurable
943
-
944
- configuration do
945
- setting :user
946
- setting :password
947
- end
948
- end
949
-
950
- app = Application.new
951
-
952
- # class-level config
953
- Application.config.settings.user # => nil
954
- Application.config.settings.password # => nil
955
-
956
- # instance-level config
957
- app.config.settings.user # => nil
958
- app.config.settings.password # => nil
959
-
960
- # access to the class level config from an instance
961
- app.shared_config.settings.user # => nil
962
- app.shared_config.settings.password # => nil
963
-
964
- # class-level configuration
965
- Application.configure do |conf|
966
- conf.user = '0exp'
967
- conf.password = 'test123'
968
- end
969
-
970
- # instance-level configuration
971
- app.configure do |conf|
972
- conf.user = 'admin'
973
- conf.password = '123test'
974
- end
975
-
976
- # class has own config object
977
- Application.config.settings.user # => '0exp'
978
- Application.config.settings.password # => 'test123'
979
-
980
- # instance has own config object
981
- app.config.settings.user # => 'admin'
982
- app.config.settings.password # => '123test'
983
-
984
- # access to the class level config from an instance
985
- app.shared_config.settings.user # => '0exp'
986
- app.shared_config.settings.password # => 'test123'
987
-
988
- # and etc... (all Qonfig-related features)
989
- ```
990
-
991
- ```ruby
992
- # --- inheritance ---
993
-
994
- class BasicApplication
995
- # make configurable
996
- include Qonfig::Configurable
997
-
998
- configuration do
999
- setting :user
1000
- setting :pswd
1001
- end
1002
-
1003
- configure do |conf|
1004
- conf.user = 'admin'
1005
- conf.pswd = 'admin'
1006
- end
1007
- end
1008
-
1009
- class GeneralApplication < BasicApplication
1010
- # extend inherited definitions
1011
- configuration do
1012
- setting :db do
1013
- setting :adapter
1014
- end
1015
- end
1016
-
1017
- configure do |conf|
1018
- conf.user = '0exp' # .user inherited from BasicApplication
1019
- conf.pswd = '123test' # .pswd inherited from BasicApplication
1020
- conf.db.adapter = 'pg'
1021
- end
1022
- end
1023
-
1024
- BasicApplication.config.to_h
1025
- { 'user' => 'admin', 'pswd' => 'admin' }
1026
-
1027
- GeneralApplication.config.to_h
1028
- { 'user' => '0exp', 'pswd' => '123test', 'db' => { 'adapter' => 'pg' } }
1029
-
1030
- # and etc... (all Qonfig-related features)
1031
- ```
1032
-
1033
- ---
1034
-
1035
1353
### Plugins
1036
1354
1037
1355
```ruby
@@ -1047,23 +1365,25 @@ Qonfig.plugin(:plugin_name) # or Qonfig.plugin('plugin_name')
1047
1365
### Plugins: toml
1048
1366
1049
1367
- adds support for `toml` format ([specification](https://github.com/toml-lang/toml));
1050
- - depends on `toml-rb` gem;
1051
- - provides `load_from_toml` (works in `load_from_yaml` manner [doc](#load-from-yaml-file));
1052
- - provides `save_to_toml` (works in `save_to_yaml` manner [doc](#save-to-yaml-file)) (`toml-rb` has no native options);
1053
- - provides `expose_toml` (works in `expose_yaml` manner [doc](#expose-yaml));
1368
+ - depends on `toml-rb` gem ([link](https://github.com/emancu/toml-rb));
1369
+ - supports TOML `0.4.0` format (dependency lock);
1370
+ - provides `load_from_toml` (works in `load_from_yaml` manner ([doc](#load-from-yaml-file)));
1371
+ - provides `save_to_toml` (works in `save_to_yaml` manner ([doc](#save-to-yaml-file))) (`toml-rb` has no native options);
1372
+ - provides `expose_toml` (works in `expose_yaml` manner ([doc](#expose-yaml)));
1054
1373
1055
1374
```ruby
1375
+ # 1) require external dependency
1056
1376
require 'toml-rb'
1377
+
1378
+ # 2) enable plugin
1057
1379
Qonfig.plugin(:toml)
1058
- # and use :)
1380
+
1381
+ # 3) use :)
1059
1382
```
1060
1383
---
1061
1384
1062
1385
## Roadmap
1063
1386
1064
- - support for TOML format;
1065
- - explicit "settings" object;
1066
- - validation layer;
1067
1387
- distributed configuration server;
1068
1388
- support for Rails-like secrets;
1069
1389
data/lib/qonfig.rb CHANGED
@@ -10,6 +10,7 @@ module Qonfig
10
10
require_relative 'qonfig/uploaders'
11
11
require_relative 'qonfig/commands'
12
12
require_relative 'qonfig/command_set'
13
+ require_relative 'qonfig/validator'
13
14
require_relative 'qonfig/settings'
14
15
require_relative 'qonfig/dsl'
15
16
require_relative 'qonfig/data_set'
data/lib/qonfig/command_set.rb CHANGED
@@ -3,12 +3,18 @@
3
3
# @api private
4
4
# @since 0.1.0
5
5
class Qonfig::CommandSet
6
+ # @api private
7
+ # @since 0.13.0
8
+ include Enumerable
9
+
6
10
# @return [Array<Qonfig::Commands::Base>]
7
11
#
8
12
# @api private
9
13
# @since 0.1.0
10
14
attr_reader :commands
11
15
16
+ # @return [void]
17
+ #
12
18
# @api private
13
19
# @since 0.1.0
14
20
def initialize
@@ -57,7 +63,7 @@ class Qonfig::CommandSet
57
63
private
58
64
59
65
# @param block [Proc]
60
- # @return [Object]
66
+ # @return [Any]
61
67
#
62
68
# @api private
63
69
# @since 0.2.0
data/lib/qonfig/commands/add_nested_option.rb CHANGED
@@ -32,14 +32,16 @@ class Qonfig::Commands::AddNestedOption < Qonfig::Commands::Base
32
32
end
33
33
end
34
34
35
+ # @param data_set [Qonfig::DataSet]
35
36
# @param settings [Qonfig::Settings]
36
37
# @return [void]
37
38
#
38
39
# @api private
39
40
# @since 0.1.0
40
- def call(settings)
41
+ def call(data_set, settings)
41
42
nested_settings = nested_data_set_klass.new.settings
42
43
44
+ nested_settings.__mutation_callbacks__.add(settings.__mutation_callbacks__)
43
45
settings.__define_setting__(key, nested_settings)
44
46
end
45
47
end
data/lib/qonfig/commands/add_option.rb CHANGED
@@ -30,12 +30,13 @@ class Qonfig::Commands::AddOption < Qonfig::Commands::Base
30
30
@value = value
31
31
end
32
32
33
+ # @param data_set [Qonfig::DataSet]
33
34
# @param settings [Qonfig::Settings]
34
35
# @return [void]
35
36
#
36
37
# @api private
37
38
# @since 0.1.0
38
- def call(settings)
39
+ def call(data_set, settings)
39
40
settings.__define_setting__(key, value)
40
41
end
41
42
end
data/lib/qonfig/commands/base.rb CHANGED
@@ -3,10 +3,11 @@
3
3
# @api private
4
4
# @since 0.1.0
5
5
class Qonfig::Commands::Base
6
+ # @param data_set [Qonfig::DataSet]
6
7
# @param settings [Qonfig::Settings]
7
8
# @return [void]
8
9
#
9
10
# @api private
10
11
# @since 0.1.0
11
- def call(settings); end
12
+ def call(data_set, settings); end
12
13
end
data/lib/qonfig/commands/compose.rb CHANGED
@@ -7,31 +7,37 @@ class Qonfig::Commands::Compose < Qonfig::Commands::Base
7
7
#
8
8
# @api private
9
9
# @since 0.1.0
10
- attr_reader :data_set_klass
10
+ attr_reader :composable_data_set_klass
11
11
12
- # @param data_set_klass [Qonfig::DataSet]
12
+ # @param composable_data_set_klass [Qonfig::DataSet]
13
13
#
14
14
# @raise [Qonfig::ArgumentError]
15
15
#
16
16
# @api private
17
17
# @since 0.1.0
18
- def initialize(data_set_klass)
19
- raise(
20
- Qonfig::ArgumentError,
21
- 'Composed config class should be a subtype of Qonfig::DataSet'
22
- ) unless data_set_klass.is_a?(Class) && data_set_klass < Qonfig::DataSet
18
+ def initialize(composable_data_set_klass)
19
+ unless composable_data_set_klass.is_a?(Class) && composable_data_set_klass < Qonfig::DataSet
20
+ raise(
21
+ Qonfig::ArgumentError,
22
+ 'Composed config class should be a subtype of Qonfig::DataSet'
23
+ )
24
+ end
23
25
24
- @data_set_klass = data_set_klass
26
+ @composable_data_set_klass = composable_data_set_klass
25
27
end
26
28
29
+ # @param data_set [Qonfig::DataSet]
27
30
# @param settings [Qonfig::Settings]
28
31
# @return [void]
29
32
#
30
33
# @api private
31
34
# @since 0.1.0
32
- def call(settings)
33
- composite_settings = data_set_klass.new.settings
35
+ def call(data_set, settings)
36
+ # NOTE: append new validators
37
+ data_set.class.validators.concat(composable_data_set_klass.validators.dup)
34
38
39
+ # NOTE: append new settings
40
+ composite_settings = composable_data_set_klass.new.settings
35
41
settings.__append_settings__(composite_settings)
36
42
end
37
43
end
data/lib/qonfig/commands/expose_yaml.rb CHANGED
@@ -60,12 +60,13 @@ class Qonfig::Commands::ExposeYAML < Qonfig::Commands::Base
60
60
@env = env
61
61
end
62
62
63
+ # @param data_set [Qonfig::DataSet]
63
64
# @param settings [Qonfig::Settings]
64
65
# @return [void]
65
66
#
66
67
# @api private
67
68
# @since 0.7.0
68
- def call(settings)
69
+ def call(data_set, settings)
69
70
case via
70
71
when EXPOSERS[:file_name]
71
72
expose_file_name!(settings)
data/lib/qonfig/commands/load_from_env.rb CHANGED
@@ -55,12 +55,13 @@ class Qonfig::Commands::LoadFromENV < Qonfig::Commands::Base
55
55
@trim_pattern = prefix.is_a?(Regexp) ? prefix : /\A(#{Regexp.escape(prefix.to_s)})/m
56
56
end
57
57
58
+ # @param data_set [Qonfig::DataSet]
58
59
# @param settings [Qonfig::Settings]
59
60
# @return [void]
60
61
#
61
62
# @api private
62
63
# @since 0.2.0
63
- def call(settings)
64
+ def call(data_set, settings)
64
65
env_data = extract_env_data
65
66
66
67
env_based_settings = build_data_set_class(env_data).new.settings
data/lib/qonfig/commands/load_from_json.rb CHANGED
@@ -25,12 +25,13 @@ class Qonfig::Commands::LoadFromJSON < Qonfig::Commands::Base
25
25
@strict = strict
26
26
end
27
27
28
+ # @param data_set [Qonfig::DataSet]
28
29
# @param settings [Qonfig::Settings]
29
30
# @return [void]
30
31
#
31
32
# @api private
32
33
# @since 0.5.0
33
- def call(settings)
34
+ def call(data_set, settings)
34
35
json_data = Qonfig::Loaders::JSON.load_file(file_path, fail_on_unexist: strict)
35
36
36
37
raise(
data/lib/qonfig/commands/load_from_self.rb CHANGED
@@ -17,12 +17,13 @@ class Qonfig::Commands::LoadFromSelf < Qonfig::Commands::Base
17
17
@caller_location = caller_location
18
18
end
19
19
20
+ # @param data_set [Qonfig::DataSet]
20
21
# @param settings [Qonfig::Settings]
21
22
# @return [void]
22
23
#
23
24
# @api private
24
25
# @since 0.2.0
25
- def call(settings)
26
+ def call(data_set, settings)
26
27
yaml_data = load_self_placed_yaml_data
27
28
28
29
yaml_based_settings = build_data_set_klass(yaml_data).new.settings
data/lib/qonfig/commands/load_from_yaml.rb CHANGED
@@ -25,6 +25,7 @@ class Qonfig::Commands::LoadFromYAML < Qonfig::Commands::Base
25
25
@strict = strict
26
26
end
27
27
28
+ # @param data_set [Qonfig::DataSet]
28
29
# @param settings [Qonfig::Settings]
29
30
# @return [void]
30
31
#
@@ -32,7 +33,7 @@ class Qonfig::Commands::LoadFromYAML < Qonfig::Commands::Base
32
33
#
33
34
# @api private
34
35
# @since 0.2.0
35
- def call(settings)
36
+ def call(data_set, settings)
36
37
yaml_data = Qonfig::Loaders::YAML.load_file(file_path, fail_on_unexist: strict)
37
38
38
39
raise(
data/lib/qonfig/data_set.rb CHANGED
@@ -2,29 +2,30 @@
2
2
3
3
# @api public
4
4
# @since 0.1.0
5
- class Qonfig::DataSet
5
+ class Qonfig::DataSet # rubocop:disable Metrics/ClassLength
6
6
require_relative 'data_set/class_builder'
7
- require_relative 'data_set/validator'
7
+ require_relative 'data_set/lock'
8
8
9
9
# @since 0.1.0
10
10
extend Qonfig::DSL
11
11
12
+ # @since 0.13.0
13
+ extend Qonfig::Validator::DSL
14
+
12
15
# @return [Qonfig::Settings]
13
16
#
14
17
# @api private
15
18
# @since 0.1.0
16
19
attr_reader :settings
17
20
18
- # @param options_map [Hash]
21
+ # @param settings_map [Hash]
19
22
# @param configurations [Proc]
20
23
#
21
24
# @api public
22
25
# @since 0.1.0
23
- def initialize(options_map = {}, &configurations)
24
- @__access_lock__ = Mutex.new
25
- @__definition_lock__ = Mutex.new
26
+ def initialize(settings_map = {}, &configurations)
27
+ @__lock__ = Qonfig::DataSet::Lock.new
28
+ thread_safe_definition { load!(settings_map, &configurations) }
26
-
27
- thread_safe_definition { load!(options_map, &configurations) }
28
29
end
29
30
30
31
# @return [void]
@@ -43,7 +44,7 @@ class Qonfig::DataSet
43
44
thread_safe_access { settings.__is_frozen__ }
44
45
end
45
46
46
- # @param options_map [Hash]
47
+ # @param settings_map [Hash]
47
48
# @param configurations [Proc]
48
49
# @return [void]
49
50
#
@@ -51,22 +52,21 @@ class Qonfig::DataSet
51
52
#
52
53
# @api public
53
54
# @since 0.2.0
54
- def reload!(options_map = {}, &configurations)
55
+ def reload!(settings_map = {}, &configurations)
55
56
thread_safe_definition do
56
57
raise Qonfig::FrozenSettingsError, 'Frozen config can not be reloaded' if frozen?
57
- load!(options_map, &configurations)
58
+ load!(settings_map, &configurations)
58
59
end
59
60
end
60
61
61
- # @param options_map [Hash]
62
+ # @param settings_map [Hash]
62
63
# @return [void]
63
64
#
64
65
# @api public
65
66
# @since 0.1.0
66
- def configure(options_map = {})
67
+ def configure(settings_map = {}, &configurations)
67
68
thread_safe_access do
68
- settings.__apply_values__(options_map)
69
+ apply_settings(settings_map, &configurations)
69
- yield(settings) if block_given?
70
70
end
71
71
end
72
72
@@ -172,25 +172,94 @@ class Qonfig::DataSet
172
172
thread_safe_access { settings.__clear__ }
173
173
end
174
174
175
+ # @param block [Proc]
176
+ # @return [Enumerable]
177
+ #
178
+ # @yield [setting_key, setting_value]
179
+ # @yieldparam setting_key [String]
180
+ # @yieldparam setting_value [Object]
181
+ #
182
+ # @api public
183
+ # @since 0.13.0
184
+ def each_setting(&block)
185
+ thread_safe_access { settings.__each_setting__(&block) }
186
+ end
187
+
188
+ # @param block [Proc]
189
+ # @return [Enumerable]
190
+ #
191
+ # @yield [setting_key, setting_value]
192
+ # @yieldparam setting_key [String]
193
+ # @yieldparam setting_value [Object]
194
+ #
195
+ # @api public
196
+ # @since 0.13.0
197
+ def deep_each_setting(&block)
198
+ thread_safe_access { settings.__deep_each_setting__(&block) }
199
+ end
200
+
201
+ # @return [Boolean]
202
+ #
203
+ # @api public
204
+ # @since 0.13.0
205
+ def valid?
206
+ thread_safe_access { validator.valid? }
207
+ end
208
+
209
+ # @return [void]
210
+ #
211
+ # @api public
212
+ # @since 0.13.0
213
+ def validate!
214
+ thread_safe_access { validator.validate! }
215
+ end
216
+
175
217
private
176
218
177
- # @return [Qonfig::Settings]
219
+ # @return [Qonfig::Validator]
220
+ #
221
+ # @api private
222
+ # @since 0.13.0
223
+ attr_reader :validator
224
+
225
+ # @return [void]
178
226
#
179
227
# @api private
180
228
# @since 0.2.0
181
229
def build_settings
182
- Qonfig::Settings::Builder.build(self.class.commands.dup)
230
+ @settings = Qonfig::Settings::Builder.build(self)
231
+ validator.validate!
232
+ end
233
+
234
+ # @return [void]
235
+ #
236
+ # @api private
237
+ # @since 0.13.0
238
+ def build_validator
239
+ @validator = Qonfig::Validator.new(self)
240
+ end
241
+
242
+ # @param settings_map [Hash]
243
+ # @param configurations [Proc]
244
+ # @return [void]
245
+ #
246
+ # @api private
247
+ # @since 0.13.0
248
+ def apply_settings(settings_map = {}, &configurations)
249
+ settings.__apply_values__(settings_map)
250
+ yield(settings) if block_given?
183
251
end
184
252
185
- # @param options_map [Hash]
253
+ # @param settings_map [Hash]
186
254
# @param configurations [Proc]
187
255
# @return [void]
188
256
#
189
257
# @api private
190
258
# @since 0.2.0
191
- def load!(options_map = {}, &configurations)
192
- @settings = build_settings
193
- configure(options_map, &configurations)
259
+ def load!(settings_map = {}, &configurations)
260
+ build_validator
261
+ build_settings
262
+ apply_settings(settings_map, &configurations)
194
263
end
195
264
196
265
# @param instructions [Proc]
@@ -199,7 +268,7 @@ class Qonfig::DataSet
199
268
# @api private
200
269
# @since 0.2.0
201
270
def thread_safe_access(&instructions)
202
- @__access_lock__.synchronize(&instructions)
271
+ @__lock__.thread_safe_access(&instructions)
203
272
end
204
273
205
274
# @param instructions [Proc]
@@ -208,6 +277,6 @@ class Qonfig::DataSet
208
277
# @api private
209
278
# @since 0.2.0
210
279
def thread_safe_definition(&instructions)
211
- @__definition_lock__.synchronize(&instructions)
280
+ @__lock__.thread_safe_definition(&instructions)
212
281
end
213
282
end
data/lib/qonfig/data_set/lock.rb ADDED
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::DataSet::Lock
6
+ # @return [void]
7
+ #
8
+ # @api private
9
+ # @since 0.13.0
10
+ def initialize
11
+ @access_lock = Mutex.new
12
+ @definition_lock = Mutex.new
13
+ end
14
+
15
+ # @param instructions [Proc]
16
+ # @return [void]
17
+ #
18
+ # @api private
19
+ # @since 0.13.0
20
+ def thread_safe_access(&instructions)
21
+ access_lock.owned? ? yield : access_lock.synchronize(&instructions)
22
+ end
23
+
24
+ # @param instructions [Proc]
25
+ # @return [void]
26
+ #
27
+ # @api private
28
+ # @since 0.13.0
29
+ def thread_safe_definition(&instructions)
30
+ definition_lock.owned? ? yield : definition_lock.synchronize(&instructions)
31
+ end
32
+
33
+ private
34
+
35
+ # @return [Mutex]
36
+ #
37
+ # @api private
38
+ # @since 0.13.0
39
+ attr_reader :access_lock
40
+
41
+ # @return [Mutex]
42
+ #
43
+ # @api private
44
+ # @since 0.13.0
45
+ attr_reader :definition_lock
46
+ end
data/lib/qonfig/data_set/validator.rb DELETED
@@ -1,7 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # @api public
4
- # @since 0.?.0
5
- class Qonfig::DataSet::Validator
6
- # TODO: think about validation layer
7
- end
data/lib/qonfig/errors.rb CHANGED
@@ -10,6 +10,14 @@ module Qonfig
10
10
ArgumentError = Class.new(ArgumentError)
11
11
12
12
# @api public
13
+ # @since 0.13.0
14
+ ValidatorArgumentError = Class.new(ArgumentError)
15
+
16
+ # @api public
17
+ # @since 0.13.0
18
+ ValidationError = Class.new(Error)
19
+
20
+ # @api public
13
21
# @since 0.12.0
14
22
PluginError = Class.new(Error)
15
23
data/lib/qonfig/plugins/toml/commands/expose_toml.rb CHANGED
@@ -60,12 +60,13 @@ class Qonfig::Commands::ExposeTOML < Qonfig::Commands::Base
60
60
@env = env
61
61
end
62
62
63
+ # @param data_set [Qonfig::DataSet]
63
64
# @param settings [Qonfig::Settings]
64
65
# @return [void]
65
66
#
66
67
# @api private
67
68
# @since 0.12.0
68
- def call(settings)
69
+ def call(data_set, settings)
69
70
case via
70
71
when EXPOSERS[:file_name]
71
72
expose_file_name!(settings)
data/lib/qonfig/plugins/toml/commands/load_from_toml.rb CHANGED
@@ -25,12 +25,13 @@ class Qonfig::Commands::LoadFromTOML < Qonfig::Commands::Base
25
25
@strict = strict
26
26
end
27
27
28
+ # @param data_set [Qonfig::DataSet]
28
29
# @param settings [Qonfig::Settings]
29
30
# @return [void]
30
31
#
31
32
# @api private
32
33
# @since 0.12.0
33
- def call(settings)
34
+ def call(data_set, settings)
34
35
toml_data = Qonfig::Loaders::TOML.load_file(file_path, fail_on_unexist: strict)
35
36
toml_based_settings = build_data_set_class(toml_data).new.settings
36
37
settings.__append_settings__(toml_based_settings)
data/lib/qonfig/settings.rb CHANGED
@@ -2,11 +2,13 @@
2
2
3
3
# @api private
4
4
# @since 0.1.0
5
- # rubocop:disable Metrics/ClassLength
6
- class Qonfig::Settings
5
+ # rubocop:disable Metrics/ClassLength, Layout/ClassStructure
6
+ class Qonfig::Settings # NOTE: Layout/ClassStructure is disabled only for CORE_METHODS constant
7
+ require_relative 'settings/callbacks'
7
8
require_relative 'settings/lock'
8
9
require_relative 'settings/builder'
9
10
require_relative 'settings/key_guard'
11
+ require_relative 'settings/key_matcher'
10
12
11
13
# @return [Proc]
12
14
#
@@ -26,11 +28,49 @@ class Qonfig::Settings
26
28
# @since 0.1.0
27
29
attr_reader :__options__
28
30
31
+ # @return [Qonfig::Settings::Callbacks]
32
+ #
33
+ # @api private
34
+ # @since 0.13.0
35
+ attr_reader :__mutation_callbacks__
36
+
29
37
# @api private
30
38
# @since 0.1.0
31
- def initialize
39
+ def initialize(__mutation_callbacks__)
32
40
@__options__ = {}
33
41
@__lock__ = Lock.new
42
+ @__mutation_callbacks__ = __mutation_callbacks__
43
+ end
44
+
45
+ # @param block [Proc]
46
+ # @return [Enumerable]
47
+ #
48
+ # @yield [key, value]
49
+ # @yieldparam key [String]
50
+ # @yieldparam value [Object]
51
+ #
52
+ # @api private
53
+ # @since 0.13.0
54
+ def __each_setting__(&block)
55
+ __lock__.thread_safe_access do
56
+ __each_key_value_pair__(&block)
57
+ end
58
+ end
59
+
60
+ # @param initial_setting_key [String, NilClass]
61
+ # @param block [Proc]
62
+ # @return [Enumerable]
63
+ #
64
+ # @yield [key, value]
65
+ # @yieldparam key [String]
66
+ # @yieldparam value [Object]
67
+ #
68
+ # @api private
69
+ # @since 0.13.0
70
+ def __deep_each_setting__(initial_setting_key = nil, &block)
71
+ __lock__.thread_safe_access do
72
+ __deep_each_key_value_pair__(initial_setting_key, &block)
73
+ end
34
74
end
35
75
36
76
# @param key [Symbol, String]
@@ -39,7 +79,7 @@ class Qonfig::Settings
39
79
#
40
80
# @api private
41
81
# @since 0.1.0
42
- def __define_setting__(key, value)
82
+ def __define_setting__(key, value) # rubocop:disable Metrics/AbcSize
43
83
__lock__.thread_safe_definition do
44
84
key = __indifferently_accessable_option_key__(key)
45
85
@@ -48,13 +88,15 @@ class Qonfig::Settings
48
88
case
49
89
when !__options__.key?(key)
50
90
__options__[key] = value
51
- when __options__[key].is_a?(Qonfig::Settings) && value.is_a?(Qonfig::Settings)
91
+ when __is_a_setting__(__options__[key]) && __is_a_setting__(value)
52
92
__options__[key].__append_settings__(value)
53
93
else
54
94
__options__[key] = value
55
95
end
56
96
57
- __define_accessor__(key)
97
+ __define_option_reader__(key)
98
+ __define_option_writer__(key)
99
+ __define_option_predicate__(key)
58
100
end
59
101
end
60
102
@@ -71,6 +113,14 @@ class Qonfig::Settings
71
113
end
72
114
end
73
115
116
+ # @return [void]
117
+ #
118
+ # @api private
119
+ # @since 0.13.0
120
+ def __invoke_mutation_callbacks__
121
+ __mutation_callbacks__.call
122
+ end
123
+
74
124
# @param key [Symbol, String]
75
125
# @return [Object]
76
126
#
@@ -90,13 +140,13 @@ class Qonfig::Settings
90
140
__lock__.thread_safe_access { __set_value__(key, value) }
91
141
end
92
142
93
- # @param options_map [Hash]
143
+ # @param settings_map [Hash]
94
144
# @return [void]
95
145
#
96
146
# @api private
97
147
# @since 0.3.0
98
- def __apply_values__(options_map)
99
- __lock__.thread_safe_access { __set_values_from_map__(options_map) }
148
+ def __apply_values__(settings_map)
149
+ __lock__.thread_safe_access { __set_values_from_map__(settings_map) }
100
150
end
101
151
102
152
# @param keys [Array<String, Symbol>]
@@ -191,7 +241,7 @@ class Qonfig::Settings
191
241
__options__.freeze
192
242
193
243
__options__.each_value do |value|
194
- value.__freeze__ if value.is_a?(Qonfig::Settings)
244
+ value.__freeze__ if __is_a_setting__(value)
195
245
end
196
246
end
197
247
end
@@ -204,6 +254,15 @@ class Qonfig::Settings
204
254
__lock__.thread_safe_access { __options__.frozen? }
205
255
end
206
256
257
+ # @param value [Any]
258
+ # @return [Boolean]
259
+ #
260
+ # @api private
261
+ # @since 0.13.0
262
+ def __is_a_setting__(value)
263
+ value.is_a?(Qonfig::Settings)
264
+ end
265
+
207
266
private
208
267
209
268
# @return [Qonfig::Settings::Lock]
@@ -212,7 +271,47 @@ class Qonfig::Settings
212
271
# @since 0.2.0
213
272
attr_reader :__lock__
214
273
215
- # @param options_map [Hash]
274
+ # @param block [Proc]
275
+ # @return [Enumerable]
276
+ #
277
+ # @yield [setting_key, setting_value]
278
+ # @yieldparam key [String]
279
+ # @yieldparam value [Object]
280
+ #
281
+ # @api private
282
+ # @since 0.13.0
283
+ def __each_key_value_pair__(&block)
284
+ __options__.each_pair(&block)
285
+ end
286
+
287
+ # @param initial_setting_key [String, NilClass]
288
+ # @param block [Proc]
289
+ # @return [Enumerable]
290
+ #
291
+ # @yield [setting_key, setting_value]
292
+ # @yieldparam setting_key [String]
293
+ # @yieldparam setting_value [Object]
294
+ #
295
+ # @api private
296
+ # @since 0.13.0
297
+ def __deep_each_key_value_pair__(initial_setting_key = nil, &block)
298
+ enumerator = Enumerator.new do |yielder|
299
+ __each_key_value_pair__ do |setting_key, setting_value|
300
+ final_setting_key =
301
+ initial_setting_key ? "#{initial_setting_key}.#{setting_key}" : setting_key
302
+
303
+ if __is_a_setting__(setting_value)
304
+ setting_value.__deep_each_setting__(final_setting_key, &block)
305
+ else
306
+ yielder.yield(final_setting_key, setting_value)
307
+ end
308
+ end
309
+ end
310
+
311
+ block_given? ? enumerator.each(&block) : enumerator
312
+ end
313
+
314
+ # @param settings_map [Hash]
216
315
# @return [void]
217
316
#
218
317
# @raise [Qonfig::ArgumentError]
@@ -220,21 +319,21 @@ class Qonfig::Settings
220
319
#
221
320
# @api private
222
321
# @since 0.3.0
223
- def __set_values_from_map__(options_map)
322
+ def __set_values_from_map__(settings_map)
224
323
::Kernel.raise(
225
324
Qonfig::ArgumentError, 'Options map should be represented as a hash'
226
- ) unless options_map.is_a?(Hash)
325
+ ) unless settings_map.is_a?(Hash)
227
326
228
- options_map.each_pair do |key, value|
327
+ settings_map.each_pair do |key, value|
229
328
current_value = __get_value__(key)
230
329
231
330
# NOTE: some duplications here was made only for the better code readability
232
331
case
233
- when !current_value.is_a?(Qonfig::Settings)
332
+ when !__is_a_setting__(current_value)
234
333
__set_value__(key, value)
235
- when current_value.is_a?(Qonfig::Settings) && value.is_a?(Hash)
334
+ when __is_a_setting__(current_value) && value.is_a?(Hash)
236
335
current_value.__apply_values__(value)
237
- when current_value.is_a?(Qonfig::Settings) && !value.is_a?(Hash)
336
+ when __is_a_setting__(current_value) && !value.is_a?(Hash)
238
337
::Kernel.raise(
239
338
Qonfig::AmbiguousSettingValueError,
240
339
"Can not redefine option <#{key}> that contains nested options"
@@ -255,12 +354,10 @@ class Qonfig::Settings
255
354
) if __options__.frozen?
256
355
257
356
__options__.each_pair do |key, value|
258
- if value.is_a?(Qonfig::Settings)
357
+ __is_a_setting__(value) ? value.__clear__ : __options__[key] = nil
259
- value.__clear__
260
- else
261
- __options__[key] = nil
262
- end
263
358
end
359
+
360
+ __invoke_mutation_callbacks__
264
361
end
265
362
266
363
# @param key [String, Symbol]
@@ -301,14 +398,16 @@ class Qonfig::Settings
301
398
::Kernel.raise(Qonfig::FrozenSettingsError, 'Can not modify frozen settings')
302
399
end
303
400
304
- if __options__[key].is_a?(Qonfig::Settings)
401
+ if __is_a_setting__(__options__[key])
305
402
::Kernel.raise(
306
403
Qonfig::AmbiguousSettingValueError,
307
404
"Can not redefine option <#{key}> that contains nested options"
308
405
)
309
406
end
310
407
311
- __options__[key] = value
408
+ (__options__[key] = value)
409
+
410
+ __invoke_mutation_callbacks__
312
411
end
313
412
314
413
# @param keys [Array<Symbol, String>]
@@ -328,12 +427,12 @@ class Qonfig::Settings
328
427
case
329
428
when rest_keys.empty?
330
429
result
331
- when !result.is_a?(Qonfig::Settings)
430
+ when !__is_a_setting__(result)
332
431
::Kernel.raise(
333
432
Qonfig::UnknownSettingError,
334
433
'Setting with required digging sequence does not exist!'
335
434
)
336
- when result.is_a?(Qonfig::Settings)
435
+ when __is_a_setting__(result)
337
436
result.__dig__(*rest_keys)
338
437
end
339
438
end
@@ -350,7 +449,7 @@ class Qonfig::Settings
350
449
{}.tap do |result|
351
450
__deep_access__(*keys).tap do |setting|
352
451
required_key = __indifferently_accessable_option_key__(keys.last)
353
- result[required_key] = setting.is_a?(Qonfig::Settings) ? setting.__to_h__ : setting
452
+ result[required_key] = __is_a_setting__(setting) ? setting.__to_h__ : setting
354
453
end
355
454
end
356
455
end
@@ -386,7 +485,7 @@ class Qonfig::Settings
386
485
transform_key: transform_key,
387
486
transform_value: transform_value
388
487
)
389
- when value.is_a?(Qonfig::Settings)
488
+ when __is_a_setting__(value)
390
489
hash[final_key] = value.__to_hash__(
391
490
transform_key: transform_key,
392
491
transform_value: transform_value
@@ -402,16 +501,30 @@ class Qonfig::Settings
402
501
# @return [void]
403
502
#
404
503
# @api private
405
- # @since 0.1.0
406
- def __define_accessor__(key)
504
+ # @since 0.13.0
505
+ def __define_option_reader__(key)
407
506
define_singleton_method(key) do
408
507
self.[](key)
409
508
end
509
+ end
410
510
511
+ # @param key [Symbol, String]
512
+ # @return [void]
513
+ #
514
+ # @api private
515
+ # @since 0.13.0
516
+ def __define_option_writer__(key)
411
517
define_singleton_method("#{key}=") do |value|
412
518
self.[]=(key, value)
413
519
end
520
+ end
414
521
522
+ # @param key [Symbol, String]
523
+ # @return [void]
524
+ #
525
+ # @api private
526
+ # @since 0.13.0
527
+ def __define_option_predicate__(key)
415
528
define_singleton_method("#{key}?") do
416
529
!!self.[](key)
417
530
end
@@ -442,7 +555,6 @@ class Qonfig::Settings
442
555
KeyGuard.new(key).prevent_core_method_intersection!
443
556
end
444
557
445
- # rubocop:disable Layout/ClassStructure
446
558
# @return [Array<String>]
447
559
#
448
560
# @api private
@@ -452,6 +564,5 @@ class Qonfig::Settings
452
564
private_instance_methods(false) |
453
565
%i[super define_singleton_method self]
454
566
).map(&:to_s).freeze
455
- # rubocop:enable Layout/ClassStructure
456
567
end
457
- # rubocop:enable Metrics/ClassLength
568
+ # rubocop:enable Metrics/ClassLength, Layout/ClassStructure
data/lib/qonfig/settings/builder.rb CHANGED
@@ -4,14 +4,30 @@
4
4
# @since 0.2.0
5
5
module Qonfig::Settings::Builder
6
6
class << self
7
- # @param commands [Qonfig::CommandSet]
7
+ # @param data_set [Qonfig::DataSet]
8
8
# @return [Qonfig::Settings]
9
9
#
10
10
# @api private
11
11
# @since 0.2.0
12
- def build(commands)
13
- Qonfig::Settings.new.tap do |settings|
14
- commands.each { |command| command.call(settings) }
12
+ def build(data_set)
13
+ Qonfig::Settings.new(build_mutation_callbacks(data_set)).tap do |settings|
14
+ data_set.class.commands.dup.each do |command|
15
+ command.call(data_set, settings)
16
+ end
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ # @param data_set [Qonfig::DataSet]
23
+ # @return [Qonfig::Settings::Callbacks]
24
+ #
25
+ # @api private
26
+ # @since 0.13.0
27
+ def build_mutation_callbacks(data_set)
28
+ Qonfig::Settings::Callbacks.new.tap do |callbacks|
29
+ # NOTE: validation callbacks
30
+ callbacks.add(proc { data_set.validate! })
15
31
end
16
32
end
17
33
end
data/lib/qonfig/settings/callbacks.rb ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Settings::Callbacks
6
+ # @api private
7
+ # @since 0.13.0
8
+ include Enumerable
9
+
10
+ # @return [void]
11
+ #
12
+ # @api private
13
+ # @since 0.13.0
14
+ def initialize
15
+ @callbacks = []
16
+ end
17
+
18
+ # @return [void]
19
+ #
20
+ # @api private
21
+ # @since 0.13.0
22
+ def call
23
+ callbacks.each(&:call)
24
+ end
25
+
26
+ # @param callback [Proc, Qonfig::Settings::Callbacks, #call]
27
+ # @return [void]
28
+ #
29
+ # @api private
30
+ # @since 0.13.0
31
+ def add(callback)
32
+ callbacks << callback
33
+ end
34
+ attr_reader :callback
35
+
36
+ private
37
+
38
+ # @return [Array<Proc>]
39
+ #
40
+ # @api private
41
+ # @since 0.13.0
42
+ attr_reader :callbacks
43
+ end
data/lib/qonfig/settings/key_matcher.rb ADDED
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Settings::KeyMatcher
6
+ # @return [String]
7
+ #
8
+ # @api private
9
+ # @since 0.13.0
10
+ SCOPE_SPLITTER = '.'
11
+
12
+ # @return [String]
13
+ #
14
+ # @api private
15
+ # @since 0.13.0
16
+ MATCHER_SCOPE_SPLITTER = '\.'
17
+
18
+ # @return [String]
19
+ #
20
+ # @api private
21
+ # @since 0.13.0
22
+ GENERIC_PART_PATTERN = '*'
23
+
24
+ # @return [String]
25
+ #
26
+ # @api private
27
+ # @since 0.13.0
28
+ GENERIC_REGEXP_PATTERN = '[^\.]+\.'
29
+
30
+ # @return [String]
31
+ #
32
+ # @api private
33
+ # @since 0.13.0
34
+ INFINITE_PART_PATTERN = '#'
35
+
36
+ # @return [String]
37
+ #
38
+ # @api private
39
+ # @since 0.13.0
40
+ INFINITE_REGEXP_PATTERN = '\.*.*'
41
+
42
+ # @param scope_pattern [String]
43
+ # @return [void]
44
+ #
45
+ # @raise [Qonfig::ArgumentError]
46
+ #
47
+ # @api private
48
+ # @since 0.13.0
49
+ def initialize(scope_pattern)
50
+ raise Qonfig::ArgumentError unless scope_pattern.is_a?(String)
51
+
52
+ @scope_pattern = scope_pattern
53
+ @scope_pattern_size = count_scope_pattern_size(scope_pattern)
54
+ @pattern_matcher = build_pattern_matcher(scope_pattern)
55
+ end
56
+
57
+ # @param setting_key_pattern [String]
58
+ # @return [Boolean]
59
+ #
60
+ # @api private
61
+ # @since 0.13.0
62
+ def match?(setting_key_pattern)
63
+ return false unless comparable_event_scopes?(setting_key_pattern)
64
+ !!pattern_matcher.match(setting_key_pattern)
65
+ end
66
+
67
+ private
68
+
69
+ # @return [Regexp]
70
+ #
71
+ # @api private
72
+ # @since 0.13.0
73
+ attr_reader :pattern_matcher
74
+
75
+ # @return [String]
76
+ #
77
+ # @api private
78
+ # @since 0.13.0
79
+ attr_reader :scope_pattern
80
+
81
+ # @return [Integer, Float::INFINITY]
82
+ #
83
+ # @api private
84
+ # @since 0.13.0
85
+ attr_reader :scope_pattern_size
86
+
87
+ # @param scope_pattern [String]
88
+ # @return [Integer, Float::INFINITY]
89
+ #
90
+ # @api private
91
+ # @since 0.13.0
92
+ def count_scope_pattern_size(scope_pattern)
93
+ return Float::INFINITY if scope_pattern == INFINITE_PART_PATTERN
94
+ return Float::INFINITY if scope_pattern.include?('.#')
95
+ return Float::INFINITY if scope_pattern.include?('#.')
96
+ return Float::INFINITY if scope_pattern.include?('.#.')
97
+
98
+ scope_pattern.split(SCOPE_SPLITTER).size
99
+ end
100
+
101
+ # @param setting_key_pattern [String]
102
+ # @return [Integer]
103
+ #
104
+ # @api private
105
+ # @since 0.13.0
106
+ def count_setting_key_pattern_size(setting_key_pattern)
107
+ setting_key_pattern.split(SCOPE_SPLITTER).size
108
+ end
109
+
110
+ # @param setting_key_pattern [String]
111
+ # @return [Boolean]
112
+ #
113
+ # @api private
114
+ # @since 0.13.0
115
+ def comparable_event_scopes?(setting_key_pattern)
116
+ # NOTE: Integer#finite?, Integer#infinite?, Float#finite?, Float#nfinite?
117
+ # Cant be used (backward compatability with old ruby versions)
118
+ return true if scope_pattern_size == Float::INFINITY
119
+ scope_pattern_size == count_setting_key_pattern_size(setting_key_pattern)
120
+ end
121
+
122
+ # @param pattern [String, NilClass]
123
+ # @return [Boolean]
124
+ #
125
+ # @api private
126
+ # @since 0.13.0
127
+ def non_generic_pattern?(pattern = nil)
128
+ return false unless pattern
129
+ pattern != GENERIC_REGEXP_PATTERN && pattern != INFINITE_REGEXP_PATTERN
130
+ end
131
+
132
+ # "\.test\.created\.today\." => "test\.created\.today"
133
+ #
134
+ # @param regexp_string [String]
135
+ # @option left [Boolean]
136
+ # @option right [Boolean]
137
+ # @return [String]
138
+ #
139
+ # @api private
140
+ # @since 0.13.0
141
+ def strip_regexp_string(regexp_string, left: false, right: false)
142
+ pattern = regexp_string
143
+ pattern = pattern[2..-1] if left && pattern[0..1] == MATCHER_SCOPE_SPLITTER
144
+ pattern = pattern[0..-3] if right && pattern[-2..-1] == MATCHER_SCOPE_SPLITTER
145
+ pattern
146
+ end
147
+
148
+ # @param scope_pattern [String]
149
+ # @return [Regexp]
150
+ #
151
+ # @api private
152
+ # @since 0.13.0
153
+ def build_pattern_matcher(scope_pattern)
154
+ routing_parts = scope_pattern.split(SCOPE_SPLITTER)
155
+
156
+ regexp_string = routing_parts.each_with_object([]) do |routing_part, regexp_parts|
157
+ case routing_part
158
+ when GENERIC_PART_PATTERN
159
+ regexp_parts << GENERIC_REGEXP_PATTERN
160
+ when INFINITE_PART_PATTERN
161
+ if non_generic_pattern?(regexp_parts.last)
162
+ regexp_parts[-1] = strip_regexp_string(regexp_parts.last, right: true)
163
+ end
164
+
165
+ regexp_parts << INFINITE_REGEXP_PATTERN
166
+ else
167
+ regexp_parts << (Regexp.escape(routing_part) + MATCHER_SCOPE_SPLITTER)
168
+ end
169
+ end.join
170
+
171
+ regexp_string = strip_regexp_string(regexp_string, left: true, right: true)
172
+
173
+ Regexp.new('\A' + regexp_string + '\z')
174
+ end
175
+ end
data/lib/qonfig/settings/lock.rb CHANGED
@@ -17,7 +17,7 @@ class Qonfig::Settings::Lock
17
17
# @api private
18
18
# @since 0.2.0
19
19
def thread_safe_definition(&instructions)
20
- definition_lock.synchronize(&instructions)
20
+ definition_lock.owned? ? yield : definition_lock.synchronize(&instructions)
21
21
end
22
22
23
23
# @param instructions [Proc]
@@ -26,7 +26,7 @@ class Qonfig::Settings::Lock
26
26
# @api private
27
27
# @since 0.2.0
28
28
def thread_safe_access(&instructions)
29
- access_lock.synchronize(&instructions)
29
+ access_lock.owned? ? yield : access_lock.synchronize(&instructions)
30
30
end
31
31
32
32
# @param instructions [Proc]
@@ -35,7 +35,7 @@ class Qonfig::Settings::Lock
35
35
# @api private
36
36
# @since 0.2.0
37
37
def thread_safe_merge(&instructions)
38
- merge_lock.synchronize(&instructions)
38
+ merge_lock.owned? ? yield : merge_lock.synchronize(&instructions)
39
39
end
40
40
41
41
private
data/lib/qonfig/validator.rb ADDED
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Validator
6
+ require_relative 'validator/basic'
7
+ require_relative 'validator/method_based'
8
+ require_relative 'validator/proc_based'
9
+ require_relative 'validator/builder'
10
+ require_relative 'validator/collection'
11
+ require_relative 'validator/predefined'
12
+ require_relative 'validator/dsl'
13
+
14
+ # @param data_set [Qonfig::DataSet]
15
+ # @return [void]
16
+ #
17
+ # @api private
18
+ # @since 0.13.0
19
+ def initialize(data_set)
20
+ @data_set = data_set
21
+ @validators = data_set.class.validators.dup
22
+ end
23
+
24
+ # @return [void]
25
+ #
26
+ # @api private
27
+ # @since 0.13.0
28
+ def validate!
29
+ validators.each do |validator|
30
+ validator.validate(data_set)
31
+ end
32
+ end
33
+
34
+ # @return [Boolean]
35
+ #
36
+ # @api private
37
+ # @since 0.13.0
38
+ def valid?
39
+ validate!
40
+ true
41
+ rescue Qonfig::ValidationError
42
+ false
43
+ end
44
+
45
+ private
46
+
47
+ # @return [Qonfig::DataSet]
48
+ #
49
+ # @api private
50
+ # @since 0.13.0
51
+ attr_reader :data_set
52
+
53
+ # @return [Qonfig::Validator::Collection]
54
+ #
55
+ # @api private
56
+ # @since 0.13.0
57
+ attr_reader :validators
58
+ end
data/lib/qonfig/validator/basic.rb ADDED
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Validator::Basic
6
+ # @return [String, Symbol, NilClass]
7
+ #
8
+ # @api private
9
+ # @since 0.13.0
10
+ attr_reader :setting_key_matcher
11
+
12
+ # @param setting_key_matcher [Qonfig::Settings::KeyMatcher, NilClass]
13
+ # @return [void]
14
+ #
15
+ # @api private
16
+ # @since 0.13.0
17
+ def initialize(setting_key_matcher)
18
+ @setting_key_matcher = setting_key_matcher
19
+ end
20
+
21
+ # @param data_set [Qonfig::DataSet]
22
+ # @return [Boolean]
23
+ #
24
+ # @api private
25
+ # @since 0.13.0
26
+ def validate(data_set)
27
+ setting_key_provided? ? validate_concrete(data_set) : validate_full(data_set)
28
+ end
29
+
30
+ private
31
+
32
+ # @return [Boolean]
33
+ #
34
+ # @api private
35
+ # @since 0.13.0
36
+ def setting_key_provided?
37
+ !setting_key_matcher.nil?
38
+ end
39
+
40
+ # @param data_set [Qonfig::DataSet]
41
+ # @return [Any]
42
+ #
43
+ # @api private
44
+ # @since 0.13.0
45
+ def validate_full(data_set); end
46
+
47
+ # @param data_set [Qonfig::DataSet]
48
+ # @return [Any]
49
+ #
50
+ # @api private
51
+ # @since 0.13.0
52
+ def validate_concrete(data_set); end
53
+ end
data/lib/qonfig/validator/builder.rb ADDED
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Validator::Builder
6
+ require_relative 'builder/attribute_consistency'
7
+
8
+ # @return [NilClass]
9
+ #
10
+ # @api private
11
+ # @since 0.13.0
12
+ EMPTY_SETTING_KEY_PATTERN = nil
13
+
14
+ # @return [NilClass]
15
+ #
16
+ # @api private
17
+ # @since 0.13.0
18
+ NO_RUNTIME_VALIDATION_METHOD = nil
19
+
20
+ # @return [NilClass]
21
+ #
22
+ # @api private
23
+ # @since 0.13.0
24
+ NO_VALIDATION_LOGIC = nil
25
+
26
+ # @return [NilClass]
27
+ #
28
+ # @api private
29
+ # @since 0.13.0
30
+ NO_PREDEFINED_VALIDATOR = nil
31
+
32
+ class << self
33
+ # @option setting_key_pattern [String, Symbol, NilClass]
34
+ # @option predefined_validator [String, Symbol, NilClass]
35
+ # @option runtime_validation_method [String, Symbol, NilClass]
36
+ # @option validation_logic [Proc, NilClass]
37
+ # @return [Qonfig::Validator::MethodBased, Qonfig::Validator::ProcBased]
38
+ #
39
+ # @api private
40
+ # @since 0.13.0
41
+ def build(
42
+ setting_key_pattern: EMPTY_SETTING_KEY_PATTERN,
43
+ runtime_validation_method: NO_RUNTIME_VALIDATION_METHOD,
44
+ validation_logic: NO_VALIDATION_LOGIC,
45
+ predefined_validator: NO_PREDEFINED_VALIDATOR
46
+ )
47
+ new(
48
+ setting_key_pattern,
49
+ predefined_validator,
50
+ runtime_validation_method,
51
+ validation_logic
52
+ ).build
53
+ end
54
+ end
55
+
56
+ # @param setting_key_pattern [String, Symbol, NilClass]
57
+ # @param predefined_validator [String, Symbol, NilClass]
58
+ # @param runtime_validation_method [String, Symbol, NilClass]
59
+ # @param validation_logic [Proc, NilClass]
60
+ # @return [void]
61
+ #
62
+ # @api private
63
+ # @since 0.13.0
64
+ def initialize(
65
+ setting_key_pattern,
66
+ predefined_validator,
67
+ runtime_validation_method,
68
+ validation_logic
69
+ )
70
+ @setting_key_pattern = setting_key_pattern
71
+ @predefined_validator = predefined_validator
72
+ @runtime_validation_method = runtime_validation_method
73
+ @validation_logic = validation_logic
74
+ end
75
+
76
+ # @return [Qonfig::Validator::MethodBased, Qonfig::Validator::ProcBased]
77
+ #
78
+ # @api private
79
+ # @since 0.13.0
80
+ def build
81
+ validate_attributes!
82
+ build_validator
83
+ end
84
+
85
+ private
86
+
87
+ # @return [String, Symbol, NilClass]
88
+ #
89
+ # @api private
90
+ # @since 0.13.0
91
+ attr_reader :setting_key_pattern
92
+
93
+ # @return [String, Symbol, NilClass]
94
+ #
95
+ # @api private
96
+ # @since 0.13.0
97
+ attr_reader :predefined_validator
98
+
99
+ # @return [String, Symbol, NilClass]
100
+ #
101
+ # @api private
102
+ # @since 0.13.0
103
+ attr_reader :runtime_validation_method
104
+
105
+ # @return [Proc, NilClass]
106
+ #
107
+ # @api private
108
+ # @since 0.13.0
109
+ attr_reader :validation_logic
110
+
111
+ # @return [void]
112
+ #
113
+ # @raise [Qonfig::ArgumentError]
114
+ #
115
+ # @api private
116
+ # @since 0.13.0
117
+ def validate_attributes!
118
+ AttributeConsistency.check!(
119
+ setting_key_pattern,
120
+ predefined_validator,
121
+ runtime_validation_method,
122
+ validation_logic
123
+ )
124
+ end
125
+
126
+ # @return [Qonfig::Validator::MethodBased, Qonfig::Validator::PorcBased]
127
+ #
128
+ # @api private
129
+ # @since 0.13.0
130
+ def build_validator
131
+ case
132
+ when predefined_validator then build_predefined
133
+ when runtime_validation_method then build_method_based
134
+ when validation_logic then build_proc_based
135
+ end
136
+ end
137
+
138
+ # @return [Qonfig::Settings::KeyMatcher, NilClass]
139
+ #
140
+ # @api private
141
+ # @since 0.13.0
142
+ def build_setting_key_matcher
143
+ Qonfig::Settings::KeyMatcher.new(setting_key_pattern.to_s) if setting_key_pattern
144
+ end
145
+
146
+ # @return [Qonfig::Validator::MethodBased]
147
+ #
148
+ # @api private
149
+ # @since 0.13.0
150
+ def build_method_based
151
+ Qonfig::Validator::MethodBased.new(build_setting_key_matcher, runtime_validation_method)
152
+ end
153
+
154
+ # @return [Qonfig::Validator::ProcBased]
155
+ #
156
+ # @api private
157
+ # @since 0.13.0
158
+ def build_proc_based
159
+ Qonfig::Validator::ProcBased.new(build_setting_key_matcher, validation_logic)
160
+ end
161
+
162
+ # @return [Qonfig::Settings::Predefined]
163
+ #
164
+ # @api private
165
+ # @since 0.13.0
166
+ def build_predefined
167
+ Qonfig::Validator::Predefined.build(predefined_validator, build_setting_key_matcher)
168
+ end
169
+ end
data/lib/qonfig/validator/builder/attribute_consistency.rb ADDED
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Validator::Builder::AttributeConsistency
6
+ class << self
7
+ # @param setting_key_pattern [String, Symbol, NilClass]
8
+ # @param predefined_validator [String, Symbol, NilClass]
9
+ # @param runtime_validation_method [String, Symbol, NilClass]
10
+ # @param validation_logic [Proc, NilClass]
11
+ # @return [void]
12
+ #
13
+ # @api private
14
+ # @since 0.13.0
15
+ def check!(
16
+ setting_key_pattern,
17
+ predefined_validator,
18
+ runtime_validation_method,
19
+ validation_logic
20
+ )
21
+ new(
22
+ setting_key_pattern,
23
+ predefined_validator,
24
+ runtime_validation_method,
25
+ validation_logic
26
+ ).check!
27
+ end
28
+ end
29
+
30
+ # @param setting_key_pattern [String, Symbol, NilClass]
31
+ # @param predefined_validator [String, Symbol, NilClass]
32
+ # @param runtime_validation_method [String, Symbol, NilClass]
33
+ # @param validation_logic [Proc, NilClass]
34
+ # @return [void]
35
+ #
36
+ # @api private
37
+ # @since 0.13.0
38
+ def initialize(
39
+ setting_key_pattern,
40
+ predefined_validator,
41
+ runtime_validation_method,
42
+ validation_logic
43
+ )
44
+ @setting_key_pattern = setting_key_pattern
45
+ @predefined_validator = predefined_validator
46
+ @runtime_validation_method = runtime_validation_method
47
+ @validation_logic = validation_logic
48
+ end
49
+
50
+ # @return [void]
51
+ #
52
+ # @raise [Qonfig::ValidatorArgumentError]
53
+ #
54
+ # @api private
55
+ # @since 0.13.0
56
+ def check!
57
+ consistent_method_choice!
58
+ consistent_predefined_validator!
59
+ cosnistent_runtime_validation_method!
60
+ consistent_validation_logic!
61
+ consistent_setting_key_pattern!
62
+ end
63
+
64
+ private
65
+
66
+ # @return [String, Symbol, NilClass]
67
+ #
68
+ # @api private
69
+ # @since 0.13.0
70
+ attr_reader :setting_key_pattern
71
+
72
+ # @return [String, Symbol, NilClass]
73
+ #
74
+ # @api private
75
+ # @since 0.13.0
76
+ attr_reader :predefined_validator
77
+
78
+ # @return [String, Symbol, NilClass]
79
+ #
80
+ # @api private
81
+ # @since 0.13.0
82
+ attr_reader :runtime_validation_method
83
+
84
+ # @return [Proc, NilClass]
85
+ #
86
+ # @api private
87
+ # @since 0.13.0
88
+ attr_reader :validation_logic
89
+
90
+ # @return [void]
91
+ #
92
+ # @raise [Qonfig::ValidatorArgumentError]
93
+ #
94
+ # @api private
95
+ # @since 0.13.0
96
+ def consistent_method_choice!
97
+ unless runtime_validation_method || validation_logic || predefined_validator
98
+ raise(
99
+ Qonfig::ValidatorArgumentError,
100
+ 'Empty validation (you should provide: dataset method OR proc OR predefined validator)'
101
+ )
102
+ end
103
+
104
+ if ((runtime_validation_method && validation_logic) ||
105
+ (predefined_validator && (runtime_validation_method || validation_logic)))
106
+ raise(
107
+ Qonfig::ValidatorArgumentError,
108
+ 'Incosistent validation (you should use: dataset method OR proc OR predefined validator)'
109
+ )
110
+ end
111
+ end
112
+
113
+ # @return [void]
114
+ #
115
+ # @raise [Qonfig::ValidatorArgumentError]
116
+ #
117
+ # @api private
118
+ # @since 0.13.0
119
+ def consistent_predefined_validator!
120
+ return if predefined_validator.nil?
121
+ return if predefined_validator.is_a?(Symbol)
122
+ return if predefined_validator.is_a?(String)
123
+
124
+ raise(
125
+ Qonfig::ValidatorArgumentError,
126
+ 'Incorrect name of predefined validator (should be a symbol or a string)'
127
+ )
128
+ end
129
+
130
+ # @return [void]
131
+ #
132
+ # @raise [Qonfig::ValidatorArgumentError]
133
+ #
134
+ # @api private
135
+ # @since 0.13.0
136
+ def cosnistent_runtime_validation_method!
137
+ return if runtime_validation_method.nil?
138
+ return if runtime_validation_method.is_a?(Symbol)
139
+ return if runtime_validation_method.is_a?(String)
140
+
141
+ raise(
142
+ Qonfig::ValidatorArgumentError,
143
+ 'Incompatible validation method name (should be a symbol or a string)'
144
+ )
145
+ end
146
+
147
+ # @return [void]
148
+ #
149
+ # @raise [Qonfig::ValidatorArgumentError]
150
+ #
151
+ # @api private
152
+ # @since 0.13.0
153
+ def consistent_validation_logic!
154
+ return if validation_logic.nil?
155
+ return if validation_logic.is_a?(Proc)
156
+
157
+ # :nocov:
158
+ raise(
159
+ Qonfig::ValidatorArgumentError,
160
+ 'Incompatible validation object (should be a proc)'
161
+ )
162
+ # :nocov:
163
+ end
164
+
165
+ # @return [void]
166
+ #
167
+ # @raise [Qonfig::ValidatorArgumentError]
168
+ #
169
+ # @api private
170
+ # @since 0.13.0
171
+ def consistent_setting_key_pattern!
172
+ return if setting_key_pattern.nil?
173
+ return if setting_key_pattern.is_a?(Symbol)
174
+ return if setting_key_pattern.is_a?(String)
175
+
176
+ raise(
177
+ Qonfig::ValidatorArgumentError,
178
+ 'Incompatible setting key pattern (should be a string or a symbol)'
179
+ )
180
+ end
181
+ end
data/lib/qonfig/validator/collection.rb ADDED
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Validator::Collection
6
+ # @api private
7
+ # @since 0.13.0
8
+ include Enumerable
9
+
10
+ # @return [Array<Qonfig::Validator::MethodBased,Qonfig::Validator::ProcBased>]
11
+ #
12
+ # @api private
13
+ # @since 0.13.0
14
+ attr_reader :validators
15
+
16
+ # @return [void]
17
+ #
18
+ # @api private
19
+ # @since 0.13.0
20
+ def initialize
21
+ @validators = []
22
+ @access_lock = Mutex.new
23
+ end
24
+
25
+ # @param validator [Qonfig::Validator::MethodBased, Qonfig::Validator::ProcBased]
26
+ # @return [void]
27
+ #
28
+ # @api private
29
+ # @since 0.13.0
30
+ def add_validator(validator)
31
+ thread_safe { validators << validator }
32
+ end
33
+ alias_method :<<, :add_validator
34
+
35
+ # @param block [Proc]
36
+ # @return [Enumerable]
37
+ #
38
+ # @api private
39
+ # @since 0.13.0
40
+ def each(&block)
41
+ thread_safe { block_given? ? validators.each(&block) : validators.each }
42
+ end
43
+
44
+ # @param collection [Qonfig::Validator::Collection]
45
+ # @return [void]
46
+ #
47
+ # @api private
48
+ # @since 0.13.0
49
+ def concat(collection)
50
+ thread_safe { validators.concat(collection.validators) }
51
+ end
52
+
53
+ # @return [Qonfig::Validator::Collection]
54
+ #
55
+ # @api private
56
+ # @since 0.13.0
57
+ def dup
58
+ thread_safe do
59
+ self.class.new.tap { |duplicate| duplicate.concat(self) }
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ # @param block [Proc]
66
+ # @return [Any]
67
+ #
68
+ # @api private
69
+ # @since 0.13.0
70
+ def thread_safe(&block)
71
+ @access_lock.synchronize(&block)
72
+ end
73
+ end
data/lib/qonfig/validator/dsl.rb ADDED
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ module Qonfig::Validator::DSL
6
+ class << self
7
+ # @param child_klass [Qonfig::DataSet]
8
+ # @return [void]
9
+ #
10
+ # @api private
11
+ # @since 0.13.0
12
+ def extended(child_klass)
13
+ child_klass.instance_variable_set(:@validators, Qonfig::Validator::Collection.new)
14
+
15
+ child_klass.singleton_class.prepend(Module.new do
16
+ def inherited(child_klass)
17
+ child_klass.instance_variable_set(:@validators, Qonfig::Validator::Collection.new)
18
+ child_klass.validators.concat(validators)
19
+ super
20
+ end
21
+ end)
22
+ end
23
+ end
24
+
25
+ # @return [Qonfig::Validator::Collection]
26
+ #
27
+ # @api private
28
+ # @since 0.13.0
29
+ def validators
30
+ @validators
31
+ end
32
+
33
+ # @param setting_key_pattern [String, Symbol, NilClass]
34
+ # @param predefined [String, Symbol]
35
+ # @option by [String, Symbol, NilClass]
36
+ # @param custom_validation [Proc]
37
+ # @return [void]
38
+ #
39
+ # @see Qonfig::Validator::Builder
40
+ #
41
+ # @api private
42
+ # @since 0.13.0
43
+ def validate(setting_key_pattern = nil, predefined = nil, by: nil, &custom_validation)
44
+ validators << Qonfig::Validator::Builder.build(
45
+ setting_key_pattern: setting_key_pattern,
46
+ predefined_validator: predefined,
47
+ runtime_validation_method: by,
48
+ validation_logic: custom_validation
49
+ )
50
+ end
51
+ end
data/lib/qonfig/validator/method_based.rb ADDED
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Validator::MethodBased < Qonfig::Validator::Basic
6
+ # @return [Symbol, String]
7
+ #
8
+ # @api private
9
+ # @since 0.13.0
10
+ attr_reader :runtime_validation_method
11
+
12
+ # @param setting_key_matcher [Qonfig::Settings::KeyMatcher, NilClass]
13
+ # @param runtime_validation_method [String, Symbol]
14
+ # @return [void]
15
+ #
16
+ # @api private
17
+ # @since 0.13.0
18
+ def initialize(setting_key_matcher, runtime_validation_method)
19
+ super(setting_key_matcher)
20
+ @runtime_validation_method = runtime_validation_method
21
+ end
22
+
23
+ # @param data_set [Qonfig::DataSet]
24
+ # @return [Boolean]
25
+ #
26
+ # @api private
27
+ # @since 0.13.0
28
+ def validate_concrete(data_set)
29
+ data_set.settings.__deep_each_setting__ do |setting_key, setting_value|
30
+ next unless setting_key_matcher.match?(setting_key)
31
+
32
+ raise(
33
+ Qonfig::ValidationError,
34
+ "Invalid value of setting <#{setting_key}> (#{setting_value})"
35
+ ) unless data_set.__send__(runtime_validation_method, setting_value)
36
+ end
37
+ end
38
+
39
+ # @param data_set [Qonfig::DataSet]
40
+ # @return [Boolean]
41
+ #
42
+ # @api private
43
+ # @since 0.13.0
44
+ def validate_full(data_set)
45
+ unless data_set.__send__(runtime_validation_method)
46
+ raise(Qonfig::ValidationError, 'Invalid config object')
47
+ end
48
+ end
49
+ end
data/lib/qonfig/validator/predefined.rb ADDED
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ module Qonfig::Validator::Predefined
6
+ require_relative 'predefined/common'
7
+ require_relative 'predefined/registry'
8
+ require_relative 'predefined/registry_control_mixin'
9
+
10
+ # @since 0.13.0
11
+ extend Qonfig::Validator::Predefined::RegistryControlMixin
12
+
13
+ # @since 0.13.0
14
+ predefine(:integer) { |value| value.is_a?(Integer) }
15
+ # @since 0.13.0
16
+ predefine(:float) { |value| value.is_a?(Float) }
17
+ # @since 0.13.0
18
+ predefine(:numeric) { |value| value.is_a?(Numeric) }
19
+ # @since 0.13.0
20
+ predefine(:string) { |value| value.is_a?(String) }
21
+ # @since 0.13.0
22
+ predefine(:symbol) { |value| value.is_a?(Symbol) }
23
+ # @since 0.13.0
24
+ predefine(:text) { |value| value.is_a?(Symbol) || value.is_a?(String) }
25
+ # @since 0.13.0
26
+ predefine(:array) { |value| value.is_a?(Array) }
27
+ # @since 0.13.0
28
+ predefine(:hash) { |value| value.is_a?(Hash) }
29
+ # @since 0.13.0
30
+ predefine(:big_decimal) { |value| value.is_a?(BigDecimal) }
31
+ # @since 0.13.0
32
+ predefine(:boolean) { |value| value.is_a?(TrueClass) || value.is_a?(FalseClass) }
33
+ # @since 0.13.0
34
+ predefine(:proc) { |value| value.is_a?(Proc) }
35
+ # @since 0.13.0
36
+ predefine(:class) { |value| value.is_a?(Class) }
37
+ # @since 0.13.0
38
+ predefine(:module) { |value| value.is_a?(Module) }
39
+ # @since 0.13.0
40
+ predefine(:not_nil) { |value| !value.nil? }
41
+ end
data/lib/qonfig/validator/predefined/common.rb ADDED
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Validator::Predefined::Common < Qonfig::Validator::Basic
6
+ # @return [Proc]
7
+ #
8
+ # @api private
9
+ # @since 0.13.0
10
+ attr_reader :validation
11
+
12
+ # @param setting_key_matcher [Qonfig::Settings::KeyMatcher]
13
+ # @param validation [Proc]
14
+ # @return [void]
15
+ #
16
+ # @api private
17
+ # @since 0.13.0
18
+ def initialize(setting_key_matcher, validation)
19
+ super(setting_key_matcher)
20
+ @validation = validation
21
+ end
22
+
23
+ # @param data_set [Qonfig::DataSet]
24
+ # @return [void]
25
+ #
26
+ # @raise [Qonfig::ValidationError]
27
+ #
28
+ # @api private
29
+ # @since 0.13.0
30
+ def validate_concrete(data_set)
31
+ data_set.settings.__deep_each_setting__ do |setting_key, setting_value|
32
+ next unless setting_key_matcher.match?(setting_key)
33
+
34
+ raise(
35
+ Qonfig::ValidationError,
36
+ "Invalid value of setting <#{setting_key}> (#{setting_value})"
37
+ ) unless validation.call(setting_value)
38
+ end
39
+ end
40
+
41
+ # @param data_set [Qonfig::DataSet]
42
+ # @return [void]
43
+ #
44
+ # @raise [Qonfig::ValidationError]
45
+ #
46
+ # @api private
47
+ # @since 0.13.0
48
+ def validate_full(data_set)
49
+ # :nocov:
50
+ raise Qonfig::Error, 'Predefined validator should be used only with a setting key'
51
+ # :nocov:
52
+ end
53
+ end
data/lib/qonfig/validator/predefined/registry.rb ADDED
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Validator::Predefined::Registry
6
+ # @return [void]
7
+ #
8
+ # @api private
9
+ # @since 0.13.0
10
+ def initialize
11
+ @validators = {}
12
+ @lock = Mutex.new
13
+ end
14
+
15
+ # @param name [String, Symbol]
16
+ # @param validation [Proc]
17
+ # @return [void]
18
+ #
19
+ # @raise [Qonfig::ValidatorArgumentError]
20
+ #
21
+ # @api private
22
+ # @since 0.13.0
23
+ def register(name, &validation)
24
+ thread_safe do
25
+ name = indifferently_accessable_name(name)
26
+
27
+ raise(
28
+ Qonfig::ValidatorArgumentError,
29
+ "Predefined validator with name '#{name}' already exists."
30
+ ) if validators.key?(name)
31
+
32
+ validators[name] = validation
33
+ end
34
+ end
35
+
36
+ # @param name [String, Symbol]
37
+ # @return [Qonfig::Validator::ProcBased, Qonfig::Validator::MethodBased]
38
+ #
39
+ # @raise [Qonfig::ValidatorArgumentError]
40
+ #
41
+ # @api private
42
+ # @since 0.13.0
43
+ def resolve(name)
44
+ thread_safe do
45
+ # rubocop:disable Style/RedundantBegin
46
+ begin
47
+ validators.fetch(indifferently_accessable_name(name))
48
+ rescue KeyError
49
+ raise(
50
+ Qonfig::ValidatorArgumentError,
51
+ "Predefined validator with name '#{name}' does not exist."
52
+ )
53
+ end
54
+ # rubocop:enable Style/RedundantBegin
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ # @return [Hash<String,Proc>]
61
+ #
62
+ # @api private
63
+ # @since 0.13.0
64
+ attr_reader :validators
65
+
66
+ # @param name [String, Symbol]
67
+ # @return [String]
68
+ #
69
+ # @api private
70
+ # @since 0.13.0
71
+ def indifferently_accessable_name(name)
72
+ name.to_s
73
+ end
74
+
75
+ # @param block [Proc]
76
+ # @return [Any]
77
+ #
78
+ # @api private
79
+ # @since 0.13.0
80
+ def thread_safe(&block)
81
+ @lock.synchronize(&block)
82
+ end
83
+ end
data/lib/qonfig/validator/predefined/registry_control_mixin.rb ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ module Qonfig::Validator::Predefined::RegistryControlMixin
6
+ class << self
7
+ # @param basic_klass [Class, Module]
8
+ # @return [void]
9
+ #
10
+ # @api private
11
+ # @since 0.13.0
12
+ def extended(basic_klass)
13
+ basic_klass.instance_variable_set(:@registry, Qonfig::Validator::Predefined::Registry.new)
14
+ end
15
+ end
16
+
17
+ # @return [Qonfig::Validator::Predefined::Registry]
18
+ #
19
+ # @api private
20
+ # @since 0.13.0
21
+ attr_reader :registry
22
+
23
+ # @param name [String, Symbol]
24
+ # @param validation [Proc]
25
+ # @return [void]
26
+ #
27
+ # @api private
28
+ # @since 0.13.0
29
+ def predefine(name, &validation)
30
+ registry.register(name, &validation)
31
+ end
32
+
33
+ # @param name [String, Symbol]
34
+ # @param setting_key_matcher [Qonfig::Setting::KeyMatcher]
35
+ # @return [Qonfig::Validator::Predefined::Common]
36
+ #
37
+ # @api private
38
+ # @since 0.13.0
39
+ def build(name, setting_key_matcher)
40
+ validation = registry.resolve(name)
41
+ Qonfig::Validator::Predefined::Common.new(setting_key_matcher, validation)
42
+ end
43
+ end
data/lib/qonfig/validator/proc_based.rb ADDED
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @api private
4
+ # @since 0.13.0
5
+ class Qonfig::Validator::ProcBased < Qonfig::Validator::Basic
6
+ # @return [Proc]
7
+ #
8
+ # @api private
9
+ # @since 0.13.0
10
+ attr_reader :validation
11
+
12
+ # @param setting_key_matcher [Qonfig::Settings::KeyMatcher, NilClass]
13
+ # @param vaidation [Proc]
14
+ # @return [void]
15
+ #
16
+ # @api private
17
+ # @since 0.13.0
18
+ def initialize(setting_key_matcher, validation)
19
+ super(setting_key_matcher)
20
+ @validation = validation
21
+ end
22
+
23
+ # @param data_set [Qonfig::DataSet]
24
+ # @return [Boolean]
25
+ #
26
+ # @raise [Qonfig::ValidationError]
27
+ #
28
+ # @api private
29
+ # @since 0.13.0
30
+ def validate_concrete(data_set)
31
+ data_set.settings.__deep_each_setting__ do |setting_key, setting_value|
32
+ next unless setting_key_matcher.match?(setting_key)
33
+
34
+ raise(
35
+ Qonfig::ValidationError,
36
+ "Invalid value of setting <#{setting_key}> (#{setting_value})"
37
+ ) unless data_set.instance_exec(setting_value, &validation)
38
+ end
39
+ end
40
+
41
+ # @param data_set [Qonfig::DataSet]
42
+ # @return [Boolean]
43
+ #
44
+ # @raise [Qonfig::ValidationError]
45
+ #
46
+ # @api private
47
+ # @since 0.13.0
48
+ def validate_full(data_set)
49
+ unless data_set.instance_eval(&validation)
50
+ raise(Qonfig::ValidationError, 'Invalid config object')
51
+ end
52
+ end
53
+ end
data/lib/qonfig/version.rb CHANGED
@@ -5,5 +5,5 @@ module Qonfig
5
5
#
6
6
# @api public
7
7
# @since 0.1.0
8
- VERSION = '0.12.0'
8
+ VERSION = '0.13.0'
9
9
end
data/qonfig.gemspec CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
spec.add_development_dependency 'coveralls', '~> 0.8'
32
32
spec.add_development_dependency 'simplecov', '~> 0.16'
33
33
spec.add_development_dependency 'rspec', '~> 3.8'
34
- spec.add_development_dependency 'armitage-rubocop', '~> 0.73'
34
+ spec.add_development_dependency 'armitage-rubocop', '~> 0.74'
35
35
36
36
spec.add_development_dependency 'bundler'
37
37
spec.add_development_dependency 'rake'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
--- !ruby/object:Gem::Specification
2
2
name: qonfig
3
3
version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.13.0
5
5
platform: ruby
6
6
authors:
7
7
- Rustam Ibragimov
8
8
autorequire:
9
9
bindir: bin
10
10
cert_chain: []
11
- date: 2019-07-19 00:00:00.000000000 Z
11
+ date: 2019-08-13 00:00:00.000000000 Z
12
12
dependencies:
13
13
- !ruby/object:Gem::Dependency
14
14
name: coveralls
@@ -58,14 +58,14 @@ dependencies:
58
58
requirements:
59
59
- - "~>"
60
60
- !ruby/object:Gem::Version
61
- version: '0.73'
61
+ version: '0.74'
62
62
type: :development
63
63
prerelease: false
64
64
version_requirements: !ruby/object:Gem::Requirement
65
65
requirements:
66
66
- - "~>"
67
67
- !ruby/object:Gem::Version
68
- version: '0.73'
68
+ version: '0.74'
69
69
- !ruby/object:Gem::Dependency
70
70
name: bundler
71
71
requirement: !ruby/object:Gem::Requirement
@@ -149,7 +149,7 @@ files:
149
149
- lib/qonfig/configurable.rb
150
150
- lib/qonfig/data_set.rb
151
151
- lib/qonfig/data_set/class_builder.rb
152
- - lib/qonfig/data_set/validator.rb
152
+ - lib/qonfig/data_set/lock.rb
153
153
- lib/qonfig/dsl.rb
154
154
- lib/qonfig/errors.rb
155
155
- lib/qonfig/loaders.rb
@@ -170,13 +170,27 @@ files:
170
170
- lib/qonfig/plugins/toml/uploaders/toml.rb
171
171
- lib/qonfig/settings.rb
172
172
- lib/qonfig/settings/builder.rb
173
+ - lib/qonfig/settings/callbacks.rb
173
174
- lib/qonfig/settings/key_guard.rb
175
+ - lib/qonfig/settings/key_matcher.rb
174
176
- lib/qonfig/settings/lock.rb
175
177
- lib/qonfig/uploaders.rb
176
178
- lib/qonfig/uploaders/base.rb
177
179
- lib/qonfig/uploaders/file.rb
178
180
- lib/qonfig/uploaders/json.rb
179
181
- lib/qonfig/uploaders/yaml.rb
182
+ - lib/qonfig/validator.rb
183