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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +50 -0
  3. data/.rubocop.yml +1 -1
  4. data/CHANGELOG.md +22 -0
  5. data/Gemfile +0 -3
  6. data/Gemfile.lock +77 -98
  7. data/LICENSE.txt +1 -1
  8. data/README.md +6 -4
  9. data/gemfiles/with_external_deps.gemfile +2 -0
  10. data/gemfiles/with_external_deps.gemfile.lock +84 -99
  11. data/gemfiles/without_external_deps.gemfile.lock +78 -95
  12. data/lib/qonfig/commands/definition/add_nested_option.rb +5 -1
  13. data/lib/qonfig/commands/definition/load_from_env/value_converter.rb +6 -6
  14. data/lib/qonfig/commands/definition/re_define_option.rb +5 -1
  15. data/lib/qonfig/commands/instantiation/values_file.rb +4 -6
  16. data/lib/qonfig/imports/direct_key.rb +4 -4
  17. data/lib/qonfig/imports/mappings.rb +4 -4
  18. data/lib/qonfig/plugins/pretty_print/requirements.rb +1 -1
  19. data/lib/qonfig/plugins/toml/loaders/dynamic.rb +1 -1
  20. data/lib/qonfig/plugins/toml/loaders.rb +1 -1
  21. data/lib/qonfig/plugins.rb +2 -4
  22. data/lib/qonfig/settings/callbacks.rb +9 -0
  23. data/lib/qonfig/settings.rb +104 -18
  24. data/lib/qonfig/uploaders/yaml.rb +1 -1
  25. data/lib/qonfig/validation/building/instance_builder/attribute_consistency.rb +2 -2
  26. data/lib/qonfig/version.rb +1 -1
  27. data/lib/qonfig.rb +1 -0
  28. data/qonfig.gemspec +8 -8
  29. data/spec/features/config_definition_and_representation_spec.rb +7 -7
  30. data/spec/features/freeze_state_spec.rb +10 -10
  31. data/spec/features/nested_access_caching_spec.rb +134 -0
  32. data/spec/features/plugins/pretty_print_spec.rb +15 -15
  33. data/spec/features/plugins/toml/save_to_toml_spec.rb +1 -1
  34. data/spec/features/plugins/vault/expose_vault_spec.rb +1 -1
  35. data/spec/features/run_code_with_temporary_settings_spec.rb +70 -68
  36. data/spec/features/save_to_file/save_to_json_spec.rb +38 -18
  37. metadata +37 -49
  38. data/.github/workflows/test.yml +0 -62
  39. data/.jrubyrc +0 -1
  40. 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 = '>= 2.6.0'
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.add_development_dependency 'simplecov', '~> 0.21'
31
- spec.add_development_dependency 'rspec', '~> 3.11'
32
- spec.add_development_dependency 'armitage-rubocop', '~> 1.30'
33
- spec.add_development_dependency 'steep', '~> 1.0'
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 'bundler-audit', '~> 0.9'
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 { |value| value.to_sym })).to match(
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 { |value| value.to_s })).to match(
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 { |value| value.to_sym },
118
- value_transformer: proc { |value| value.to_s }
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, (proc {}), (-> {})].each do |non_hash|
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, (proc {}), (-> {})].each do |key|
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, (proc {}), Class.new, true, false]
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(::FrozenError)
40
- expect { conf.api.format = :xml }.to raise_error(::FrozenError)
41
- expect { conf.additionals = true }.to raise_error(::FrozenError)
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(::RuntimeError)
44
- expect { conf.api.format = :xml }.to raise_error(::RuntimeError)
45
- expect { conf.additionals = true }.to raise_error(::RuntimeError)
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(::FrozenError)
59
- expect { frozen_config.clear! }.to raise_error(::FrozenError)
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(::RuntimeError)
62
- expect { frozen_config.clear! }.to raise_error(::RuntimeError)
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
- " 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"
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
- " 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"
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(::TomlRB::ParseError)
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) { Hash[] }
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
- specify 'thread-safety' do
26
- config = Qonfig::DataSet.build do
27
- setting :api do
28
- setting :token, 'test123'
29
- setting :login, 'D@iVeR'
30
- end
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
- setting :credentials do
33
- setting :user, 'admin'
34
- setting :password, '1234asdf'
33
+ setting :credentials do
34
+ setting :user, 'admin'
35
+ setting :password, '1234asdf'
36
+ end
35
37
  end
36
- end
37
38
 
38
- ThreadGroup.new.tap do |thread_group|
39
- 10.times do
40
- thread_group.add(Thread.new do
41
- # NOTE: change settings temporary
42
- config.with(api: { token: '777555' }, credentials: { password: 'test123' }) do
43
- config.settings.credentials.user = 'nimda'
44
- end
45
- end)
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
- thread_group.add(Thread.new do
48
- # NOTE: settings are not changed :)
49
- expect(config.settings.credentials.user).to eq('admin')
50
- expect(config.settings.credentials.password).to eq('1234asdf')
51
- expect(config.settings.api.token).to eq('test123')
52
- expect(config.settings.api.login).to eq('D@iVeR')
53
- end)
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
- thread_group.add(Thread.new do
56
- # NOTE: change settings temporary
57
- config.with do
58
- config.settings.api.login = 'provider'
59
- config.settings.api.token = 'super_puper_123'
60
- end
61
- end)
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
- thread_group.add(Thread.new do
64
- # NOTE: settings are not changed :)
65
- expect(config.settings.credentials.user).to eq('admin')
66
- expect(config.settings.credentials.password).to eq('1234asdf')
67
- expect(config.settings.api.token).to eq('test123')
68
- expect(config.settings.api.login).to eq('D@iVeR')
69
- end)
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
- thread_group.add(Thread.new do
72
- config.with(api: { login: '0exp' }, credentials: { user: 'D@iVeR' }) do
73
- config.settings.api.token = 'kekpek123'
74
- config.settings.credentials.password = 'admin'
75
- end
76
- end)
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
- thread_group.add(Thread.new do
79
- # NOTE: settings are not changed :)
80
- expect(config.settings.credentials.user).to eq('admin')
81
- expect(config.settings.credentials.password).to eq('1234asdf')
82
- expect(config.settings.api.token).to eq('test123')
83
- expect(config.settings.api.login).to eq('D@iVeR')
84
- end)
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
- thread_group.add(Thread.new do
87
- # NOTE: change settings temporary
88
- config.with(api: { login: 'mobile_legends' }, credentials: { user: 'dota2' }) do
89
- config.settings.api.token = 'league_of_legends'
90
- config.settings.credentials.password = 'overwatch'
91
- end
92
- end)
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
- thread_group.add(Thread.new do
95
- # NOTE: settings are not changed :)
96
- expect(config.settings.credentials.user).to eq('admin')
97
- expect(config.settings.credentials.password).to eq('1234asdf')
98
- expect(config.settings.api.token).to eq('test123')
99
- expect(config.settings.api.login).to eq('D@iVeR')
100
- end)
101
- end
102
- end.list.map(&:join)
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
- expect(file_data).to eq(<<~JSON.strip)
112
- {
113
- "true_bollean": true,
114
- "false_boolean": false,
115
- "empty_object": {
116
- },
117
- "filled_object": {
118
- "a": 1,
119
- "b": null,
120
- "c": true,
121
- "d": "1",
122
- "e": false
123
- },
124
- "null_data": null,
125
- "collection": [ "1", 2, true, false, null, [], {
126
- }]
127
- }
128
- JSON
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