qonfig 0.13.0 → 0.14.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/.gitignore +1 -0
- data/.rubocop.yml +4 -0
- data/CHANGELOG.md +16 -0
- data/README.md +247 -30
- data/lib/qonfig/commands/expose_json.rb +160 -0
- data/lib/qonfig/commands/expose_self.rb +100 -0
- data/lib/qonfig/commands/expose_yaml.rb +3 -3
- data/lib/qonfig/commands/load_from_env.rb +2 -2
- data/lib/qonfig/commands/load_from_json.rb +2 -2
- data/lib/qonfig/commands/load_from_yaml.rb +2 -2
- data/lib/qonfig/commands.rb +2 -0
- data/lib/qonfig/dsl.rb +24 -0
- data/lib/qonfig/plugins/toml/commands/expose_toml.rb +3 -3
- data/lib/qonfig/plugins/toml/commands/load_from_toml.rb +2 -2
- data/lib/qonfig/settings/builder.rb +3 -2
- data/lib/qonfig/settings/callbacks.rb +11 -3
- data/lib/qonfig/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 881009df231adc7035ec25267ffe8d63a58a66878eb5017cc8a8f2084bd5753d
|
4
|
+
data.tar.gz: de6d094271c5a42737b1f868cb5e6ec61d4e1c15090bf69249f4147ae168ae15
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d65fa945c02683ed445089bfec0f558eda6db5e34157ed315b781cf4410302b5c9a7908580f484ea82657a211674d1a04615291ec81e3fda127d45accf060858
|
7
|
+
data.tar.gz: 6dab7357d7315839c5aeeb8a3a67b53cda1ad535847635ba099f37a979d43517c1b464d85eb7b37bd85c5864c5ee6ac8eb3eadce5ea3bb7f23367a45b34fe30c
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,21 @@
|
|
1
1
|
# Changelog
|
2
2
|
All notable changes to this project will be documented in this file.
|
3
3
|
|
4
|
+
## [0.14.0] - 2019-08-28
|
5
|
+
### Added
|
6
|
+
- `expose_json`
|
7
|
+
- a command that provides an ability to define config settings by loading them from a json file
|
8
|
+
where the concrete settings depends on the chosen environment;
|
9
|
+
- works in `expose_yaml` manner;
|
10
|
+
- `expose_self`
|
11
|
+
- a command that provides an ability to define config settings by loading them from the current file
|
12
|
+
where `__END__` instruction is defined (concrete settings dependes on the chosen environment);
|
13
|
+
- works with `YAML` format;
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
- `Qonfig::Settings::Callbacks` is thread safe now;
|
17
|
+
- Minor refactorings;
|
18
|
+
|
4
19
|
## [0.13.0] - 2019-08-13
|
5
20
|
### Added
|
6
21
|
- Iteration over setting keys (`#each_setting { |key, value| }`, `#deep_each_setting { |key, value| }`);
|
@@ -14,6 +29,7 @@ All notable changes to this project will be documented in this file.
|
|
14
29
|
- Support for **TOML** (`.toml`) format
|
15
30
|
- realized as a plugin (`Qonfig.plugin(:toml)`);
|
16
31
|
- provides `#save_to_toml`, `#load_from_toml`, `#expose_toml` methods and works in `#*_yaml`-like manner);
|
32
|
+
- depends on `gem toml-rb (>= 1)`
|
17
33
|
- Custom `bin/rspec` command:
|
18
34
|
- `bin/rspec -n` - run tests without plugin tests;
|
19
35
|
- `bin/rspec -w` - run all tests;
|
data/README.md
CHANGED
@@ -36,6 +36,7 @@ require 'qonfig'
|
|
36
36
|
- [State freeze](#state-freeze)
|
37
37
|
- [Settings as Predicates](#settings-as-predicates)
|
38
38
|
- [Validation](#validation)
|
39
|
+
- [Introduction](#introdaction)
|
39
40
|
- [Key search pattern](#key-search-pattern)
|
40
41
|
- [Proc-based validation](#proc-based-validation)
|
41
42
|
- [Method-based validation](#method-based-validation)
|
@@ -44,16 +45,28 @@ require 'qonfig'
|
|
44
45
|
- [Load from YAML file](#load-from-yaml-file)
|
45
46
|
- [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
|
46
47
|
- [Load from JSON file](#load-from-json-file)
|
48
|
+
- [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)
|
47
49
|
- [Load from ENV](#load-from-env)
|
48
50
|
- [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
|
51
|
+
- [Expose \_\_END\_\_](#expose-__end__) (aka `expose_self`)
|
49
52
|
- [Save to JSON file](#save-to-json-file) (`save_to_json`)
|
50
53
|
- [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
|
51
54
|
- [Plugins](#plugins)
|
52
55
|
- [toml](#plugins-toml) (provides `load_from_toml`, `save_to_toml`, `expose_toml`)
|
56
|
+
- [Roadmap](#roadmap)
|
53
57
|
---
|
54
58
|
|
55
59
|
## Definition
|
56
60
|
|
61
|
+
- [Definition and Settings Access](#definition-and-access)
|
62
|
+
- [Configuration](#configuration)
|
63
|
+
- [Inheritance](#inheritance)
|
64
|
+
- [Composition](#composition)
|
65
|
+
- [Hash representation](#hash-representation)
|
66
|
+
- [Smart Mixin](#smart-mixin) (`Qonfig::Configurable`)
|
67
|
+
|
68
|
+
---
|
69
|
+
|
57
70
|
### Definition and Access
|
58
71
|
|
59
72
|
```ruby
|
@@ -396,9 +409,14 @@ GeneralApplication.config.to_h
|
|
396
409
|
|
397
410
|
---
|
398
411
|
|
399
|
-
|
400
412
|
## Interaction
|
401
413
|
|
414
|
+
- [Iteration over setting keys](#iteration-over-setting-keys) (`#each_setting`, `#deep_each_setting`)
|
415
|
+
- [Config reloading](#config-reloading) (reload config definitions and option values)
|
416
|
+
- [Clear options](#clear-options) (set to nil)
|
417
|
+
- [State freeze](#state-freeze)
|
418
|
+
- [Settings as Predicates](#settings-as-predicates)
|
419
|
+
|
402
420
|
---
|
403
421
|
|
404
422
|
### Iteration over setting keys
|
@@ -407,7 +425,8 @@ GeneralApplication.config.to_h
|
|
407
425
|
- iterates over the root setting keys;
|
408
426
|
- `#deep_each_setting { |key, value| }`
|
409
427
|
- iterates over all setting keys (deep inside);
|
410
|
-
- key object is represented as a string of `.`-joined
|
428
|
+
- key object is represented as a string of `.`-joined setting key names;
|
429
|
+
|
411
430
|
|
412
431
|
```ruby
|
413
432
|
class Config < Qonfig::DataSet
|
@@ -424,16 +443,24 @@ class Config < Qonfig::DataSet
|
|
424
443
|
end
|
425
444
|
|
426
445
|
config = Config.new
|
446
|
+
```
|
447
|
+
|
448
|
+
#### .each_setting
|
427
449
|
|
428
|
-
|
450
|
+
```ruby
|
429
451
|
config.each_setting { |key, value| { key => value } }
|
452
|
+
|
430
453
|
# result of each step:
|
431
454
|
{ 'db' => <Qonfig::Settings:0x00007ff8> }
|
432
455
|
{ 'telegraf_url' => 'udp://localhost:8094' }
|
433
456
|
{ 'telegraf_prefix' => 'test' }
|
457
|
+
```
|
458
|
+
|
459
|
+
#### .deep_each_setting
|
434
460
|
|
435
|
-
|
461
|
+
```ruby
|
436
462
|
config.deep_each_setting { |key, value| { key => value } }
|
463
|
+
|
437
464
|
# result of each step:
|
438
465
|
{ 'db.creds.user' => 'D@iveR' }
|
439
466
|
{ 'db.creds.password' => 'test123' }
|
@@ -595,25 +622,32 @@ config.settings.database.engine.driver? # => true (true => true)
|
|
595
622
|
|
596
623
|
## Validation
|
597
624
|
|
625
|
+
- [Introduction](#introduction)
|
626
|
+
- [Key Search Pattern](#key-search-pattern)
|
627
|
+
- [Proc-based validation](#proc-based-validation)
|
628
|
+
- [Method-based validation](#method-based-validation)
|
629
|
+
- [Predefined validations](#predefined-validations)
|
630
|
+
|
631
|
+
---
|
632
|
+
|
633
|
+
### Introduction
|
634
|
+
|
598
635
|
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-
|
636
|
+
Settings are validated as keys (matched with a [specific string pattern](#key-search-pattern)).
|
600
637
|
You can validate both a set of keys and each key separately.
|
601
638
|
If you want to check the config object completely you can define a custom validation.
|
602
639
|
|
603
640
|
**Features**:
|
604
|
-
|
605
|
-
- is invoked on any mutation of any setting key
|
641
|
+
- validation is invoked on any mutation of any setting:
|
606
642
|
- during dataset instantiation;
|
607
643
|
- when assigning new values;
|
608
644
|
- when calling `#reload!`;
|
609
645
|
- when calling `#clear!`;
|
610
|
-
|
611
646
|
- provides special [key search pattern](#key-search-pattern) for matching setting key names;
|
612
647
|
- uses the [key search pattern](#key-search-pattern) for definging what the setting key should be validated;
|
613
648
|
- you can define your own custom validation logic and validate dataset instance completely;
|
614
649
|
- validation logic should return **truthy** or **falsy** value;
|
615
|
-
|
616
|
-
- supprots two validation techniques (**proc-based** and **dataset-method-based**)
|
650
|
+
- supprots two validation techniques (**proc-based** ([doc](#proc-based-validation)) and **dataset-method-based** ([doc](#method-based-validation))):
|
617
651
|
- **proc-based** (`setting validation`)
|
618
652
|
```ruby
|
619
653
|
validate 'db.user' do |value|
|
@@ -642,22 +676,23 @@ If you want to check the config object completely you can define a custom valida
|
|
642
676
|
settings.user == User[1]
|
643
677
|
end
|
644
678
|
```
|
645
|
-
|
646
|
-
-
|
647
|
-
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
679
|
+
- provides a **set of standard validations** ([doc](#predefined-validations)):
|
680
|
+
- DSL: `validate 'key.pattern', :predefned_validator`;
|
681
|
+
- validators:
|
682
|
+
- `integer`
|
683
|
+
- `float`
|
684
|
+
- `numeric`
|
685
|
+
- `big_decimal`
|
686
|
+
- `boolean`
|
687
|
+
- `string`
|
688
|
+
- `symbol`
|
689
|
+
- `text` (string or symbol)
|
690
|
+
- `array`
|
691
|
+
- `hash`
|
692
|
+
- `proc`
|
693
|
+
- `class`
|
694
|
+
- `module`
|
695
|
+
- `not_nil`
|
661
696
|
|
662
697
|
---
|
663
698
|
|
@@ -818,12 +853,12 @@ config.settings.enabled = nil # => Qonfig::ValidationError (should be a boolean)
|
|
818
853
|
|
819
854
|
```ruby
|
820
855
|
class Config < Qonfig::DataSet
|
821
|
-
setting :user
|
822
|
-
setting :password
|
856
|
+
setting :user, 'empty'
|
857
|
+
setting :password, 'empty'
|
823
858
|
|
824
859
|
setting :service do
|
825
|
-
setting :provider
|
826
|
-
setting :protocol
|
860
|
+
setting :provider, :empty
|
861
|
+
setting :protocol, :empty
|
827
862
|
setting :on_fail, -> { puts 'atata!' }
|
828
863
|
end
|
829
864
|
|
@@ -851,6 +886,18 @@ config.settings.ignorance = nil # => Qonfig::ValidationError (cant be nil)
|
|
851
886
|
|
852
887
|
## Work with files
|
853
888
|
|
889
|
+
- [Load from YAML file](#load-from-yaml-file)
|
890
|
+
- [Expose YAML](#expose-yaml) (`Rails`-like environment-based YAML configs)
|
891
|
+
- [Load from JSON file](#load-from-json-file)
|
892
|
+
- [Expose JSON](#expose-json) (`Rails`-like environment-based JSON configs)
|
893
|
+
- [Load from ENV](#load-from-env)
|
894
|
+
- [Load from \_\_END\_\_](#load-from-__end__) (aka `load_from_self`)
|
895
|
+
- [Expose \_\_END\_\_](#expose-__end__) (aka `expose_self`)
|
896
|
+
- [Save to JSON file](#save-to-json-file) (`save_to_json`)
|
897
|
+
- [Save to YAML file](#save-to-yaml-file) (`save_to_yaml`)
|
898
|
+
|
899
|
+
---
|
900
|
+
|
854
901
|
### Load from YAML file
|
855
902
|
|
856
903
|
- supports `ERB`;
|
@@ -1097,6 +1144,120 @@ Config.new.to_h # => { "nonexistent_json" => {}, "another_key" => nil }
|
|
1097
1144
|
|
1098
1145
|
---
|
1099
1146
|
|
1147
|
+
### Expose JSON
|
1148
|
+
|
1149
|
+
- load configurations from JSON file in Rails-like manner (with environments);
|
1150
|
+
- works in `load_from_jsom`/`expose_yaml` manner;
|
1151
|
+
- `via:` - how an environment will be determined:
|
1152
|
+
- `:file_name`
|
1153
|
+
- load configuration from JSON file that have an `:env` part in it's name;
|
1154
|
+
- `:env_key`
|
1155
|
+
- load configuration from JSON file;
|
1156
|
+
- concrete configuration should be defined in the root key with `:env` name;
|
1157
|
+
- `env:` - your environment name (must be a type of `String`, `Symbol` or `Numeric`);
|
1158
|
+
- `strict:` - requires the existence of the file and/or key with the name of the used environment:
|
1159
|
+
- `true`:
|
1160
|
+
- file should exist;
|
1161
|
+
- root key with `:env` name should exist (if `via: :env_key` is used);
|
1162
|
+
- raises `Qonfig::ExposeError` if file does not contain the required env key (if `via: :env` key is used);
|
1163
|
+
- raises `Qonfig::FileNotFoundError` if the required file does not exist;
|
1164
|
+
- `false`:
|
1165
|
+
- file is not required;
|
1166
|
+
- root key with `:env` name is not required (if `via: :env_key` is used);
|
1167
|
+
|
1168
|
+
#### Environment is defined as a root key of JSON file
|
1169
|
+
|
1170
|
+
```json
|
1171
|
+
// config/project.json
|
1172
|
+
|
1173
|
+
{
|
1174
|
+
"development": {
|
1175
|
+
"api_mode_enabled": true,
|
1176
|
+
"logging": false,
|
1177
|
+
"db_driver": "sequel",
|
1178
|
+
"throttle_requests": false,
|
1179
|
+
"credentials": {}
|
1180
|
+
},
|
1181
|
+
"test": {
|
1182
|
+
"api_mode_enabled": true,
|
1183
|
+
"logging": false,
|
1184
|
+
"db_driver": "in_memory",
|
1185
|
+
"throttle_requests": false,
|
1186
|
+
"credentials": {}
|
1187
|
+
},
|
1188
|
+
"staging": {
|
1189
|
+
"api_mode_enabled": true,
|
1190
|
+
"logging": true,
|
1191
|
+
"db_driver": "active_record",
|
1192
|
+
"throttle_requests": true,
|
1193
|
+
"credentials": {}
|
1194
|
+
},
|
1195
|
+
"production": {
|
1196
|
+
"api_mode_enabled": true,
|
1197
|
+
"logging": true,
|
1198
|
+
"db_driver": "rom",
|
1199
|
+
"throttle_requests": true,
|
1200
|
+
"credentials": {}
|
1201
|
+
}
|
1202
|
+
}
|
1203
|
+
```
|
1204
|
+
|
1205
|
+
```ruby
|
1206
|
+
class Config < Qonfig::DataSet
|
1207
|
+
expose_json 'config/project.json', via: :env_key, env: :production # load from production env
|
1208
|
+
|
1209
|
+
# NOTE: in rails-like application you can use this:
|
1210
|
+
expose_json 'config/project.json', via: :env_key, env: Rails.env
|
1211
|
+
end
|
1212
|
+
|
1213
|
+
config = Config.new
|
1214
|
+
|
1215
|
+
config.settings.api_mode_enabled # => true (from :production subset of keys)
|
1216
|
+
config.settings.logging # => true (from :production subset of keys)
|
1217
|
+
config.settings.db_driver # => "rom" (from :production subset of keys)
|
1218
|
+
config.settings.throttle_requests # => true (from :production subset of keys)
|
1219
|
+
config.settings.credentials # => {} (from :production subset of keys)
|
1220
|
+
```
|
1221
|
+
|
1222
|
+
#### Environment is defined as a part of JSON file name
|
1223
|
+
|
1224
|
+
```json
|
1225
|
+
// config/sidekiq.staging.json
|
1226
|
+
{
|
1227
|
+
"web": {
|
1228
|
+
"username": "staging_admin",
|
1229
|
+
"password": "staging_password"
|
1230
|
+
}
|
1231
|
+
}
|
1232
|
+
```
|
1233
|
+
|
1234
|
+
```json
|
1235
|
+
// config/sidekiq.production.json
|
1236
|
+
{
|
1237
|
+
"web": {
|
1238
|
+
"username": "urj1o2",
|
1239
|
+
"password": "u192jd0ixz0"
|
1240
|
+
}
|
1241
|
+
}
|
1242
|
+
```
|
1243
|
+
|
1244
|
+
```ruby
|
1245
|
+
class SidekiqConfig < Qonfig::DataSet
|
1246
|
+
# NOTE: file name should be described WITHOUT environment part (in file name attribute)
|
1247
|
+
expose_json 'config/sidekiq.json', via: :file_name, env: :staging # load from staging env
|
1248
|
+
|
1249
|
+
# NOTE: in rails-like application you can use this:
|
1250
|
+
expose_json 'config/sidekiq.json', via: :file_name, env: Rails.env
|
1251
|
+
end
|
1252
|
+
|
1253
|
+
config = SidekiqConfig.new
|
1254
|
+
|
1255
|
+
config.settings.web.username # => "staging_admin" (from sidekiq.staging.json)
|
1256
|
+
config.settings.web.password # => "staging_password" (from sidekiq.staging.json)
|
1257
|
+
```
|
1258
|
+
|
1259
|
+
---
|
1260
|
+
|
1100
1261
|
### Load from ENV
|
1101
1262
|
|
1102
1263
|
- `:convert_values` (`false` by default):
|
@@ -1168,6 +1329,7 @@ config.settings['RUN_CI'] # => '1'
|
|
1168
1329
|
### Load from \_\_END\_\_
|
1169
1330
|
|
1170
1331
|
- aka `load_from_self`
|
1332
|
+
- works with `YAML` format;
|
1171
1333
|
|
1172
1334
|
```ruby
|
1173
1335
|
class Config < Qonfig::DataSet
|
@@ -1206,6 +1368,57 @@ connection_timeout:
|
|
1206
1368
|
|
1207
1369
|
---
|
1208
1370
|
|
1371
|
+
### Expose \_\_END\_\_
|
1372
|
+
|
1373
|
+
- aka `expose_self`;
|
1374
|
+
- works in `expose_json` and `expose_yaml` manner, but with `__END__` instruction of the current file;
|
1375
|
+
- `env:` - your environment name (must be a type of `String`, `Symbol` or `Numeric`);
|
1376
|
+
- works with `YAML` format;
|
1377
|
+
|
1378
|
+
```ruby
|
1379
|
+
class Config < Qonfig::DataSet
|
1380
|
+
expose_self env: :production
|
1381
|
+
|
1382
|
+
# NOTE: for Rails-like applications you can use this:
|
1383
|
+
expose_self env: Rails.env
|
1384
|
+
end
|
1385
|
+
|
1386
|
+
config = Config.new
|
1387
|
+
|
1388
|
+
config.settings.log # => true (from :production environment)
|
1389
|
+
config.settings.api_enabled # => true (from :production environment)
|
1390
|
+
config.settings.creds.user # => "D@iVeR" (from :production environment)
|
1391
|
+
config.settings.creds.password # => "test123" (from :production environment)
|
1392
|
+
|
1393
|
+
__END__
|
1394
|
+
default: &default
|
1395
|
+
log: false
|
1396
|
+
api_enabled: true
|
1397
|
+
creds:
|
1398
|
+
user: admin
|
1399
|
+
password: 1234
|
1400
|
+
|
1401
|
+
development:
|
1402
|
+
<<: *default
|
1403
|
+
log: true
|
1404
|
+
|
1405
|
+
test:
|
1406
|
+
<<: *default
|
1407
|
+
log: false
|
1408
|
+
|
1409
|
+
staging:
|
1410
|
+
<<: *default
|
1411
|
+
|
1412
|
+
production:
|
1413
|
+
<<: *default
|
1414
|
+
log: true
|
1415
|
+
creds:
|
1416
|
+
user: D@iVeR
|
1417
|
+
password: test123
|
1418
|
+
```
|
1419
|
+
|
1420
|
+
---
|
1421
|
+
|
1209
1422
|
### Save to JSON file
|
1210
1423
|
|
1211
1424
|
- `#save_to_json` - represents config object as a json structure and saves it to a file:
|
@@ -1360,6 +1573,10 @@ Qonfig.plugins # => array of strings
|
|
1360
1573
|
Qonfig.plugin(:plugin_name) # or Qonfig.plugin('plugin_name')
|
1361
1574
|
```
|
1362
1575
|
|
1576
|
+
Provided plugins:
|
1577
|
+
|
1578
|
+
- [toml](#plugins-toml) (provides `load_from_toml`, `save_to_toml`, `expose_toml`)
|
1579
|
+
|
1363
1580
|
---
|
1364
1581
|
|
1365
1582
|
### Plugins: toml
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.14.0
|
5
|
+
class Qonfig::Commands::ExposeJSON < Qonfig::Commands::Base
|
6
|
+
# @return [Hash]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.14.0
|
10
|
+
EXPOSERS = { file_name: :file_name, env_key: :env_key }.freeze
|
11
|
+
|
12
|
+
# @return [Hash]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.14.0
|
16
|
+
EMPTY_JSON_DATA = {}.freeze
|
17
|
+
|
18
|
+
# @return [String]
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
# @since 0.14.0
|
22
|
+
attr_reader :file_path
|
23
|
+
|
24
|
+
# @return [Boolean]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
# @since 0.14.0
|
28
|
+
attr_reader :strict
|
29
|
+
|
30
|
+
# @return [Symbol]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
# @since 0.14.0
|
34
|
+
attr_reader :via
|
35
|
+
|
36
|
+
# @return [Symbol, String]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
# @since 0.14.0
|
40
|
+
attr_reader :env
|
41
|
+
|
42
|
+
# @param file_path [String]
|
43
|
+
# @option strict [Boolean]
|
44
|
+
# @option via [Symbol]
|
45
|
+
# @option env [String, Symbol]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
# @since 0.14.0
|
49
|
+
def initialize(file_path, strict: true, via:, env:)
|
50
|
+
unless env.is_a?(Symbol) || env.is_a?(String) || env.is_a?(Numeric)
|
51
|
+
raise Qonfig::ArgumentError, ':env should be a string or a symbol'
|
52
|
+
end
|
53
|
+
|
54
|
+
raise Qonfig::ArgumentError, ':env should be provided' if env.to_s.empty?
|
55
|
+
raise Qonfig::ArgumentError, 'used :via is unsupported' unless EXPOSERS.key?(via)
|
56
|
+
|
57
|
+
@file_path = file_path
|
58
|
+
@strict = strict
|
59
|
+
@via = via
|
60
|
+
@env = env
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param data_set [Qonfig::DataSet]
|
64
|
+
# @param settings [Qonfig::Settings]
|
65
|
+
# @return [void]
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
# @since 0.14.0
|
69
|
+
def call(data_set, settings)
|
70
|
+
case via
|
71
|
+
when EXPOSERS[:file_name]
|
72
|
+
expose_file_name!(settings)
|
73
|
+
when EXPOSERS[:env_key]
|
74
|
+
expose_env_key!(settings)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# @param settings [Qonfig::Settings]
|
81
|
+
# @return [void]
|
82
|
+
#
|
83
|
+
# @api private
|
84
|
+
# @since 0.14.0
|
85
|
+
# rubocop:disable Metrics/AbcSize
|
86
|
+
def expose_file_name!(settings)
|
87
|
+
# NOTE: transform file name (insert environment name into the file name)
|
88
|
+
# from: path/to/file/file_name.file_extension
|
89
|
+
# to: path/to/file/file_name.env_name.file_extension
|
90
|
+
|
91
|
+
pathname = Pathname.new(file_path)
|
92
|
+
dirname = pathname.dirname
|
93
|
+
extname = pathname.extname.to_s
|
94
|
+
basename = pathname.basename.to_s.sub!(extname, '')
|
95
|
+
envname = [env.to_s, extname].reject(&:empty?).join('')
|
96
|
+
envfile = [basename, envname].reject(&:empty?).join('.')
|
97
|
+
realfile = dirname.join(envfile).to_s
|
98
|
+
|
99
|
+
json_data = load_json_data(realfile)
|
100
|
+
json_based_settings = build_data_set_klass(json_data).new.settings
|
101
|
+
|
102
|
+
settings.__append_settings__(json_based_settings)
|
103
|
+
end
|
104
|
+
# rubocop:enable Metrics/AbcSize
|
105
|
+
|
106
|
+
# @param settings [Qonfig::Settings]
|
107
|
+
# @return [void]
|
108
|
+
#
|
109
|
+
# @raise [Qonfig::ExposeError]
|
110
|
+
# @raise [Qonfig::IncompatibleJSONStructureError]
|
111
|
+
#
|
112
|
+
# @api private
|
113
|
+
# @since 0.14.0
|
114
|
+
# rubocop:disable Metrics/AbcSize
|
115
|
+
def expose_env_key!(settings)
|
116
|
+
json_data = load_json_data(file_path)
|
117
|
+
json_data_slice = json_data[env] || json_data[env.to_s] || json_data[env.to_sym]
|
118
|
+
json_data_slice = EMPTY_JSON_DATA.dup if json_data_slice.nil? && !strict
|
119
|
+
|
120
|
+
raise(
|
121
|
+
Qonfig::ExposeError,
|
122
|
+
"#{file_path} file does not contain settings with <#{env}> environment key!"
|
123
|
+
) unless json_data_slice
|
124
|
+
|
125
|
+
raise(
|
126
|
+
Qonfig::IncompatibleJSONStructureError,
|
127
|
+
'JSON content should have a hash-like structure'
|
128
|
+
) unless json_data_slice.is_a?(Hash)
|
129
|
+
|
130
|
+
json_based_settings = build_data_set_klass(json_data_slice).new.settings
|
131
|
+
|
132
|
+
settings.__append_settings__(json_based_settings)
|
133
|
+
end
|
134
|
+
# rubocop:enable Metrics/AbcSize
|
135
|
+
|
136
|
+
# @param file_path [String]
|
137
|
+
# @return [Hash]
|
138
|
+
#
|
139
|
+
# @raise [Qonfig::IncompatibleJSONStructureError]
|
140
|
+
#
|
141
|
+
# @api private
|
142
|
+
# @since 0.14.0
|
143
|
+
def load_json_data(file_path)
|
144
|
+
Qonfig::Loaders::JSON.load_file(file_path, fail_on_unexist: strict).tap do |json_data|
|
145
|
+
raise(
|
146
|
+
Qonfig::IncompatibleJSONStructureError,
|
147
|
+
'JSON content should have a hash-like structure'
|
148
|
+
) unless json_data.is_a?(Hash)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# @param json_data [Hash]
|
153
|
+
# @return [Class<Qonfig::DataSet>]
|
154
|
+
#
|
155
|
+
# @api private
|
156
|
+
# @since 0.14.0
|
157
|
+
def build_data_set_klass(json_data)
|
158
|
+
Qonfig::DataSet::ClassBuilder.build_from_hash(json_data)
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 0.14.0
|
5
|
+
class Qonfig::Commands::ExposeSelf < Qonfig::Commands::Base
|
6
|
+
# @return [String]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
# @since 0.14.0
|
10
|
+
attr_reader :caller_location
|
11
|
+
|
12
|
+
# @return [Symbol, String]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
# @since 0.14.0
|
16
|
+
attr_reader :env
|
17
|
+
|
18
|
+
# @param caller_location [String]
|
19
|
+
# @option env [String, Symbol]
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
# @since 0.14.0
|
23
|
+
def initialize(caller_location, env:)
|
24
|
+
unless env.is_a?(Symbol) || env.is_a?(String)
|
25
|
+
raise Qonfig::ArgumentError, ':env should be a string or a symbol'
|
26
|
+
end
|
27
|
+
|
28
|
+
if env.to_s.empty?
|
29
|
+
raise Qonfig::ArgumentError, ':env should be provided'
|
30
|
+
end
|
31
|
+
|
32
|
+
@caller_location = caller_location
|
33
|
+
@env = env
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param data_set [Qonfig::DataSet]
|
37
|
+
# @param settings [Qonfig::Settings]
|
38
|
+
# @return [void]
|
39
|
+
#
|
40
|
+
# @api private
|
41
|
+
# @since 0.14.0
|
42
|
+
def call(data_set, settings)
|
43
|
+
yaml_data = load_self_placed_yaml_data
|
44
|
+
yaml_data_slice = yaml_data[env] || yaml_data[env.to_s] || yaml_data[env.to_sym]
|
45
|
+
|
46
|
+
raise(
|
47
|
+
Qonfig::ExposeError,
|
48
|
+
"#{file_path} file does not contain settings with <#{env}> environment key!"
|
49
|
+
) unless yaml_data_slice
|
50
|
+
|
51
|
+
raise(
|
52
|
+
Qonfig::IncompatibleYAMLStructureError,
|
53
|
+
'YAML content should have a hash-like structure'
|
54
|
+
) unless yaml_data_slice.is_a?(Hash)
|
55
|
+
|
56
|
+
yaml_based_settings = build_data_set_klass(yaml_data_slice).new.settings
|
57
|
+
settings.__append_settings__(yaml_based_settings)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# @return [Hash]
|
63
|
+
#
|
64
|
+
# @raise [Qonfig::SelfDataNotFound]
|
65
|
+
# @raise [Qonfig::IncompatibleYAMLStructureError]
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
# @since 0.14.0
|
69
|
+
def load_self_placed_yaml_data
|
70
|
+
caller_file = caller_location.split(':').first
|
71
|
+
|
72
|
+
raise(
|
73
|
+
Qonfig::SelfDataNotFoundError,
|
74
|
+
"Caller file does not exist! (location: #{caller_location})"
|
75
|
+
) unless File.exist?(caller_file)
|
76
|
+
|
77
|
+
data_match = IO.read(caller_file).match(/\n__END__\n(?<end_data>.*)/m)
|
78
|
+
raise Qonfig::SelfDataNotFoundError, '__END__ data not found!' unless data_match
|
79
|
+
|
80
|
+
end_data = data_match[:end_data]
|
81
|
+
raise Qonfig::SelfDataNotFoundError, '__END__ data not found!' unless end_data
|
82
|
+
|
83
|
+
yaml_data = Qonfig::Loaders::YAML.load(end_data)
|
84
|
+
raise(
|
85
|
+
Qonfig::IncompatibleYAMLStructureError,
|
86
|
+
'YAML content should have a hash-like structure'
|
87
|
+
) unless yaml_data.is_a?(Hash)
|
88
|
+
|
89
|
+
yaml_data
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param self_placed_yaml_data [Hash]
|
93
|
+
# @return [Class<Qonfig::DataSet>]
|
94
|
+
#
|
95
|
+
# @api private
|
96
|
+
# @since 0.14.0
|
97
|
+
def build_data_set_klass(self_placed_yaml_data)
|
98
|
+
Qonfig::DataSet::ClassBuilder.build_from_hash(self_placed_yaml_data)
|
99
|
+
end
|
100
|
+
end
|
@@ -97,7 +97,7 @@ class Qonfig::Commands::ExposeYAML < Qonfig::Commands::Base
|
|
97
97
|
realfile = dirname.join(envfile).to_s
|
98
98
|
|
99
99
|
yaml_data = load_yaml_data(realfile)
|
100
|
-
yaml_based_settings =
|
100
|
+
yaml_based_settings = build_data_set_klass(yaml_data).new.settings
|
101
101
|
|
102
102
|
settings.__append_settings__(yaml_based_settings)
|
103
103
|
end
|
@@ -127,7 +127,7 @@ class Qonfig::Commands::ExposeYAML < Qonfig::Commands::Base
|
|
127
127
|
'YAML content should have a hash-like structure'
|
128
128
|
) unless yaml_data_slice.is_a?(Hash)
|
129
129
|
|
130
|
-
yaml_based_settings =
|
130
|
+
yaml_based_settings = build_data_set_klass(yaml_data_slice).new.settings
|
131
131
|
|
132
132
|
settings.__append_settings__(yaml_based_settings)
|
133
133
|
end
|
@@ -154,7 +154,7 @@ class Qonfig::Commands::ExposeYAML < Qonfig::Commands::Base
|
|
154
154
|
#
|
155
155
|
# @api private
|
156
156
|
# @since 0.7.0
|
157
|
-
def
|
157
|
+
def build_data_set_klass(yaml_data)
|
158
158
|
Qonfig::DataSet::ClassBuilder.build_from_hash(yaml_data)
|
159
159
|
end
|
160
160
|
end
|
@@ -64,7 +64,7 @@ class Qonfig::Commands::LoadFromENV < Qonfig::Commands::Base
|
|
64
64
|
def call(data_set, settings)
|
65
65
|
env_data = extract_env_data
|
66
66
|
|
67
|
-
env_based_settings =
|
67
|
+
env_based_settings = build_data_set_klass(env_data).new.settings
|
68
68
|
|
69
69
|
settings.__append_settings__(env_based_settings)
|
70
70
|
end
|
@@ -90,7 +90,7 @@ class Qonfig::Commands::LoadFromENV < Qonfig::Commands::Base
|
|
90
90
|
#
|
91
91
|
# @api private
|
92
92
|
# @since 0.2.0
|
93
|
-
def
|
93
|
+
def build_data_set_klass(env_data)
|
94
94
|
Qonfig::DataSet::ClassBuilder.build_from_hash(env_data)
|
95
95
|
end
|
96
96
|
end
|
@@ -39,7 +39,7 @@ class Qonfig::Commands::LoadFromJSON < Qonfig::Commands::Base
|
|
39
39
|
'JSON object should have a hash-like structure'
|
40
40
|
) unless json_data.is_a?(Hash)
|
41
41
|
|
42
|
-
json_based_settings =
|
42
|
+
json_based_settings = build_data_set_klass(json_data).new.settings
|
43
43
|
|
44
44
|
settings.__append_settings__(json_based_settings)
|
45
45
|
end
|
@@ -51,7 +51,7 @@ class Qonfig::Commands::LoadFromJSON < Qonfig::Commands::Base
|
|
51
51
|
#
|
52
52
|
# @api private
|
53
53
|
# @since 0.5.0
|
54
|
-
def
|
54
|
+
def build_data_set_klass(json_data)
|
55
55
|
Qonfig::DataSet::ClassBuilder.build_from_hash(json_data)
|
56
56
|
end
|
57
57
|
end
|
@@ -41,7 +41,7 @@ class Qonfig::Commands::LoadFromYAML < Qonfig::Commands::Base
|
|
41
41
|
'YAML content should have a hash-like structure'
|
42
42
|
) unless yaml_data.is_a?(Hash)
|
43
43
|
|
44
|
-
yaml_based_settings =
|
44
|
+
yaml_based_settings = build_data_set_klass(yaml_data).new.settings
|
45
45
|
|
46
46
|
settings.__append_settings__(yaml_based_settings)
|
47
47
|
end
|
@@ -53,7 +53,7 @@ class Qonfig::Commands::LoadFromYAML < Qonfig::Commands::Base
|
|
53
53
|
#
|
54
54
|
# @api private
|
55
55
|
# @since 0.2.0
|
56
|
-
def
|
56
|
+
def build_data_set_klass(yaml_data)
|
57
57
|
Qonfig::DataSet::ClassBuilder.build_from_hash(yaml_data)
|
58
58
|
end
|
59
59
|
end
|
data/lib/qonfig/commands.rb
CHANGED
data/lib/qonfig/dsl.rb
CHANGED
@@ -119,4 +119,28 @@ module Qonfig::DSL
|
|
119
119
|
def expose_yaml(file_path, strict: true, via:, env:)
|
120
120
|
commands << Qonfig::Commands::ExposeYAML.new(file_path, strict: strict, via: via, env: env)
|
121
121
|
end
|
122
|
+
|
123
|
+
# @param file_path [String]
|
124
|
+
# @option strict [Boolean]
|
125
|
+
# @option via [Symbol]
|
126
|
+
# @option env [Symbol, String]
|
127
|
+
# @return [void]
|
128
|
+
#
|
129
|
+
# @api public
|
130
|
+
# @since 0.14.0
|
131
|
+
def expose_json(file_path, strict: true, via:, env:)
|
132
|
+
commands << Qonfig::Commands::ExposeJSON.new(file_path, strict: strict, via: via, env: env)
|
133
|
+
end
|
134
|
+
|
135
|
+
# @option env [Symbol, String]
|
136
|
+
# @return [void]
|
137
|
+
#
|
138
|
+
# @see Qonfig::Commands::LoadFromSelf
|
139
|
+
#
|
140
|
+
# @api public
|
141
|
+
# @since 0.14.0
|
142
|
+
def expose_self(env:)
|
143
|
+
caller_location = caller(1, 1).first
|
144
|
+
commands << Qonfig::Commands::ExposeSelf.new(caller_location, env: env)
|
145
|
+
end
|
122
146
|
end
|
@@ -97,7 +97,7 @@ class Qonfig::Commands::ExposeTOML < Qonfig::Commands::Base
|
|
97
97
|
realfile = dirname.join(envfile).to_s
|
98
98
|
|
99
99
|
toml_data = load_toml_data(realfile)
|
100
|
-
toml_based_settings =
|
100
|
+
toml_based_settings = builde_data_set_klass(toml_data).new.settings
|
101
101
|
|
102
102
|
settings.__append_settings__(toml_based_settings)
|
103
103
|
end
|
@@ -121,7 +121,7 @@ class Qonfig::Commands::ExposeTOML < Qonfig::Commands::Base
|
|
121
121
|
"#{file_path} file does not contain settings with <#{env}> environment key!"
|
122
122
|
) unless toml_data_slice
|
123
123
|
|
124
|
-
toml_based_settings =
|
124
|
+
toml_based_settings = builde_data_set_klass(toml_data_slice).new.settings
|
125
125
|
|
126
126
|
settings.__append_settings__(toml_based_settings)
|
127
127
|
end
|
@@ -141,7 +141,7 @@ class Qonfig::Commands::ExposeTOML < Qonfig::Commands::Base
|
|
141
141
|
#
|
142
142
|
# @api private
|
143
143
|
# @since 0.12.0
|
144
|
-
def
|
144
|
+
def builde_data_set_klass(toml_data)
|
145
145
|
Qonfig::DataSet::ClassBuilder.build_from_hash(toml_data)
|
146
146
|
end
|
147
147
|
end
|
@@ -33,7 +33,7 @@ class Qonfig::Commands::LoadFromTOML < Qonfig::Commands::Base
|
|
33
33
|
# @since 0.12.0
|
34
34
|
def call(data_set, settings)
|
35
35
|
toml_data = Qonfig::Loaders::TOML.load_file(file_path, fail_on_unexist: strict)
|
36
|
-
toml_based_settings =
|
36
|
+
toml_based_settings = build_data_set_klass(toml_data).new.settings
|
37
37
|
settings.__append_settings__(toml_based_settings)
|
38
38
|
end
|
39
39
|
|
@@ -44,7 +44,7 @@ class Qonfig::Commands::LoadFromTOML < Qonfig::Commands::Base
|
|
44
44
|
#
|
45
45
|
# @api private
|
46
46
|
# @since 0.12.0
|
47
|
-
def
|
47
|
+
def build_data_set_klass(toml_data)
|
48
48
|
Qonfig::DataSet::ClassBuilder.build_from_hash(toml_data)
|
49
49
|
end
|
50
50
|
end
|
@@ -25,9 +25,10 @@ module Qonfig::Settings::Builder
|
|
25
25
|
# @api private
|
26
26
|
# @since 0.13.0
|
27
27
|
def build_mutation_callbacks(data_set)
|
28
|
+
validation_callback = proc { data_set.validate! }
|
29
|
+
|
28
30
|
Qonfig::Settings::Callbacks.new.tap do |callbacks|
|
29
|
-
|
30
|
-
callbacks.add(proc { data_set.validate! })
|
31
|
+
callbacks.add(validation_callback)
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
@@ -13,6 +13,7 @@ class Qonfig::Settings::Callbacks
|
|
13
13
|
# @since 0.13.0
|
14
14
|
def initialize
|
15
15
|
@callbacks = []
|
16
|
+
@lock = Mutex.new
|
16
17
|
end
|
17
18
|
|
18
19
|
# @return [void]
|
@@ -20,7 +21,7 @@ class Qonfig::Settings::Callbacks
|
|
20
21
|
# @api private
|
21
22
|
# @since 0.13.0
|
22
23
|
def call
|
23
|
-
callbacks.each(&:call)
|
24
|
+
thread_safe { callbacks.each(&:call) }
|
24
25
|
end
|
25
26
|
|
26
27
|
# @param callback [Proc, Qonfig::Settings::Callbacks, #call]
|
@@ -29,9 +30,8 @@ class Qonfig::Settings::Callbacks
|
|
29
30
|
# @api private
|
30
31
|
# @since 0.13.0
|
31
32
|
def add(callback)
|
32
|
-
callbacks << callback
|
33
|
+
thread_safe { callbacks << callback }
|
33
34
|
end
|
34
|
-
attr_reader :callback
|
35
35
|
|
36
36
|
private
|
37
37
|
|
@@ -40,4 +40,12 @@ class Qonfig::Settings::Callbacks
|
|
40
40
|
# @api private
|
41
41
|
# @since 0.13.0
|
42
42
|
attr_reader :callbacks
|
43
|
+
|
44
|
+
# @return [Any]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
# @since 0.14.0
|
48
|
+
def thread_safe(&block)
|
49
|
+
@lock.owned? ? yield : @lock.synchronize(&block)
|
50
|
+
end
|
43
51
|
end
|
data/lib/qonfig/version.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.14.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-08-
|
11
|
+
date: 2019-08-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coveralls
|
@@ -140,6 +140,8 @@ files:
|
|
140
140
|
- lib/qonfig/commands/add_option.rb
|
141
141
|
- lib/qonfig/commands/base.rb
|
142
142
|
- lib/qonfig/commands/compose.rb
|
143
|
+
- lib/qonfig/commands/expose_json.rb
|
144
|
+
- lib/qonfig/commands/expose_self.rb
|
143
145
|
- lib/qonfig/commands/expose_yaml.rb
|
144
146
|
- lib/qonfig/commands/load_from_env.rb
|
145
147
|
- lib/qonfig/commands/load_from_env/value_converter.rb
|