qonfig 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +9 -1
- data/README.md +313 -14
- data/lib/qonfig/data_set.rb +49 -1
- data/lib/qonfig/errors.rb +24 -0
- data/lib/qonfig/imports/abstract.rb +90 -0
- data/lib/qonfig/imports/direct_key.rb +110 -0
- data/lib/qonfig/imports/dsl.rb +46 -0
- data/lib/qonfig/imports/export.rb +39 -0
- data/lib/qonfig/imports/general.rb +131 -0
- data/lib/qonfig/imports/mappings.rb +120 -0
- data/lib/qonfig/imports.rb +23 -0
- data/lib/qonfig/settings/key_matcher.rb +9 -8
- data/lib/qonfig/settings.rb +66 -4
- data/lib/qonfig/version.rb +1 -1
- data/lib/qonfig.rb +1 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec197a7e1d7185ff8c4e5906e244c5cbce610d1449dc60cfb1d2a167a2e79921
|
4
|
+
data.tar.gz: 47d2d394cfcc97cf5ab58a198865490ecd4c9494c7ee7096474ff7a6f0c5d7f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87e2688f74aa2dba959c423ea79ec36686c1873e20654826ad4eefc599da6f0d9967066e8de605b0f0a83d58465ecdb3f061e193fedc1aef6223d03794ffff7f
|
7
|
+
data.tar.gz: 107ccfc0dfe0ee109765699c4af58d431edd572d6ea30dace2449ff87d991bc8dbb555dedf95ccf7d3101cac2fb728fd7e44dec5c8a04a74583b006926e8ec46
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
# Changelog
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
-
## [
|
4
|
+
## [0.18.0] - 2019-11-04
|
5
|
+
### Added
|
6
|
+
- `#keys` - returns a list of all config keys in dot-notation format;
|
7
|
+
- `#root_keys` - returns a list of root config keys;
|
8
|
+
- Inroduce `Import API`:
|
9
|
+
- `.import_settings` - DSL method for importing configuration settings (from a config instance) as instance methods of a class;
|
10
|
+
- `#export_settings` - config's instance method that exports config settings to an arbitrary object as singelton methods;
|
11
|
+
|
12
|
+
## [0.17.0] - 2019-10-30
|
5
13
|
### Added
|
6
14
|
- Introduce `strict` validations: `strict: false` option ignores `nil` values and used by default;
|
7
15
|
- Setting's key existence check methods: `#key?(*key_path)`, `#setting?(*key_path)`, `#option?(*key_path)`;
|
data/README.md
CHANGED
@@ -44,12 +44,16 @@ require 'qonfig'
|
|
44
44
|
- [Instantiation without class definition](#instantiation-without-class-definition) (`Qonfig::DataSet.build(&definitions)`)
|
45
45
|
- [Interaction](#interaction)
|
46
46
|
- [Iteration over setting keys](#iteration-over-setting-keys) (`#each_setting`, `#deep_each_setting`)
|
47
|
+
- [List of config keys](#list-of-config-keys) (`#keys`, `#root_keys`)
|
47
48
|
- [Config reloading](#config-reloading) (reload config definitions and option values)
|
48
49
|
- [Clear options](#clear-options) (set to `nil`)
|
49
50
|
- [State freeze](#state-freeze)
|
50
51
|
- [Settings as Predicates](#settings-as-predicates)
|
51
52
|
- [Setting key existence](#setting-key-existence) (`#key?`/`#option?`/`#setting?`)
|
52
|
-
- [Run
|
53
|
+
- [Run arbitrary code with temporary settings](#run-arbitrary-code-with-temporary-settings) (`#with(configs = {}, &arbitrary_code)`)
|
54
|
+
- [Import settings / Export settings](#import-settings--export-settings)
|
55
|
+
- [Import config settings](#import-config-settings) (`as instance methods`)
|
56
|
+
- [Export config settings](#export-config-settings) (`as singleton methods`)
|
53
57
|
- [Validation](#validation)
|
54
58
|
- [Introduction](#introduction)
|
55
59
|
- [Key search pattern](#key-search-pattern)
|
@@ -75,7 +79,7 @@ require 'qonfig'
|
|
75
79
|
- [Save to JSON file](#save-to-json-file) (`#save_to_json`)
|
76
80
|
- [Save to YAML file](#save-to-yaml-file) (`#save_to_yaml`)
|
77
81
|
- [Plugins](#plugins)
|
78
|
-
- [toml](#plugins-toml) (
|
82
|
+
- [toml](#plugins-toml) (support for `TOML` format)
|
79
83
|
- [Roadmap](#roadmap)
|
80
84
|
---
|
81
85
|
|
@@ -493,6 +497,8 @@ GeneralApplication.config.to_h
|
|
493
497
|
|
494
498
|
### Instantiation without class definition
|
495
499
|
|
500
|
+
- without inheritance:
|
501
|
+
|
496
502
|
```ruby
|
497
503
|
config = Qonfig::DataSet.build do
|
498
504
|
setting :user, 'D@iVeR'
|
@@ -510,17 +516,35 @@ config.settings.password # => 'test123'
|
|
510
516
|
config.custom_method # => 'custom_result'
|
511
517
|
```
|
512
518
|
|
519
|
+
- with inheritance:
|
520
|
+
|
521
|
+
```ruby
|
522
|
+
class GeneralConfig < Qonfig::DataSet
|
523
|
+
setting :db_adapter, :postgresql
|
524
|
+
end
|
525
|
+
|
526
|
+
config = Qonfig::DataSet.build(GeneralConfig) do
|
527
|
+
setting :web_api, 'api.google.com'
|
528
|
+
end
|
529
|
+
|
530
|
+
config.is_a?(Qonfig::DataSet) # => true
|
531
|
+
|
532
|
+
config.settings.db_adapter # => :postgresql
|
533
|
+
config.settings.web_api # => "api.google.com"
|
534
|
+
```
|
535
|
+
|
513
536
|
---
|
514
537
|
|
515
538
|
## Interaction
|
516
539
|
|
517
540
|
- [Iteration over setting keys](#iteration-over-setting-keys) (`#each_setting`, `#deep_each_setting`)
|
541
|
+
- [List of config keys](#list-of-config-keys) (`#keys`, `#root_keys`)
|
518
542
|
- [Config reloading](#config-reloading) (reload config definitions and option values)
|
519
|
-
- [Clear options](#clear-options) (set to nil)
|
543
|
+
- [Clear options](#clear-options) (set to `nil`)
|
520
544
|
- [State freeze](#state-freeze)
|
521
545
|
- [Settings as Predicates](#settings-as-predicates)
|
522
546
|
- [Setting key existence](#setting-key-existence) (`#key?`/`#option?`/`#setting?`)
|
523
|
-
- [Run
|
547
|
+
- [Run arbitrary code with temporary settings](#run-arbitrary-code-with-temporary-settings)
|
524
548
|
|
525
549
|
---
|
526
550
|
|
@@ -576,8 +600,96 @@ config.deep_each_setting { |key, value| { key => value } }
|
|
576
600
|
|
577
601
|
---
|
578
602
|
|
603
|
+
### List of config keys
|
604
|
+
|
605
|
+
- `#keys` - returns a list of all config keys in dot-notation format;
|
606
|
+
- `all_variants:` - get all possible variants of the config's keys sequences (`false` by default);
|
607
|
+
- `only_root:` - get only the root config keys (`false` by default);
|
608
|
+
- `#root_keys` - returns a list of root config keys (an alias for `#keys(only_root: true)`);
|
609
|
+
|
610
|
+
```ruby
|
611
|
+
# NOTE: suppose we have the following config
|
612
|
+
|
613
|
+
class Config < Qonfig::DataSet
|
614
|
+
setting :credentials do
|
615
|
+
setting :social do
|
616
|
+
setting :service, 'instagram'
|
617
|
+
setting :login, '0exp'
|
618
|
+
end
|
619
|
+
|
620
|
+
setting :admin do
|
621
|
+
setting :enabled, true
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
setting :server do
|
626
|
+
setting :type, 'cloud'
|
627
|
+
setting :options do
|
628
|
+
setting :os, 'CentOS'
|
629
|
+
end
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
config = Config.new
|
634
|
+
```
|
635
|
+
|
636
|
+
#### Default behavior
|
637
|
+
|
638
|
+
```ruby
|
639
|
+
config.keys
|
640
|
+
|
641
|
+
# the result:
|
642
|
+
[
|
643
|
+
"credentials.social.service",
|
644
|
+
"credentials.social.login",
|
645
|
+
"credentials.admin.enabled",
|
646
|
+
"server.type",
|
647
|
+
"server.options.os"
|
648
|
+
]
|
649
|
+
```
|
650
|
+
|
651
|
+
#### All key variants
|
652
|
+
|
653
|
+
```ruby
|
654
|
+
config.keys(all_variants: true)
|
655
|
+
|
656
|
+
# the result:
|
657
|
+
[
|
658
|
+
"credentials",
|
659
|
+
"credentials.social",
|
660
|
+
"credentials.social.service",
|
661
|
+
"credentials.social.login",
|
662
|
+
"credentials.admin",
|
663
|
+
"credentials.admin.enabled",
|
664
|
+
"server",
|
665
|
+
"server.type",
|
666
|
+
"server.options",
|
667
|
+
"server.options.os"
|
668
|
+
]
|
669
|
+
```
|
670
|
+
|
671
|
+
#### Only root keys
|
672
|
+
|
673
|
+
```ruby
|
674
|
+
config.keys(only_root: true)
|
675
|
+
|
676
|
+
# the result:
|
677
|
+
['credentials', 'server']
|
678
|
+
```
|
679
|
+
|
680
|
+
```ruby
|
681
|
+
config.root_keys
|
682
|
+
|
683
|
+
# the result:
|
684
|
+
['credentials', 'server']
|
685
|
+
```
|
686
|
+
|
687
|
+
---
|
688
|
+
|
579
689
|
### Config reloading
|
580
690
|
|
691
|
+
- method signature: `#reload!(configurations = {}, &configuration)`;
|
692
|
+
|
581
693
|
```ruby
|
582
694
|
class Config < Qonfig::DataSet
|
583
695
|
setting :db do
|
@@ -624,6 +736,9 @@ config.settings.enable_api # => true # value from instant change
|
|
624
736
|
|
625
737
|
### Clear options
|
626
738
|
|
739
|
+
- set all config's settings to `nil`;
|
740
|
+
- method signature: `#clear!`;
|
741
|
+
|
627
742
|
```ruby
|
628
743
|
class Config
|
629
744
|
setting :database do
|
@@ -659,6 +774,8 @@ config.settings.web_api.endpoint # => nil
|
|
659
774
|
|
660
775
|
### State freeze
|
661
776
|
|
777
|
+
- method signature: `#freeze!`;
|
778
|
+
|
662
779
|
```ruby
|
663
780
|
class Config < Qonfig::DataSet
|
664
781
|
setting :logger, Logger.new(STDOUT)
|
@@ -756,10 +873,10 @@ config.option?(:credentials, :password) # => true
|
|
756
873
|
|
757
874
|
---
|
758
875
|
|
759
|
-
### Run
|
876
|
+
### Run arbitrary code with temporary settings
|
760
877
|
|
761
|
-
- provides a way to run an
|
762
|
-
- your
|
878
|
+
- provides a way to run an arbitrary code with temporarily specified settings;
|
879
|
+
- your arbitrary code can temporary change any setting too - all settings will be returned to the original state;
|
763
880
|
- (it is convenient to run code samples by this way in tests (with substitued configs));
|
764
881
|
- it is fully thread-safe `:)`;
|
765
882
|
|
@@ -791,6 +908,188 @@ config.settings.queue.options # => {}
|
|
791
908
|
|
792
909
|
---
|
793
910
|
|
911
|
+
## Import settings / Export settings
|
912
|
+
|
913
|
+
- [Import config settings](#import-config-settings) (`as instance methods`)
|
914
|
+
- [Export config settings](#export-config-settings) (`as singleton methods`)
|
915
|
+
|
916
|
+
Sometimes the nesting of configs in your project is quite high, and it makes you write the rather "cumbersome" code
|
917
|
+
(`config.settings.web_api.credentials.account.auth_token` for example). Frequent access to configs in this way is inconvinient - so developers wraps
|
918
|
+
such code by methods or variables. In order to make developer's life easer `Qonfig` provides a special Import API simplifies the config importing
|
919
|
+
(gives you `.import_settings` DSL) and gives an ability to instant config setting export from a config object (gives you `#export_settings` config's method).
|
920
|
+
|
921
|
+
---
|
922
|
+
|
923
|
+
### Import config settings
|
924
|
+
|
925
|
+
- `Qonfig::Imports` - a special mixin that provides the convenient DSL to work with config import features (`.import_settings` method);
|
926
|
+
- `.import_settings` - DSL method for importing configuration settings (from a config instance) as instance methods of a class;
|
927
|
+
- (**IMPORTANT**) `import_settings` imports config settings as access methods to config's settings (creates `attr_reader`s for your config);
|
928
|
+
- signature: `.import_settings(config_object, *setting_keys, mappings: {}, prefix: '', raw: false)`
|
929
|
+
- `config_object` - an instance of `Qonfig::DataSet` whose config settings should be imported;
|
930
|
+
- `*setting_keys` - an array of dot-notaed config's setting keys that should be imported
|
931
|
+
(dot-notaed key is a key that describes each part of nested setting key as a string separated by `dot`-symbol);
|
932
|
+
- last part of dot-notated key will become a name of the setting access instance method;
|
933
|
+
- `mappings:` - a map of keys that describes custom method names for each imported setting;
|
934
|
+
- `prefix:` - prexifies setting access method name with custom prefix;
|
935
|
+
- `raw:` - use nested settings as objects or hashify them (`false` by default (means "hashify nested settings"));
|
936
|
+
|
937
|
+
---
|
938
|
+
|
939
|
+
Suppose we have a config with deeply nested keys:
|
940
|
+
|
941
|
+
```ruby
|
942
|
+
# NOTE: (Qonfig::DataSet.build creates a class and instantly instantiates it)
|
943
|
+
AppConfig = Qonfig::DataSet.build do
|
944
|
+
setting :web_api do
|
945
|
+
setting :credentials do
|
946
|
+
setting :account do
|
947
|
+
setting :login, 'DaiveR'
|
948
|
+
setting :auth_token, 'IAdkoa0@()1239uA'
|
949
|
+
end
|
950
|
+
end
|
951
|
+
end
|
952
|
+
end
|
953
|
+
```
|
954
|
+
|
955
|
+
Let's see what we can to do :)
|
956
|
+
|
957
|
+
#### Import a set of setting keys (simple dot-noated key list)
|
958
|
+
|
959
|
+
- last part of dot-notated key will become a name of the setting access instance method;
|
960
|
+
|
961
|
+
```ruby
|
962
|
+
class ServiceObject
|
963
|
+
include Qonfig::Imports
|
964
|
+
|
965
|
+
import_settings(AppConfig,
|
966
|
+
'web_api.credentials.account.login',
|
967
|
+
'web_api.credentials.account'
|
968
|
+
)
|
969
|
+
end
|
970
|
+
|
971
|
+
service = ServiceObject.new
|
972
|
+
|
973
|
+
service.login # => "D@iVeR"
|
974
|
+
service.account # => { "login" => "D@iVeR", "auth_token" => IAdkoa0@()1239uA" }
|
975
|
+
```
|
976
|
+
|
977
|
+
#### Import with custom method names (mappings)
|
978
|
+
|
979
|
+
- `mappings:` defines a map of keys that describes custom method names for each imported setting;
|
980
|
+
|
981
|
+
```ruby
|
982
|
+
class ServiceObject
|
983
|
+
include Qonfig::Imports
|
984
|
+
|
985
|
+
import_settings(AppConfig, mappings: {
|
986
|
+
account_data: 'web_api.credentials.account', # NOTE: name access method with "account_data"
|
987
|
+
secret_token: 'web_api.credentials.account.auth_token' # NOTE: name access method with "secret_token"
|
988
|
+
})
|
989
|
+
end
|
990
|
+
|
991
|
+
service = ServiceObject.new
|
992
|
+
|
993
|
+
service.account_data # => { "login" => "D@iVeR", "auth_token" => "IAdkoa0@()1239uA" }
|
994
|
+
service.auth_token # => "IAdkoa0@()1239uA"
|
995
|
+
```
|
996
|
+
|
997
|
+
#### Prexify method name
|
998
|
+
|
999
|
+
- `prefix:` - prexifies setting access method name with custom prefix;
|
1000
|
+
|
1001
|
+
```ruby
|
1002
|
+
class ServiceObject
|
1003
|
+
include Qonfig::Imports
|
1004
|
+
|
1005
|
+
import_settings(AppConfig,
|
1006
|
+
'web_api.credentials.account',
|
1007
|
+
mappings: { secret_token: 'web_api.credentials.account.auth_token' },
|
1008
|
+
prefix: 'config_'
|
1009
|
+
)
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
service = ServiceObject.new
|
1013
|
+
|
1014
|
+
service.config_credentials # => { login" => "D@iVeR", "auth_token" => "IAdkoa0@()1239uA" }
|
1015
|
+
service.config_secret_token # => "IAdkoa0@()1239uA"
|
1016
|
+
```
|
1017
|
+
|
1018
|
+
#### Import nested settings as raw Qonfig::Settings objects
|
1019
|
+
|
1020
|
+
- `raw: false` is used by default (hashify nested settings)
|
1021
|
+
|
1022
|
+
```ruby
|
1023
|
+
# NOTE: import nested settings as raw objects (raw: true)
|
1024
|
+
class ServiceObject
|
1025
|
+
include Qonfig::Imports
|
1026
|
+
|
1027
|
+
import_settings(AppConfig, 'web_api.credentials', raw: true)
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
service = ServiceObject.new
|
1031
|
+
|
1032
|
+
service.credentials # => <Qonfig::Settings:0x00007ff8>
|
1033
|
+
service.credentials.account.login # => "D@iVeR"
|
1034
|
+
service.credentials.account.auth_token # => "IAdkoa0@()1239uA"
|
1035
|
+
```
|
1036
|
+
|
1037
|
+
```ruby
|
1038
|
+
# NOTE: import nested settings as converted-to-hash objects (raw: false) (default behavior)
|
1039
|
+
class ServiceObject
|
1040
|
+
include Qonfig::Imports
|
1041
|
+
|
1042
|
+
import_settings(AppConfig, 'web_api.credentials', raw: false)
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
service = ServiceObject.new
|
1046
|
+
|
1047
|
+
service.credentials # => { "account" => { "login" => "D@iVeR", "auth_token" => "IAdkoa0@()1239uA"} }
|
1048
|
+
```
|
1049
|
+
|
1050
|
+
---
|
1051
|
+
|
1052
|
+
### Export config settings
|
1053
|
+
|
1054
|
+
- all config objects can export their settings to an arbitrary object as singleton methods;
|
1055
|
+
- (**IMPORTANT**) `export_settings` exports config settings as access methods to config's settings (creates `attr_reader`s for your config);
|
1056
|
+
- signature: `#export(exportable_object, *setting_keys, mappings: {}, prefix: '', raw: false)`:
|
1057
|
+
- `exportable_object` - an arbitrary object for exporting;
|
1058
|
+
- `*setting_keys` - an array of dot-notaed config's setting keys that should be exported
|
1059
|
+
(dot-notaed key is a key that describes each part of nested setting key as a string separated by `dot`-symbol);
|
1060
|
+
- last part of dot-notated key will become a name of the setting access instance method;
|
1061
|
+
- `mappings:` - a map of keys that describes custom method names for each exported setting;
|
1062
|
+
- `prefix:` - prexifies setting access method name with custom prefix;
|
1063
|
+
- `raw:` - use nested settings as objects or hashify them (`false` by default (means "hashify nested settings"));
|
1064
|
+
- works in `.import_settings` manner [doc](#import-config-settings) (see examples and documentation above `:)`)
|
1065
|
+
|
1066
|
+
```ruby
|
1067
|
+
class Config < Qonfig::DataSet
|
1068
|
+
setting :web_api do
|
1069
|
+
setting :credentials do
|
1070
|
+
setting :account do
|
1071
|
+
setting :login, 'DaiveR'
|
1072
|
+
setting :auth_token, 'IAdkoa0@()1239uA'
|
1073
|
+
end
|
1074
|
+
end
|
1075
|
+
end
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
class ServiceObject; end
|
1079
|
+
|
1080
|
+
config = Config.new
|
1081
|
+
service = ServiceObject.new
|
1082
|
+
|
1083
|
+
service.config_account # => NoMethodError
|
1084
|
+
|
1085
|
+
# NOTE: export settings as access methods to config's settings
|
1086
|
+
config.export(service, 'web_api.credentials.account', prefix: 'config_')
|
1087
|
+
|
1088
|
+
service.account # => { "login" => "D@iVeR", "auth_token" => "IAdkoa0@()1239uA" }
|
1089
|
+
```
|
1090
|
+
|
1091
|
+
---
|
1092
|
+
|
794
1093
|
## Validation
|
795
1094
|
|
796
1095
|
- [Introduction](#introduction)
|
@@ -1735,19 +2034,19 @@ config.settings.options.threads # => 10 (development keys subset)
|
|
1735
2034
|
#### File does not exist
|
1736
2035
|
|
1737
2036
|
```ruby
|
1738
|
-
# strict behavior (default)
|
2037
|
+
# non-strict behavior (default)
|
1739
2038
|
class Config < Qonfig::DataSet
|
1740
2039
|
values_file 'sidekiq.yml'
|
1741
2040
|
end
|
1742
2041
|
|
1743
|
-
config = Config.new #
|
2042
|
+
config = Config.new # no error
|
1744
2043
|
|
1745
|
-
#
|
2044
|
+
# strict behavior (strict: true)
|
1746
2045
|
class Config < Qonfig::DataSet
|
1747
|
-
values_file 'sidekiq.yml', strict:
|
2046
|
+
values_file 'sidekiq.yml', strict: true
|
1748
2047
|
end
|
1749
2048
|
|
1750
|
-
config = Config.new #
|
2049
|
+
config = Config.new # => Qonfig::FileNotFoundError
|
1751
2050
|
```
|
1752
2051
|
|
1753
2052
|
---
|
@@ -2201,6 +2500,8 @@ enabled: true
|
|
2201
2500
|
dynamic: 10
|
2202
2501
|
```
|
2203
2502
|
|
2503
|
+
---
|
2504
|
+
|
2204
2505
|
### Plugins
|
2205
2506
|
|
2206
2507
|
```ruby
|
@@ -2250,8 +2551,6 @@ Qonfig.plugin(:toml)
|
|
2250
2551
|
- **Minor**:
|
2251
2552
|
- custom global (and class-level) validators (with a special Validator Definition DSL);
|
2252
2553
|
- 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
2554
|
- pretty print :)));
|
2256
2555
|
|
2257
2556
|
## Contributing
|
data/lib/qonfig/data_set.rb
CHANGED
@@ -21,7 +21,7 @@ class Qonfig::DataSet # rubocop:disable Metrics/ClassLength
|
|
21
21
|
# @since 0.16.0
|
22
22
|
def build(base_dataset_klass = self, &config_klass_definitions)
|
23
23
|
unless base_dataset_klass <= Qonfig::DataSet
|
24
|
-
raise(Qonfig::ArgumentError, 'Base
|
24
|
+
raise(Qonfig::ArgumentError, 'Base class should be a type of Qonfig::DataSet')
|
25
25
|
end
|
26
26
|
|
27
27
|
Class.new(base_dataset_klass, &config_klass_definitions).new
|
@@ -309,6 +309,26 @@ class Qonfig::DataSet # rubocop:disable Metrics/ClassLength
|
|
309
309
|
thread_safe_access { validator.validate! }
|
310
310
|
end
|
311
311
|
|
312
|
+
# @option all_variants [Boolean]
|
313
|
+
# @option only_root [Boolean]
|
314
|
+
# @return [Array<String>]
|
315
|
+
#
|
316
|
+
# @api public
|
317
|
+
# @since 0.18.0
|
318
|
+
def keys(all_variants: false, only_root: false)
|
319
|
+
thread_safe_access do
|
320
|
+
only_root ? settings.__root_keys__ : settings.__keys__(all_variants: all_variants)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# @return [Array<String>]
|
325
|
+
#
|
326
|
+
# @api public
|
327
|
+
# @since 0.18.0
|
328
|
+
def root_keys
|
329
|
+
thread_safe_access { settings.__root_keys__ }
|
330
|
+
end
|
331
|
+
|
312
332
|
# @param temporary_configurations [Hash<Symbol|String,Any>]
|
313
333
|
# @param arbitary_code [Block]
|
314
334
|
# @return [void]
|
@@ -344,6 +364,34 @@ class Qonfig::DataSet # rubocop:disable Metrics/ClassLength
|
|
344
364
|
end
|
345
365
|
end
|
346
366
|
|
367
|
+
# @param exportable_object [Object]
|
368
|
+
# @param exported_setting_keys [Array<String,Symbol>]
|
369
|
+
# @option mappings [Hash<String|Symbol,String|Symbol>]
|
370
|
+
# @option raw [Boolean]
|
371
|
+
# @option prefix [String, Symbol]
|
372
|
+
# @return [void]
|
373
|
+
#
|
374
|
+
# @api public
|
375
|
+
# @since 0.18.0
|
376
|
+
def export_settings(
|
377
|
+
exportable_object,
|
378
|
+
*exported_setting_keys,
|
379
|
+
mappings: Qonfig::Imports::Abstract::EMPTY_MAPPINGS,
|
380
|
+
raw: false,
|
381
|
+
prefix: Qonfig::Imports::Abstract::EMPTY_PREFIX
|
382
|
+
)
|
383
|
+
thread_safe_access do
|
384
|
+
Qonfig::Imports::Export.export!(
|
385
|
+
exportable_object,
|
386
|
+
self,
|
387
|
+
*exported_setting_keys,
|
388
|
+
prefix: prefix,
|
389
|
+
raw: raw,
|
390
|
+
mappings: mappings
|
391
|
+
)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
347
395
|
private
|
348
396
|
|
349
397
|
# @return [Qonfig::Validator]
|
data/lib/qonfig/errors.rb
CHANGED
@@ -156,4 +156,28 @@ module Qonfig
|
|
156
156
|
# @api public
|
157
157
|
# @since 0.12.0
|
158
158
|
UnresolvedPluginDependencyError = Class.new(PluginError)
|
159
|
+
|
160
|
+
# @see Qonfig::Imports::Abstract
|
161
|
+
#
|
162
|
+
# @api public
|
163
|
+
# @since 0.18.0
|
164
|
+
IncompatibleImportedConfigError = Class.new(ArgumentError)
|
165
|
+
|
166
|
+
# @see Qonfig::Imports::DirectKey
|
167
|
+
#
|
168
|
+
# @api public
|
169
|
+
# @since 0.18.0
|
170
|
+
IncorrectImportKeyError = Class.new(ArgumentError)
|
171
|
+
|
172
|
+
# @see Qonfig::Imports::Abstract
|
173
|
+
#
|
174
|
+
# @api public
|
175
|
+
# @since 0.18.0
|
176
|
+
IncorrectImportPrefixError = Class.new(ArgumentError)
|
177
|
+
|
178
|
+
# @see Qonfig::Imports::Mappings
|
179
|
+
#
|
180
|
+
# @api public
|
181
|
+
# @since 0.18.0
|
182
|
+
IncorrectImportMappingsError = Class.new(ArgumentError)
|
159
183
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.18.0
|
5
|
+
class Qonfig::Imports::Abstract
|
6
|
+
# @return [String]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.18.0
|
10
|
+
EMPTY_PREFIX = ''
|
11
|
+
|
12
|
+
# @return [Boolean]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.18.0
|
16
|
+
DEFAULT_RAW_BEHAVIOR = false
|
17
|
+
|
18
|
+
# @param seeded_klass [Class]
|
19
|
+
# @param imported_config [Qonfig::DataSet]
|
20
|
+
# @option prefix [String, Symbol]
|
21
|
+
# @option raw [Boolean]
|
22
|
+
# @return [void]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
# @since 0.18.0
|
26
|
+
def initialize(seeded_klass, imported_config, prefix: EMPTY_PREFIX, raw: DEFAULT_RAW_BEHAVIOR)
|
27
|
+
@seeded_klass = seeded_klass
|
28
|
+
@imported_config = imported_config
|
29
|
+
@prefix = prefix
|
30
|
+
@raw = !!raw
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param settings_interface [Module]
|
34
|
+
# @return [void]
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
# @since 0.18.0
|
38
|
+
def import!(settings_interface = Module.new)
|
39
|
+
# :nocov:
|
40
|
+
raise NoMethodError
|
41
|
+
# :nocov:
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# @return [Boolean]
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
# @since 0.18.0
|
50
|
+
attr_reader :raw
|
51
|
+
|
52
|
+
# @return [String, Symbol]
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
# @since 0.18.0
|
56
|
+
attr_reader :prefix
|
57
|
+
|
58
|
+
# @return [Class]
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
# @since 0.18.0
|
62
|
+
attr_reader :seeded_klass
|
63
|
+
|
64
|
+
# @return [Qonfig::DataSet]
|
65
|
+
#
|
66
|
+
# @api private
|
67
|
+
# @since 0.18.0
|
68
|
+
attr_reader :imported_config
|
69
|
+
|
70
|
+
# @param imported_config [Qonfig::DataSet]
|
71
|
+
# @param prefix [String, Symbol]
|
72
|
+
# @return [void]
|
73
|
+
#
|
74
|
+
# @raise [Qonfig::IncompatibleImportedConfigError]
|
75
|
+
# @raise [Qonfig::IncorrectImportPrefixError]
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
# @since 0.18.0
|
79
|
+
def prevent_incompatible_import_params!(imported_config, prefix)
|
80
|
+
raise(
|
81
|
+
Qonfig::IncompatibleImportedConfigError,
|
82
|
+
'Imported config object should be an isntance of Qonfig::DataSet'
|
83
|
+
) unless imported_config.is_a?(Qonfig::DataSet)
|
84
|
+
|
85
|
+
raise(
|
86
|
+
Qonfig::IncorrectImportPrefixError,
|
87
|
+
'Import method prefix should be a type of string or symbol'
|
88
|
+
) unless prefix.is_a?(String) || prefix.is_a?(Symbol)
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.18.0
|
5
|
+
class Qonfig::Imports::DirectKey < Qonfig::Imports::Abstract
|
6
|
+
# @param seeded_klass [Class]
|
7
|
+
# @param imported_config [Qonfig::DataSet]
|
8
|
+
# @param keys [Array<String,Symbol>]
|
9
|
+
# @option prefix [String, Symbol]
|
10
|
+
# @option raw [Boolean]
|
11
|
+
# @return [void]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
# @since 0.18.8
|
15
|
+
def initialize(
|
16
|
+
seeded_klass,
|
17
|
+
imported_config,
|
18
|
+
*keys,
|
19
|
+
prefix: EMPTY_PREFIX,
|
20
|
+
raw: DEFAULT_RAW_BEHAVIOR
|
21
|
+
)
|
22
|
+
prevent_incompatible_import_params!(imported_config, prefix, keys)
|
23
|
+
super(seeded_klass, imported_config, prefix: prefix, raw: raw)
|
24
|
+
@keys = keys
|
25
|
+
@key_matchers = build_setting_key_matchers(keys)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param settings_interfcae [Module]
|
29
|
+
# @return [void]
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
# @since 0.18.0
|
33
|
+
def import!(settings_interface = Module.new) # rubocop:disable Metrics/AbcSize
|
34
|
+
key_matchers.each do |key_matcher|
|
35
|
+
raise(
|
36
|
+
Qonfig::UnknownSettingError,
|
37
|
+
"Setting with <#{key_matcher.scope_pattern}> key does not exist!"
|
38
|
+
) unless (imported_config.keys(all_variants: true).any? do |setting_key|
|
39
|
+
key_matcher.match?(setting_key)
|
40
|
+
end)
|
41
|
+
|
42
|
+
imported_config.keys(all_variants: true).each do |setting_key|
|
43
|
+
next unless key_matcher.match?(setting_key)
|
44
|
+
|
45
|
+
setting_key_path_sequence = setting_key.split('.')
|
46
|
+
access_method_name = setting_key_path_sequence.last
|
47
|
+
access_method_name = "#{prefix}#{access_method_name}" unless prefix.empty?
|
48
|
+
|
49
|
+
settings_interface.module_exec(raw, imported_config) do |raw, imported_config|
|
50
|
+
unless raw
|
51
|
+
# NOTE: get setting value via slice_value
|
52
|
+
define_method(access_method_name) do
|
53
|
+
imported_config.slice_value(*setting_key_path_sequence)
|
54
|
+
end
|
55
|
+
else
|
56
|
+
# NOTE: get setting object (concrete value or Qonfig::Settings object)
|
57
|
+
define_method(access_method_name) do
|
58
|
+
imported_config.dig(*setting_key_path_sequence)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# @return [Array<String,Symbol>]
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
# @since 0.18.8
|
72
|
+
attr_reader :keys
|
73
|
+
|
74
|
+
# @return [Array<Qonfig::Settings::KeyMatcher>]
|
75
|
+
#
|
76
|
+
# @api private
|
77
|
+
# @since 0.18.0
|
78
|
+
attr_reader :key_matchers
|
79
|
+
|
80
|
+
# @param imported_config [Qonfig::DataSet]
|
81
|
+
# @param prefix [String, Symbol]
|
82
|
+
# @param keys [Array<String,Symbol>]
|
83
|
+
# @return [void]
|
84
|
+
#
|
85
|
+
# @raise [Qonfig::IncompatibleImportedConfigError]
|
86
|
+
# @raise [Qonfig::IncorrectImportPrefixError]
|
87
|
+
# @raise [Qonfig::IncorrectImportKeyError]
|
88
|
+
#
|
89
|
+
# @see Qonfig::Imports::Abstract#prevent_incompatible_import_params
|
90
|
+
#
|
91
|
+
# @api private
|
92
|
+
# @since 0.18.0
|
93
|
+
def prevent_incompatible_import_params!(imported_config, prefix, keys)
|
94
|
+
super(imported_config, prefix)
|
95
|
+
|
96
|
+
raise(
|
97
|
+
Qonfig::IncorrectImportKeyError,
|
98
|
+
'Imported config keys should be a type of string or symbol'
|
99
|
+
) unless keys.all? { |key| key.is_a?(String) || key.is_a?(Symbol) }
|
100
|
+
end
|
101
|
+
|
102
|
+
# @param keys [Array<String,Symbol>]
|
103
|
+
# @return [Array<Qonfig::KeyMatcher>]
|
104
|
+
#
|
105
|
+
# @api private
|
106
|
+
# @since 0.18.0
|
107
|
+
def build_setting_key_matchers(keys)
|
108
|
+
keys.map { |key| Qonfig::Settings::KeyMatcher.new(key) }
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.18.0
|
5
|
+
module Qonfig::Imports::DSL
|
6
|
+
class << self
|
7
|
+
# @param base_klass [Class]
|
8
|
+
# @return [void]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
# @since 0.18.0
|
12
|
+
def included(base_klass)
|
13
|
+
base_klass.extend(ClassMethods)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# @api private
|
18
|
+
# @since 0.18.0
|
19
|
+
module ClassMethods
|
20
|
+
# @param imported_config [Qonfig::DataSet]
|
21
|
+
# @param imported_setting_keys [Array<String,Symbol>]
|
22
|
+
# @option prefix [String, Symbol]
|
23
|
+
# @option raw [Boolean]
|
24
|
+
# @option mappings [Hash<String|Symbol,String|Symbol>]
|
25
|
+
# @return [void]
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
# @since 0.18.0
|
29
|
+
def import_settings(
|
30
|
+
imported_config,
|
31
|
+
*imported_setting_keys,
|
32
|
+
prefix: Qonfig::Imports::Abstract::EMPTY_PREFIX,
|
33
|
+
raw: Qonfig::Imports::Abstract::DEFAULT_RAW_BEHAVIOR,
|
34
|
+
mappings: Qonfig::Imports::Mappings::EMPTY_MAPPINGS
|
35
|
+
)
|
36
|
+
Qonfig::Imports::General.import!(
|
37
|
+
self,
|
38
|
+
imported_config,
|
39
|
+
*imported_setting_keys,
|
40
|
+
prefix: prefix,
|
41
|
+
raw: raw,
|
42
|
+
mappings: mappings
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.18.0
|
5
|
+
module Qonfig::Imports::Export
|
6
|
+
class << self
|
7
|
+
# @param exportable_object [Object]
|
8
|
+
# @param exported_config [Qonfig::DataSet]
|
9
|
+
# @param exported_setting_keys [Array<String,Symbol>]
|
10
|
+
# @option mappings [Hash<String|Symbol,String|Symbol>]
|
11
|
+
# @option raw [Boolean]
|
12
|
+
# @option prefix [String, Symbol]
|
13
|
+
# @return [void]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
# @since 0.18.0
|
17
|
+
def export!(
|
18
|
+
exportable_object,
|
19
|
+
exported_config,
|
20
|
+
*exported_setting_keys,
|
21
|
+
mappings: Qonfig::Imports::Abstract::EMPTY_MAPPINGS,
|
22
|
+
raw: false,
|
23
|
+
prefix: Qonfig::Imports::Abstract::EMPTY_PREFIX
|
24
|
+
)
|
25
|
+
unless exportable_object.is_a?(Module)
|
26
|
+
exportable_object = exportable_object.singleton_class
|
27
|
+
end
|
28
|
+
|
29
|
+
Qonfig::Imports::General.import!(
|
30
|
+
exportable_object,
|
31
|
+
exported_config,
|
32
|
+
*exported_setting_keys,
|
33
|
+
prefix: prefix,
|
34
|
+
raw: raw,
|
35
|
+
mappings: mappings
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.18.0
|
5
|
+
class Qonfig::Imports::General
|
6
|
+
class << self
|
7
|
+
# @param seeded_klass [Class, Object]
|
8
|
+
# @param imported_config [Qonfig::DataSet]
|
9
|
+
# @param imported_keys [Array<String, Symbol>]
|
10
|
+
# @option mappings [Hash<String|Symbol,String|Symbol>]
|
11
|
+
# @option prefix [String, Symbol]
|
12
|
+
# @option raw [Boolean]
|
13
|
+
# @return void]
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
# @since 0.18.0
|
17
|
+
def import!(
|
18
|
+
seeded_klass,
|
19
|
+
imported_config,
|
20
|
+
*imported_keys,
|
21
|
+
mappings: EMPTY_MAPPINGS,
|
22
|
+
prefix: EMPTY_PREFIX,
|
23
|
+
raw: false
|
24
|
+
)
|
25
|
+
new(
|
26
|
+
seeded_klass,
|
27
|
+
imported_config,
|
28
|
+
*imported_keys,
|
29
|
+
mappings: mappings,
|
30
|
+
prefix: prefix,
|
31
|
+
raw: raw
|
32
|
+
).import!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param seeded_klass [Class, Object]
|
37
|
+
# @param imported_config [Qonfig::DataSet]
|
38
|
+
# @param imported_keys [Array<String, Symbol>]
|
39
|
+
# @option mappings [Hash<String|Symbol,String|Symbol>]
|
40
|
+
# @option prefix [String, Symbol]
|
41
|
+
# @option raw [Boolean]
|
42
|
+
# @return void]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
# @since 0.18.0
|
46
|
+
def initialize(
|
47
|
+
seeded_klass,
|
48
|
+
imported_config,
|
49
|
+
*imported_keys,
|
50
|
+
mappings: EMPTY_MAPPINGS,
|
51
|
+
prefix: EMPTY_PREFIX,
|
52
|
+
raw: false
|
53
|
+
)
|
54
|
+
@seeded_klass = seeded_klass
|
55
|
+
@direct_key_importer = build_direct_key_importer(
|
56
|
+
seeded_klass, imported_config, *imported_keys, prefix: prefix, raw: raw
|
57
|
+
)
|
58
|
+
@mappings_importer = build_mappings_importer(
|
59
|
+
seeded_klass, imported_config, mappings: mappings, prefix: prefix, raw: raw
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param settings_interface [Module]
|
64
|
+
# @return [void]
|
65
|
+
#
|
66
|
+
# @api private
|
67
|
+
# @since 0.18.0
|
68
|
+
def import!(settings_interface = Module.new)
|
69
|
+
direct_key_importer.import!(settings_interface)
|
70
|
+
mappings_importer.import!(settings_interface)
|
71
|
+
seeded_klass.include(settings_interface)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# @return [Class]
|
77
|
+
#
|
78
|
+
# @api private
|
79
|
+
# @since 0.18.0
|
80
|
+
attr_reader :seeded_klass
|
81
|
+
|
82
|
+
# @return [Qonfig::Imports::DirectKey]
|
83
|
+
#
|
84
|
+
# @api private
|
85
|
+
# @since 0.18.0
|
86
|
+
attr_reader :direct_key_importer
|
87
|
+
|
88
|
+
# @return [Qonfig::Imports::Mappings]
|
89
|
+
#
|
90
|
+
# @api private
|
91
|
+
# @since 0.18.0
|
92
|
+
attr_reader :mappings_importer
|
93
|
+
|
94
|
+
# @param seeded_klass [Class]
|
95
|
+
# @param imported_config [Qonfig::DataSet]
|
96
|
+
# @param imported_keys [Array<String,Symbol>]
|
97
|
+
# @option prefix [String, Symbol]
|
98
|
+
# @option raw [Boolean]
|
99
|
+
# @return [Qonfig::Imports::DirectKey]
|
100
|
+
#
|
101
|
+
# @api private
|
102
|
+
# @since 0.18.0
|
103
|
+
def build_direct_key_importer(seeded_klass, imported_config, *imported_keys, prefix:, raw:)
|
104
|
+
Qonfig::Imports::DirectKey.new(
|
105
|
+
seeded_klass,
|
106
|
+
imported_config,
|
107
|
+
*imported_keys,
|
108
|
+
prefix: prefix,
|
109
|
+
raw: raw
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
# @param seeded_klass [Class]
|
114
|
+
# @param imported_config [Qonfig::DataSet]
|
115
|
+
# @option mappings [Hash<Symbol|String,Symbol|String>]
|
116
|
+
# @option prefix [String, Symbol]
|
117
|
+
# @option raw [Boolean]
|
118
|
+
# @return [Qonfig::Imports::Mappings]
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
# @since 0.18.0
|
122
|
+
def build_mappings_importer(seeded_klass, imported_config, mappings:, prefix:, raw:)
|
123
|
+
Qonfig::Imports::Mappings.new(
|
124
|
+
seeded_klass,
|
125
|
+
imported_config,
|
126
|
+
mappings: mappings,
|
127
|
+
prefix: prefix,
|
128
|
+
raw: raw
|
129
|
+
)
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.18.0
|
5
|
+
class Qonfig::Imports::Mappings < Qonfig::Imports::Abstract
|
6
|
+
# @return [Hash]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.18.0
|
10
|
+
EMPTY_MAPPINGS = {}.freeze
|
11
|
+
|
12
|
+
# @param seeded_klass [Class]
|
13
|
+
# @param imported_config [Qonfig::DataSet]
|
14
|
+
# @option prefix [String, Symbol]
|
15
|
+
# @option raw [Boolean]
|
16
|
+
# @option mappings [Hash<Symbol|String,Symbol|String>]
|
17
|
+
# @return [void]
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
# @since 0.18.0
|
21
|
+
def initialize(
|
22
|
+
seeded_klass,
|
23
|
+
imported_config,
|
24
|
+
mappings: EMPTY_MAPPINGS,
|
25
|
+
prefix: EMPTY_PREFIX,
|
26
|
+
raw: DEFAULT_RAW_BEHAVIOR
|
27
|
+
)
|
28
|
+
prevent_incompatible_import_params!(imported_config, prefix, mappings)
|
29
|
+
super(seeded_klass, imported_config, prefix: prefix, raw: raw)
|
30
|
+
@mappings = mappings
|
31
|
+
@key_matchers = build_setting_key_matchers(mappings)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param settings_interface [Module]
|
35
|
+
# @return [void]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
# @since 0.18.0
|
39
|
+
def import!(settings_interface = Module.new) # rubocop:disable Metrics/AbcSize
|
40
|
+
key_matchers.each_pair do |(mapped_method_name, key_matcher)|
|
41
|
+
raise(
|
42
|
+
Qonfig::UnknownSettingError,
|
43
|
+
"Setting with <#{key_matcher.scope_pattern}> key does not exist!"
|
44
|
+
) unless (imported_config.keys(all_variants: true).any? do |setting_key|
|
45
|
+
key_matcher.match?(setting_key)
|
46
|
+
end)
|
47
|
+
|
48
|
+
imported_config.keys(all_variants: true).each do |setting_key|
|
49
|
+
next unless key_matcher.match?(setting_key)
|
50
|
+
|
51
|
+
setting_key_path_sequence = setting_key.split('.')
|
52
|
+
mapped_method_name = "#{prefix}#{mapped_method_name}" unless prefix.empty?
|
53
|
+
|
54
|
+
settings_interface.module_exec(raw, imported_config) do |raw, imported_config|
|
55
|
+
unless raw
|
56
|
+
# NOTE: get setting value via slice_value
|
57
|
+
define_method(mapped_method_name) do
|
58
|
+
imported_config.slice_value(*setting_key_path_sequence)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
# NOTE: get setting object (concrete value or Qonfig::Settings object)
|
62
|
+
define_method(mapped_method_name) do
|
63
|
+
imported_config.dig(*setting_key_path_sequence)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
# @return [Hash<Symbol|String,Symbol|String>]
|
74
|
+
#
|
75
|
+
# @api private
|
76
|
+
# @since 0.18.0
|
77
|
+
attr_reader :mappings
|
78
|
+
|
79
|
+
# @return [Hash<String|Symbol,Qonfig::Settings::KeyMatcher>]
|
80
|
+
#
|
81
|
+
# @api private
|
82
|
+
# @since 0.18.0
|
83
|
+
attr_reader :key_matchers
|
84
|
+
|
85
|
+
# @param imported_config [Qonfig::DataSet]
|
86
|
+
# @param prefix [String, Symbol]
|
87
|
+
# @param mappings [Hash<Symbol|String,Symbol|String>]
|
88
|
+
# @return [void]
|
89
|
+
#
|
90
|
+
# @raise [Qonfig::IncompatibleImportedConfigError]
|
91
|
+
# @raise [Qonfig::IncorrectImportPrefixError]
|
92
|
+
# @raise [Qonfig::IncorrectImportMappingsError]
|
93
|
+
#
|
94
|
+
# @see Qonfig::Imports::Abstract#prevent_incompatible_import_params!
|
95
|
+
#
|
96
|
+
# @api private
|
97
|
+
# @since 0.18.0
|
98
|
+
def prevent_incompatible_import_params!(imported_config, prefix, mappings)
|
99
|
+
super(imported_config, prefix)
|
100
|
+
|
101
|
+
raise(
|
102
|
+
Qonfig::IncorrectImportMappingsError,
|
103
|
+
'Import mappings should be a type of hash with String-or-Symbol keys and values'
|
104
|
+
) unless mappings.is_a?(Hash) && (mappings.each_pair.all? do |(mapping_key, mapping_value)|
|
105
|
+
(mapping_key.is_a?(String) || mapping_key.is_a?(Symbol)) &&
|
106
|
+
(mapping_value.is_a?(String) || mapping_value.is_a?(Symbol))
|
107
|
+
end)
|
108
|
+
end
|
109
|
+
|
110
|
+
# @param mappings [Hash<Symbol|String,Symbol|String>]
|
111
|
+
# @return [Hash<String|Symbol,Qonfig::Settings::KeyMatcher>]
|
112
|
+
#
|
113
|
+
# @api private
|
114
|
+
# @since 0.18.0
|
115
|
+
def build_setting_key_matchers(mappings)
|
116
|
+
mappings.each_with_object({}) do |(mapped_method_name, required_setting_key), matchers|
|
117
|
+
matchers[mapped_method_name] = Qonfig::Settings::KeyMatcher.new(required_setting_key)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api public
|
4
|
+
# @since 0.18.0
|
5
|
+
module Qonfig::Imports
|
6
|
+
require_relative 'imports/abstract'
|
7
|
+
require_relative 'imports/direct_key'
|
8
|
+
require_relative 'imports/mappings'
|
9
|
+
require_relative 'imports/general'
|
10
|
+
require_relative 'imports/dsl'
|
11
|
+
require_relative 'imports/export'
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# @param base_klass [Class]
|
15
|
+
# @return [void]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
# @since 0.18.0
|
19
|
+
def included(base_klass)
|
20
|
+
base_klass.include(Qonfig::Imports::DSL)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -39,7 +39,13 @@ class Qonfig::Settings::KeyMatcher
|
|
39
39
|
# @since 0.13.0
|
40
40
|
INFINITE_REGEXP_PATTERN = '\.*.*'
|
41
41
|
|
42
|
-
# @
|
42
|
+
# @return [String]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
# @since 0.13.0
|
46
|
+
attr_reader :scope_pattern
|
47
|
+
|
48
|
+
# @param scope_pattern [String, Symbol]
|
43
49
|
# @return [void]
|
44
50
|
#
|
45
51
|
# @raise [Qonfig::ArgumentError]
|
@@ -47,9 +53,10 @@ class Qonfig::Settings::KeyMatcher
|
|
47
53
|
# @api private
|
48
54
|
# @since 0.13.0
|
49
55
|
def initialize(scope_pattern)
|
56
|
+
scope_pattern = scope_pattern.to_s if scope_pattern.is_a?(Symbol)
|
50
57
|
raise Qonfig::ArgumentError unless scope_pattern.is_a?(String)
|
51
58
|
|
52
|
-
@scope_pattern = scope_pattern
|
59
|
+
@scope_pattern = scope_pattern.dup.freeze
|
53
60
|
@scope_pattern_size = count_scope_pattern_size(scope_pattern)
|
54
61
|
@pattern_matcher = build_pattern_matcher(scope_pattern)
|
55
62
|
end
|
@@ -72,12 +79,6 @@ class Qonfig::Settings::KeyMatcher
|
|
72
79
|
# @since 0.13.0
|
73
80
|
attr_reader :pattern_matcher
|
74
81
|
|
75
|
-
# @return [String]
|
76
|
-
#
|
77
|
-
# @api private
|
78
|
-
# @since 0.13.0
|
79
|
-
attr_reader :scope_pattern
|
80
|
-
|
81
82
|
# @return [Integer, Float::INFINITY]
|
82
83
|
#
|
83
84
|
# @api private
|
data/lib/qonfig/settings.rb
CHANGED
@@ -194,11 +194,11 @@ class Qonfig::Settings # NOTE: Layout/ClassStructure is disabled only for CORE_M
|
|
194
194
|
# rubocop:disable Metrics/LineLength
|
195
195
|
def __to_hash__(transform_key: BASIC_SETTING_KEY_TRANSFORMER, transform_value: BASIC_SETTING_VALUE_TRANSFORMER)
|
196
196
|
unless transform_key.is_a?(Proc)
|
197
|
-
::Kernel.raise(Qonfig::IncorrectKeyTransformerError, 'Key transformer should be a proc')
|
197
|
+
::Kernel.raise(Qonfig::IncorrectKeyTransformerError, 'Key transformer should be a type of proc')
|
198
198
|
end
|
199
199
|
|
200
200
|
unless transform_value.is_a?(Proc)
|
201
|
-
::Kernel.raise(Qonfig::IncorrectValueTransformerError, 'Value transformer should be a proc')
|
201
|
+
::Kernel.raise(Qonfig::IncorrectValueTransformerError, 'Value transformer should be a type of proc')
|
202
202
|
end
|
203
203
|
|
204
204
|
__lock__.thread_safe_access do
|
@@ -208,6 +208,23 @@ class Qonfig::Settings # NOTE: Layout/ClassStructure is disabled only for CORE_M
|
|
208
208
|
# rubocop:enable Metrics/LineLength
|
209
209
|
alias_method :__to_h__, :__to_hash__
|
210
210
|
|
211
|
+
# @option all_variants [Boolean]
|
212
|
+
# @return [Array<String>]
|
213
|
+
#
|
214
|
+
# @api private
|
215
|
+
# @since 0.18.0
|
216
|
+
def __keys__(all_variants: false)
|
217
|
+
__lock__.thread_safe_access { __setting_keys__(all_variants: all_variants) }
|
218
|
+
end
|
219
|
+
|
220
|
+
# @return [Array<String>]
|
221
|
+
#
|
222
|
+
# @api private
|
223
|
+
# @since 0.18.0
|
224
|
+
def __root_keys__
|
225
|
+
__lock__.thread_safe_access { __root_setting_keys__ }
|
226
|
+
end
|
227
|
+
|
211
228
|
# @return [void]
|
212
229
|
#
|
213
230
|
# @api private
|
@@ -289,6 +306,51 @@ class Qonfig::Settings # NOTE: Layout/ClassStructure is disabled only for CORE_M
|
|
289
306
|
# @since 0.2.0
|
290
307
|
attr_reader :__lock__
|
291
308
|
|
309
|
+
# @option all_variants [Boolean]
|
310
|
+
# @return [Array<String>]
|
311
|
+
#
|
312
|
+
# @api private
|
313
|
+
# @since 0.18.0
|
314
|
+
def __setting_keys__(all_variants: false)
|
315
|
+
# NOTE: generate a set of keys return simple 'a.b.c.d'
|
316
|
+
setting_keys_set = Set.new.tap do |setting_keys|
|
317
|
+
__deep_each_key_value_pair__ do |setting_key, _setting_value|
|
318
|
+
setting_keys << setting_key
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
if all_variants
|
323
|
+
# NOTE:
|
324
|
+
# We have { a: { b: { c: { d : 1 } } } }
|
325
|
+
# Its mean that we have these keys:
|
326
|
+
# - 'a' # => returns { b: { c: { d: 1 } } }
|
327
|
+
# - 'a.b' # => returns { c: { d: 1 } }
|
328
|
+
# - 'a.b.c' # => returns { d: 1 }
|
329
|
+
# - 'a.b.c.d' # => returns 1
|
330
|
+
# So, get them all :)
|
331
|
+
|
332
|
+
setting_keys_set.each_with_object(Set.new) do |setting_key, varianted_setting_keys|
|
333
|
+
setting_key_paths = setting_key.split('.')
|
334
|
+
combination_size = setting_key_paths.size
|
335
|
+
|
336
|
+
combination_size.times do |merged_key_patterns_count|
|
337
|
+
sub_setting_key = setting_key_paths.slice(0..merged_key_patterns_count).join('.')
|
338
|
+
varianted_setting_keys << sub_setting_key
|
339
|
+
end
|
340
|
+
end
|
341
|
+
else
|
342
|
+
setting_keys_set
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
# @return [Array<String>]
|
347
|
+
#
|
348
|
+
# @api private
|
349
|
+
# @since 0.18.0
|
350
|
+
def __root_setting_keys__
|
351
|
+
__options__.keys
|
352
|
+
end
|
353
|
+
|
292
354
|
# @param key_path [Array<String, Symbol>]
|
293
355
|
# @return [Boolean]
|
294
356
|
#
|
@@ -302,7 +364,7 @@ class Qonfig::Settings # NOTE: Layout/ClassStructure is disabled only for CORE_M
|
|
302
364
|
end
|
303
365
|
|
304
366
|
# @param block [Proc]
|
305
|
-
# @return [
|
367
|
+
# @return [Enumerator]
|
306
368
|
#
|
307
369
|
# @yield [setting_key, setting_value]
|
308
370
|
# @yieldparam key [String]
|
@@ -316,7 +378,7 @@ class Qonfig::Settings # NOTE: Layout/ClassStructure is disabled only for CORE_M
|
|
316
378
|
|
317
379
|
# @param initial_setting_key [String, NilClass]
|
318
380
|
# @param block [Proc]
|
319
|
-
# @return [
|
381
|
+
# @return [Enumerator]
|
320
382
|
#
|
321
383
|
# @yield [setting_key, setting_value]
|
322
384
|
# @yieldparam setting_key [String]
|
data/lib/qonfig/version.rb
CHANGED
data/lib/qonfig.rb
CHANGED
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.
|
4
|
+
version: 0.18.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-
|
11
|
+
date: 2019-11-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coveralls
|
@@ -158,6 +158,13 @@ files:
|
|
158
158
|
- lib/qonfig/data_set/lock.rb
|
159
159
|
- lib/qonfig/dsl.rb
|
160
160
|
- lib/qonfig/errors.rb
|
161
|
+
- lib/qonfig/imports.rb
|
162
|
+
- lib/qonfig/imports/abstract.rb
|
163
|
+
- lib/qonfig/imports/direct_key.rb
|
164
|
+
- lib/qonfig/imports/dsl.rb
|
165
|
+
- lib/qonfig/imports/export.rb
|
166
|
+
- lib/qonfig/imports/general.rb
|
167
|
+
- lib/qonfig/imports/mappings.rb
|
161
168
|
- lib/qonfig/loaders.rb
|
162
169
|
- lib/qonfig/loaders/basic.rb
|
163
170
|
- lib/qonfig/loaders/dynamic.rb
|