qonfig 0.29.0 → 0.31.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/.github/workflows/build.yml +50 -0
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +22 -0
- data/Gemfile +0 -3
- data/Gemfile.lock +77 -98
- data/LICENSE.txt +1 -1
- data/README.md +6 -4
- data/gemfiles/with_external_deps.gemfile +2 -0
- data/gemfiles/with_external_deps.gemfile.lock +84 -99
- data/gemfiles/without_external_deps.gemfile.lock +78 -95
- data/lib/qonfig/commands/definition/add_nested_option.rb +5 -1
- data/lib/qonfig/commands/definition/load_from_env/value_converter.rb +6 -6
- data/lib/qonfig/commands/definition/re_define_option.rb +5 -1
- data/lib/qonfig/commands/instantiation/values_file.rb +4 -6
- data/lib/qonfig/imports/direct_key.rb +4 -4
- data/lib/qonfig/imports/mappings.rb +4 -4
- data/lib/qonfig/plugins/pretty_print/requirements.rb +1 -1
- data/lib/qonfig/plugins/toml/loaders/dynamic.rb +1 -1
- data/lib/qonfig/plugins/toml/loaders.rb +1 -1
- data/lib/qonfig/plugins.rb +2 -4
- data/lib/qonfig/settings/callbacks.rb +9 -0
- data/lib/qonfig/settings.rb +104 -18
- data/lib/qonfig/uploaders/yaml.rb +1 -1
- data/lib/qonfig/validation/building/instance_builder/attribute_consistency.rb +2 -2
- data/lib/qonfig/version.rb +1 -1
- data/lib/qonfig.rb +1 -0
- data/qonfig.gemspec +8 -8
- data/spec/features/config_definition_and_representation_spec.rb +7 -7
- data/spec/features/freeze_state_spec.rb +10 -10
- data/spec/features/nested_access_caching_spec.rb +134 -0
- data/spec/features/plugins/pretty_print_spec.rb +15 -15
- data/spec/features/plugins/toml/save_to_toml_spec.rb +1 -1
- data/spec/features/plugins/vault/expose_vault_spec.rb +1 -1
- data/spec/features/run_code_with_temporary_settings_spec.rb +70 -68
- data/spec/features/save_to_file/save_to_json_spec.rb +38 -18
- metadata +37 -49
- data/.github/workflows/test.yml +0 -62
- data/.jrubyrc +0 -1
- data/sig/.keep +0 -0
data/qonfig.gemspec
CHANGED
|
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
|
6
6
|
require 'qonfig/version'
|
|
7
7
|
|
|
8
8
|
Gem::Specification.new do |spec|
|
|
9
|
-
spec.required_ruby_version = '>=
|
|
9
|
+
spec.required_ruby_version = '>= 3.1'
|
|
10
10
|
|
|
11
11
|
spec.name = 'qonfig'
|
|
12
12
|
spec.version = Qonfig::VERSION
|
|
@@ -27,14 +27,14 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
|
|
28
28
|
spec.files = `git ls-files -z`.split("\x0")
|
|
29
29
|
|
|
30
|
-
spec.
|
|
31
|
-
|
|
32
|
-
spec.add_development_dependency '
|
|
33
|
-
spec.add_development_dependency '
|
|
30
|
+
spec.add_dependency 'base64', '>= 0.2'
|
|
31
|
+
|
|
32
|
+
spec.add_development_dependency 'simplecov', '~> 0.22'
|
|
33
|
+
spec.add_development_dependency 'rspec', '~> 3.2'
|
|
34
|
+
spec.add_development_dependency 'armitage-rubocop', '~> 1.59'
|
|
35
|
+
spec.add_development_dependency 'activesupport', '~> 6', '< 8'
|
|
34
36
|
spec.add_development_dependency 'bundler', '>= 1'
|
|
35
|
-
spec.add_development_dependency '
|
|
36
|
-
spec.add_development_dependency 'ci-helper', '~> 0.5'
|
|
37
|
-
spec.add_development_dependency 'pry', '~> 0.14'
|
|
37
|
+
spec.add_development_dependency 'pry', '~> 0.15'
|
|
38
38
|
spec.add_development_dependency 'rake', '>= 13'
|
|
39
39
|
spec.add_development_dependency 'simplecov-lcov', '~> 0.8'
|
|
40
40
|
end
|
|
@@ -76,7 +76,7 @@ describe 'Config definition and representation' do
|
|
|
76
76
|
)
|
|
77
77
|
|
|
78
78
|
# hash representation (with key transformation)
|
|
79
|
-
expect(config.to_h(key_transformer: proc
|
|
79
|
+
expect(config.to_h(key_transformer: proc(&:to_sym))).to match(
|
|
80
80
|
serializers: {
|
|
81
81
|
json: :native,
|
|
82
82
|
xml: :native
|
|
@@ -94,7 +94,7 @@ describe 'Config definition and representation' do
|
|
|
94
94
|
)
|
|
95
95
|
|
|
96
96
|
# hash representation (with value transformation)
|
|
97
|
-
expect(config.to_h(value_transformer: proc
|
|
97
|
+
expect(config.to_h(value_transformer: proc(&:to_s))).to match(
|
|
98
98
|
'serializers' => {
|
|
99
99
|
'json' => 'native',
|
|
100
100
|
'xml' => 'native'
|
|
@@ -114,8 +114,8 @@ describe 'Config definition and representation' do
|
|
|
114
114
|
# hash representation (with key and value transformations)
|
|
115
115
|
expect(
|
|
116
116
|
config.to_h(
|
|
117
|
-
key_transformer: proc
|
|
118
|
-
value_transformer: proc
|
|
117
|
+
key_transformer: proc(&:to_sym),
|
|
118
|
+
value_transformer: proc(&:to_s)
|
|
119
119
|
)
|
|
120
120
|
).to match(
|
|
121
121
|
serializers: {
|
|
@@ -381,7 +381,7 @@ describe 'Config definition and representation' do
|
|
|
381
381
|
end.to raise_error(Qonfig::AmbiguousSettingValueError)
|
|
382
382
|
|
|
383
383
|
# attempt to use non-hash object
|
|
384
|
-
[1, 1.0, Object.new, true, false, Class.new, Module.new,
|
|
384
|
+
[1, 1.0, Object.new, true, false, Class.new, Module.new, proc {}, -> {}].each do |non_hash|
|
|
385
385
|
expect do
|
|
386
386
|
# without proc
|
|
387
387
|
HashConfigurableConfig.new(non_hash)
|
|
@@ -435,7 +435,7 @@ describe 'Config definition and representation' do
|
|
|
435
435
|
end
|
|
436
436
|
|
|
437
437
|
specify 'only string and symbol keys are supported' do
|
|
438
|
-
[1, 1.0, Object.new, true, false, Class.new, Module.new,
|
|
438
|
+
[1, 1.0, Object.new, true, false, Class.new, Module.new, proc {}, -> {}].each do |key|
|
|
439
439
|
expect do
|
|
440
440
|
Class.new(Qonfig::DataSet) { setting(key) }
|
|
441
441
|
end.to raise_error(Qonfig::ArgumentError)
|
|
@@ -477,7 +477,7 @@ describe 'Config definition and representation' do
|
|
|
477
477
|
end
|
|
478
478
|
|
|
479
479
|
specify 'fails when tries to use a non-string/non-symbol value as a setting key' do
|
|
480
|
-
incorrect_key_values = [123, Object.new, 15.1,
|
|
480
|
+
incorrect_key_values = [123, Object.new, 15.1, proc {}, Class.new, true, false]
|
|
481
481
|
correct_key_values = ['test', :test]
|
|
482
482
|
|
|
483
483
|
incorrect_key_values.each do |incorrect_key|
|
|
@@ -36,13 +36,13 @@ describe 'Freeze state' do
|
|
|
36
36
|
expect { conf.additionals = true }.to raise_error(Qonfig::FrozenSettingsError)
|
|
37
37
|
|
|
38
38
|
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
|
|
39
|
-
expect { conf.api_mode_enabled = false }.to raise_error(
|
|
40
|
-
expect { conf.api.format = :xml }.to raise_error(
|
|
41
|
-
expect { conf.additionals = true }.to raise_error(
|
|
39
|
+
expect { conf.api_mode_enabled = false }.to raise_error(FrozenError)
|
|
40
|
+
expect { conf.api.format = :xml }.to raise_error(FrozenError)
|
|
41
|
+
expect { conf.additionals = true }.to raise_error(FrozenError)
|
|
42
42
|
else
|
|
43
|
-
expect { conf.api_mode_enabled = false }.to raise_error(
|
|
44
|
-
expect { conf.api.format = :xml }.to raise_error(
|
|
45
|
-
expect { conf.additionals = true }.to raise_error(
|
|
43
|
+
expect { conf.api_mode_enabled = false }.to raise_error(RuntimeError)
|
|
44
|
+
expect { conf.api.format = :xml }.to raise_error(RuntimeError)
|
|
45
|
+
expect { conf.additionals = true }.to raise_error(RuntimeError)
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -55,11 +55,11 @@ describe 'Freeze state' do
|
|
|
55
55
|
expect { frozen_config.clear! }.to raise_error(Qonfig::FrozenSettingsError)
|
|
56
56
|
|
|
57
57
|
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.5.0')
|
|
58
|
-
expect { frozen_config.reload! }.to raise_error(
|
|
59
|
-
expect { frozen_config.clear! }.to raise_error(
|
|
58
|
+
expect { frozen_config.reload! }.to raise_error(FrozenError)
|
|
59
|
+
expect { frozen_config.clear! }.to raise_error(FrozenError)
|
|
60
60
|
else
|
|
61
|
-
expect { frozen_config.reload! }.to raise_error(
|
|
62
|
-
expect { frozen_config.clear! }.to raise_error(
|
|
61
|
+
expect { frozen_config.reload! }.to raise_error(RuntimeError)
|
|
62
|
+
expect { frozen_config.clear! }.to raise_error(RuntimeError)
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
expect(frozen_config.to_h).to match(
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe 'Nested access caching (with matryoshka cache invalidation)' do
|
|
4
|
+
let(:config) do
|
|
5
|
+
Qonfig::DataSet.build do
|
|
6
|
+
setting :a do
|
|
7
|
+
setting :b do
|
|
8
|
+
setting :c do
|
|
9
|
+
setting :d, 1
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
setting :flat, 'value'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
specify 'repeated dot-notated / nested reads return the same (memoized) result' do
|
|
19
|
+
expect(config['a.b.c.d']).to eq(1)
|
|
20
|
+
expect(config['a.b.c.d']).to eq(1)
|
|
21
|
+
|
|
22
|
+
nested = config['a.b.c']
|
|
23
|
+
expect(nested).to be_a(Qonfig::Settings)
|
|
24
|
+
# the very same nested settings instance is returned from the cache
|
|
25
|
+
expect(config['a.b.c']).to equal(nested)
|
|
26
|
+
expect(config.dig(:a, :b, :c)).to equal(nested)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
specify 'symbol and string key variants share the cache (indifferent access)' do
|
|
30
|
+
expect(config[:a][:b][:c][:d]).to eq(1)
|
|
31
|
+
expect(config['a.b.c.d']).to eq(1)
|
|
32
|
+
expect(config.dig('a', :b, 'c', :d)).to eq(1)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
specify 'assigning a value invalidates the cache' do
|
|
36
|
+
expect(config['a.b.c.d']).to eq(1)
|
|
37
|
+
|
|
38
|
+
config['a.b.c.d'] = 2
|
|
39
|
+
expect(config['a.b.c.d']).to eq(2)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
specify 'mutating a nested config instance invalidates every parent cache (matryoshka)' do
|
|
43
|
+
# warm up caches on every level
|
|
44
|
+
expect(config['a.b.c.d']).to eq(1)
|
|
45
|
+
expect(config['a.b.c']['d']).to eq(1)
|
|
46
|
+
|
|
47
|
+
# mutate through a directly-grabbed nested instance
|
|
48
|
+
nested = config['a.b.c']
|
|
49
|
+
nested['d'] = 99
|
|
50
|
+
|
|
51
|
+
# the mutation must be visible from the root through the (now stale) cache
|
|
52
|
+
expect(config['a.b.c.d']).to eq(99)
|
|
53
|
+
expect(config['a.b']['c.d']).to eq(99)
|
|
54
|
+
expect(nested['d']).to eq(99)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
specify 'a flat value mutation invalidates the cache' do
|
|
58
|
+
expect(config['flat']).to eq('value')
|
|
59
|
+
config['flat'] = 'changed'
|
|
60
|
+
expect(config['flat']).to eq('changed')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
specify '#clear! invalidates the cache' do
|
|
64
|
+
expect(config['a.b.c.d']).to eq(1)
|
|
65
|
+
config.clear!
|
|
66
|
+
expect(config['a.b.c.d']).to be_nil
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
specify 'full data-set validators observe fresh values after mutation (no stale cache)' do
|
|
70
|
+
validated_config = Class.new(Qonfig::DataSet) do
|
|
71
|
+
setting :nested do
|
|
72
|
+
setting :flag, :yes
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
validate { settings.nested.flag.is_a?(Symbol) }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
instance = validated_config.new
|
|
79
|
+
# warm the cache through the validator-read path
|
|
80
|
+
expect(instance.settings.nested.flag).to eq(:yes)
|
|
81
|
+
|
|
82
|
+
expect { instance.settings.nested.flag = 123 }.to raise_error(Qonfig::ValidationError)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
specify 'composed nested settings invalidate the host cache on mutation' do
|
|
86
|
+
shared = Class.new(Qonfig::DataSet) do
|
|
87
|
+
setting :s do
|
|
88
|
+
setting :v, 1
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
host = Qonfig::DataSet.build { compose shared }
|
|
93
|
+
|
|
94
|
+
expect(host['s.v']).to eq(1) # warm up the cache through the host
|
|
95
|
+
host['s.v'] = 88
|
|
96
|
+
# the nested instance was merged in "by reference"; mutating it must still
|
|
97
|
+
# cascade invalidation up to the host (otherwise the host serves a stale 1)
|
|
98
|
+
expect(host['s.v']).to eq(88)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
specify 're-defined nested settings invalidate the cache on mutation' do
|
|
102
|
+
redefined_config = Class.new(Qonfig::DataSet) do
|
|
103
|
+
setting :s do
|
|
104
|
+
setting :v, 1
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
re_setting :s do
|
|
108
|
+
setting :v, 10
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
instance = redefined_config.new
|
|
113
|
+
|
|
114
|
+
expect(instance['s.v']).to eq(10) # warm up the cache
|
|
115
|
+
instance['s.v'] = 77
|
|
116
|
+
expect(instance['s.v']).to eq(77)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
specify 'cache invalidation always swaps in a fresh hash, even when already empty' do
|
|
120
|
+
# Guards the lock-free invalidation contract: invalidation may propagate up
|
|
121
|
+
# from a nested mutation without holding our access lock, so the swap must be
|
|
122
|
+
# unconditional. A `unless empty?` guard would let a concurrent reader that has
|
|
123
|
+
# resolved a stale value (but not yet written it) persist that value forever.
|
|
124
|
+
settings = config.settings
|
|
125
|
+
empty_cache = settings.instance_variable_get(:@__deep_access_cache__)
|
|
126
|
+
expect(empty_cache).to be_empty
|
|
127
|
+
|
|
128
|
+
settings.send(:__invalidate_deep_access_cache__)
|
|
129
|
+
|
|
130
|
+
swapped_cache = settings.instance_variable_get(:@__deep_access_cache__)
|
|
131
|
+
expect(swapped_cache).to be_empty
|
|
132
|
+
expect(swapped_cache).not_to equal(empty_cache)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -35,13 +35,13 @@ describe 'Plugins(:pretty_print): Pretty print :)', plugin: :pretty_print do
|
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
expect(pretty_printer_output.string).to include(
|
|
38
|
-
" api.domain: \"google\",\n" \
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
38
|
+
" api.domain: \"google\",\n " \
|
|
39
|
+
"api.creds.token: \"a0sdj10k@\",\n " \
|
|
40
|
+
"api.creds.login: \"D2\",\n " \
|
|
41
|
+
"database.adapter: \"pg\",\n " \
|
|
42
|
+
"database.logging.queries: nil,\n " \
|
|
43
|
+
"logging: false,\n " \
|
|
44
|
+
"author: nil>\n"
|
|
45
45
|
)
|
|
46
46
|
end
|
|
47
47
|
else
|
|
@@ -50,14 +50,14 @@ describe 'Plugins(:pretty_print): Pretty print :)', plugin: :pretty_print do
|
|
|
50
50
|
value_space = SpecSupport.from_object_id_space_to_value_space(config)
|
|
51
51
|
|
|
52
52
|
expect(pretty_printer_output.string).to eq(
|
|
53
|
-
"#<#{expected_config_klass_name}:0x#{value_space}\n" \
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
53
|
+
"#<#{expected_config_klass_name}:0x#{value_space}\n " \
|
|
54
|
+
"api.domain: \"google\",\n " \
|
|
55
|
+
"api.creds.token: \"a0sdj10k@\",\n " \
|
|
56
|
+
"api.creds.login: \"D2\",\n " \
|
|
57
|
+
"database.adapter: \"pg\",\n " \
|
|
58
|
+
"database.logging.queries: nil,\n " \
|
|
59
|
+
"logging: false,\n " \
|
|
60
|
+
"author: nil>\n"
|
|
61
61
|
)
|
|
62
62
|
end
|
|
63
63
|
end
|
|
@@ -142,7 +142,7 @@ describe 'Plugins(toml): Save to .toml (TOML)', plugin: :toml do
|
|
|
142
142
|
end
|
|
143
143
|
|
|
144
144
|
specify 'fails with parser error' do
|
|
145
|
-
expect { config.save_to_toml(path: config_file_path) }.to raise_error(
|
|
145
|
+
expect { config.save_to_toml(path: config_file_path) }.to raise_error(TomlRB::ParseError)
|
|
146
146
|
end
|
|
147
147
|
end
|
|
148
148
|
end
|
|
@@ -104,7 +104,7 @@ describe 'Plugins(vault): expose vault', plugin: :vault do
|
|
|
104
104
|
end
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
-
let(:secret_data) {
|
|
107
|
+
let(:secret_data) { {} }
|
|
108
108
|
|
|
109
109
|
specify "doesn't fail and uses empty config" do
|
|
110
110
|
expect(Vault.logical).to receive(:read).with('kv/data/env_key').and_return(returned_data)
|
|
@@ -22,83 +22,85 @@ describe 'Run code with temporary settings' do
|
|
|
22
22
|
expect(config.settings.api.login).to eq('D@iVeR')
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
setting :
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
unless RUBY_ENGINE.include?('truffleruby')
|
|
26
|
+
specify 'thread-safety' do
|
|
27
|
+
config = Qonfig::DataSet.build do
|
|
28
|
+
setting :api do
|
|
29
|
+
setting :token, 'test123'
|
|
30
|
+
setting :login, 'D@iVeR'
|
|
31
|
+
end
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
setting :credentials do
|
|
34
|
+
setting :user, 'admin'
|
|
35
|
+
setting :password, '1234asdf'
|
|
36
|
+
end
|
|
35
37
|
end
|
|
36
|
-
end
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
ThreadGroup.new.tap do |thread_group|
|
|
40
|
+
10.times do
|
|
41
|
+
thread_group.add(Thread.new do
|
|
42
|
+
# NOTE: change settings temporary
|
|
43
|
+
config.with(api: { token: '777555' }, credentials: { password: 'test123' }) do
|
|
44
|
+
config.settings.credentials.user = 'nimda'
|
|
45
|
+
end
|
|
46
|
+
end)
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
thread_group.add(Thread.new do
|
|
49
|
+
# NOTE: settings are not changed :)
|
|
50
|
+
expect(config.settings.credentials.user).to eq('admin')
|
|
51
|
+
expect(config.settings.credentials.password).to eq('1234asdf')
|
|
52
|
+
expect(config.settings.api.token).to eq('test123')
|
|
53
|
+
expect(config.settings.api.login).to eq('D@iVeR')
|
|
54
|
+
end)
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
thread_group.add(Thread.new do
|
|
57
|
+
# NOTE: change settings temporary
|
|
58
|
+
config.with do
|
|
59
|
+
config.settings.api.login = 'provider'
|
|
60
|
+
config.settings.api.token = 'super_puper_123'
|
|
61
|
+
end
|
|
62
|
+
end)
|
|
62
63
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
thread_group.add(Thread.new do
|
|
65
|
+
# NOTE: settings are not changed :)
|
|
66
|
+
expect(config.settings.credentials.user).to eq('admin')
|
|
67
|
+
expect(config.settings.credentials.password).to eq('1234asdf')
|
|
68
|
+
expect(config.settings.api.token).to eq('test123')
|
|
69
|
+
expect(config.settings.api.login).to eq('D@iVeR')
|
|
70
|
+
end)
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
thread_group.add(Thread.new do
|
|
73
|
+
config.with(api: { login: '0exp' }, credentials: { user: 'D@iVeR' }) do
|
|
74
|
+
config.settings.api.token = 'kekpek123'
|
|
75
|
+
config.settings.credentials.password = 'admin'
|
|
76
|
+
end
|
|
77
|
+
end)
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
thread_group.add(Thread.new do
|
|
80
|
+
# NOTE: settings are not changed :)
|
|
81
|
+
expect(config.settings.credentials.user).to eq('admin')
|
|
82
|
+
expect(config.settings.credentials.password).to eq('1234asdf')
|
|
83
|
+
expect(config.settings.api.token).to eq('test123')
|
|
84
|
+
expect(config.settings.api.login).to eq('D@iVeR')
|
|
85
|
+
end)
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
thread_group.add(Thread.new do
|
|
88
|
+
# NOTE: change settings temporary
|
|
89
|
+
config.with(api: { login: 'mobile_legends' }, credentials: { user: 'dota2' }) do
|
|
90
|
+
config.settings.api.token = 'league_of_legends'
|
|
91
|
+
config.settings.credentials.password = 'overwatch'
|
|
92
|
+
end
|
|
93
|
+
end)
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
95
|
+
thread_group.add(Thread.new do
|
|
96
|
+
# NOTE: settings are not changed :)
|
|
97
|
+
expect(config.settings.credentials.user).to eq('admin')
|
|
98
|
+
expect(config.settings.credentials.password).to eq('1234asdf')
|
|
99
|
+
expect(config.settings.api.token).to eq('test123')
|
|
100
|
+
expect(config.settings.api.login).to eq('D@iVeR')
|
|
101
|
+
end)
|
|
102
|
+
end
|
|
103
|
+
end.list.map(&:join)
|
|
104
|
+
end
|
|
103
105
|
end
|
|
104
106
|
end
|
|
@@ -108,24 +108,44 @@ describe 'Save to .json (JSON)' do
|
|
|
108
108
|
# NOTE: step 2) read saved file
|
|
109
109
|
file_data = File.read(config_file_path)
|
|
110
110
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
111
|
+
expected_json =
|
|
112
|
+
if RUBY_ENGINE.include?('truffleruby')
|
|
113
|
+
<<~JSON.strip
|
|
114
|
+
{
|
|
115
|
+
"true_bollean": true,
|
|
116
|
+
"false_boolean": false,
|
|
117
|
+
"empty_object": {},
|
|
118
|
+
"filled_object": {
|
|
119
|
+
"a": 1,
|
|
120
|
+
"b": null,
|
|
121
|
+
"c": true,
|
|
122
|
+
"d": "1",
|
|
123
|
+
"e": false
|
|
124
|
+
},
|
|
125
|
+
"null_data": null,
|
|
126
|
+
"collection": ["1",2,true,false,null,[],{}]
|
|
127
|
+
}
|
|
128
|
+
JSON
|
|
129
|
+
else
|
|
130
|
+
<<~JSON.strip
|
|
131
|
+
{
|
|
132
|
+
"true_bollean": true,
|
|
133
|
+
"false_boolean": false,
|
|
134
|
+
"empty_object": {},
|
|
135
|
+
"filled_object": {
|
|
136
|
+
"a": 1,
|
|
137
|
+
"b": null,
|
|
138
|
+
"c": true,
|
|
139
|
+
"d": "1",
|
|
140
|
+
"e": false
|
|
141
|
+
},
|
|
142
|
+
"null_data": null,
|
|
143
|
+
"collection": [ "1", 2, true, false, null, [], {}]
|
|
144
|
+
}
|
|
145
|
+
JSON
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
expect(file_data).to eq(expected_json)
|
|
129
149
|
end
|
|
130
150
|
end
|
|
131
151
|
|