const_conf 0.0.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 +7 -0
- data/Gemfile +5 -0
- data/LICENSE +19 -0
- data/README.md +789 -0
- data/Rakefile +35 -0
- data/const_conf.gemspec +35 -0
- data/lib/const_conf/dir_plugin.rb +175 -0
- data/lib/const_conf/env_dir_extension.rb +83 -0
- data/lib/const_conf/errors.rb +93 -0
- data/lib/const_conf/file_plugin.rb +33 -0
- data/lib/const_conf/json_plugin.rb +12 -0
- data/lib/const_conf/railtie.rb +13 -0
- data/lib/const_conf/setting.rb +382 -0
- data/lib/const_conf/setting_accessor.rb +103 -0
- data/lib/const_conf/tree.rb +265 -0
- data/lib/const_conf/version.rb +8 -0
- data/lib/const_conf/yaml_plugin.rb +30 -0
- data/lib/const_conf.rb +401 -0
- data/spec/assets/.env/API_KEY +1 -0
- data/spec/assets/config.json +3 -0
- data/spec/assets/config.yml +1 -0
- data/spec/assets/config_env.yml +7 -0
- data/spec/const_conf/dir_plugin_spec.rb +249 -0
- data/spec/const_conf/env_dir_extension_spec.rb +59 -0
- data/spec/const_conf/file_plugin_spec.rb +173 -0
- data/spec/const_conf/json_plugin_spec.rb +64 -0
- data/spec/const_conf/setting_accessor_spec.rb +114 -0
- data/spec/const_conf/setting_spec.rb +628 -0
- data/spec/const_conf/tree_spec.rb +185 -0
- data/spec/const_conf/yaml_plugin_spec.rb +84 -0
- data/spec/const_conf_spec.rb +216 -0
- data/spec/spec_helper.rb +140 -0
- metadata +240 -0
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ConstConf::Tree do
|
4
|
+
module ModuleWithSettings
|
5
|
+
include ConstConf
|
6
|
+
description 'Test Configuration'
|
7
|
+
prefix 'TEST'
|
8
|
+
|
9
|
+
TEST_VAR = set do
|
10
|
+
description 'A test variable'
|
11
|
+
prefix '' # Remove the superflous TEST prefix for env var TEST_VAR
|
12
|
+
default 'default_value'
|
13
|
+
required true
|
14
|
+
end
|
15
|
+
|
16
|
+
NESTED_CONFIG = set do
|
17
|
+
# prefix is inherited from ModuleWithSettings's 'TEST', make this env var
|
18
|
+
# TEST_NESTED_CONFIG
|
19
|
+
description 'Nested configuration'
|
20
|
+
default 'nested_default'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '.from_const_conf' do
|
25
|
+
context 'with a ConstConf module' do
|
26
|
+
it 'converts module to tree structure' do
|
27
|
+
tree = ConstConf::Tree.from_const_conf(ModuleWithSettings)
|
28
|
+
|
29
|
+
expect(tree).to be_a(ConstConf::Tree)
|
30
|
+
expect(tree.to_s).to include('Test Configuration')
|
31
|
+
expect(tree.to_s).to include('::TEST_VAR')
|
32
|
+
expect(tree.to_s).to include('TEST_NESTED_CONFIG')
|
33
|
+
expect(tree.to_s).to include('::NESTED_CONFIG')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'with a Setting object' do
|
38
|
+
let(:setting) do
|
39
|
+
ModuleWithSettings::TEST_VAR!
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'converts setting to tree structure' do
|
43
|
+
tree = ConstConf::Tree.from_const_conf(setting)
|
44
|
+
|
45
|
+
expect(tree).to be_a(ConstConf::Tree)
|
46
|
+
expect(tree.to_s).to include('TEST_VAR')
|
47
|
+
expect(tree.to_s).to include('A test variable')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'with invalid argument' do
|
52
|
+
it 'raises ArgumentError' do
|
53
|
+
expect {
|
54
|
+
ConstConf::Tree.from_const_conf(Object.new)
|
55
|
+
}.to raise_error(ArgumentError)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'Tree instance methods' do
|
61
|
+
let(:tree) do
|
62
|
+
ConstConf::Tree.new('Test Node')
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#initialize' do
|
66
|
+
it 'creates tree with name and value' do
|
67
|
+
tree = ConstConf::Tree.new('Test')
|
68
|
+
expect(tree.instance_variable_get(:@name)).to eq 'Test'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#<<' do
|
73
|
+
it 'adds child nodes' do
|
74
|
+
child = ConstConf::Tree.new('Child')
|
75
|
+
tree << child
|
76
|
+
|
77
|
+
expect(tree.instance_variable_get(:@children)).to include(child)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#to_enum' do
|
82
|
+
it 'yields tree structure with proper indentation' do
|
83
|
+
child1 = ConstConf::Tree.new('Child 1')
|
84
|
+
child2 = ConstConf::Tree.new('Child 2')
|
85
|
+
|
86
|
+
tree << child1
|
87
|
+
tree << child2
|
88
|
+
|
89
|
+
enum = tree.to_enum
|
90
|
+
result = enum.to_a
|
91
|
+
|
92
|
+
expect(result).to be_an(Array)
|
93
|
+
expect(result.size).to be >= 3 # root + children
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#to_s' do
|
98
|
+
it 'returns formatted string representation' do
|
99
|
+
tree << ConstConf::Tree.new('Child Node')
|
100
|
+
|
101
|
+
result = tree.to_s
|
102
|
+
expect(result).to be_a(String)
|
103
|
+
expect(result).not_to be_empty
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#default_utf8', protect_env: true do
|
108
|
+
context 'when LANG contains utf-8' do
|
109
|
+
before do
|
110
|
+
ENV['LANG'] = 'en_US.UTF-8'
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'returns true' do
|
114
|
+
expect(tree.default_utf8).to be true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'when LANG does not contain utf-8' do
|
119
|
+
before do
|
120
|
+
ENV['LANG'] = 'en_US.ISO8859-1'
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'returns false' do
|
124
|
+
expect(tree.default_utf8).to be false
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
module ParentModule
|
131
|
+
include ConstConf
|
132
|
+
description 'Parent Configuration'
|
133
|
+
prefix 'PARENT'
|
134
|
+
|
135
|
+
module NestedModule
|
136
|
+
include ConstConf
|
137
|
+
description 'Nested Config'
|
138
|
+
prefix 'NESTED'
|
139
|
+
|
140
|
+
NESTED_VAR = set do
|
141
|
+
description 'Nested variable'
|
142
|
+
default 'nested_value'
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
PARENT_VAR = set do
|
147
|
+
description 'Parent variable'
|
148
|
+
default 'parent_value'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe 'tree rendering with nested modules' do
|
153
|
+
it 'handles nested module structures' do
|
154
|
+
tree = ConstConf::Tree.from_const_conf(ParentModule)
|
155
|
+
|
156
|
+
# Should contain parent and nested module information
|
157
|
+
expect(tree.to_s).to include('Parent Configuration')
|
158
|
+
expect(tree.to_s).to include('Nested Config')
|
159
|
+
expect(tree.to_s).to include('::PARENT_VAR')
|
160
|
+
expect(tree.to_s).to include('PARENT_PARENT_VAR')
|
161
|
+
expect(tree.to_s).to include('::NESTED_VAR')
|
162
|
+
expect(tree.to_s).to include('NESTED_NESTED_VAR')
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe 'setting details rendering' do
|
167
|
+
let(:setting) do
|
168
|
+
ModuleWithSettings::TEST_VAR!
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'renders detailed setting information' do
|
172
|
+
tree = ConstConf::Tree.from_const_conf(setting)
|
173
|
+
|
174
|
+
# Should include various setting metadata
|
175
|
+
result = tree.to_s
|
176
|
+
|
177
|
+
expect(result).to include('TEST_VAR')
|
178
|
+
expect(result).to include('A test variable')
|
179
|
+
expect(result).to include('prefix')
|
180
|
+
expect(result).to include('env var name')
|
181
|
+
expect(result).to include('default')
|
182
|
+
expect(result).to include('value')
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ConstConf::YAMLPlugin do
|
4
|
+
let(:yaml_file) { asset('config.yml') }
|
5
|
+
let(:env_yaml_file) { asset('config_env.yml') }
|
6
|
+
|
7
|
+
describe '#yaml' do
|
8
|
+
context 'when file exists' do
|
9
|
+
it 'reads and parses YAML content as a hash' do
|
10
|
+
instance = double('Config').extend(described_class)
|
11
|
+
result = instance.yaml(yaml_file)
|
12
|
+
expect(result.hello).to eq 'world'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'handles environment-specific loading when env: true' do
|
16
|
+
instance = double('Config').extend(described_class)
|
17
|
+
ENV['RAILS_ENV'] = 'development'
|
18
|
+
|
19
|
+
# This will actually call complex_config_with_env with the real ENV value
|
20
|
+
result = instance.yaml(env_yaml_file, env: true)
|
21
|
+
expect(result.hello).to eq 'world'
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'handles environment-specific loading with explicit env' do
|
25
|
+
instance = double('Config').extend(described_class)
|
26
|
+
result = instance.yaml(env_yaml_file, env: 'production')
|
27
|
+
expect(result.hello).to eq 'outer world'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when file does not exist' do
|
32
|
+
let(:nonexistent_file) { asset('nonexistent.yml') }
|
33
|
+
|
34
|
+
it 'returns nil when file does not exist and required is false' do
|
35
|
+
instance = double('Config').extend(described_class)
|
36
|
+
result = instance.yaml(nonexistent_file, required: false)
|
37
|
+
expect(result).to be_nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'raises RequiredValueNotConfigured when file is required and does not exist' do
|
41
|
+
instance = double('Config').extend(described_class)
|
42
|
+
expect {
|
43
|
+
instance.yaml(nonexistent_file, required: true)
|
44
|
+
}.to raise_error(ConstConf::RequiredValueNotConfigured)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'when env is specified but no RAILS_ENV is set' do
|
49
|
+
it 'raises RequiredValueNotConfigured when env is true and RAILS_ENV is not set' do
|
50
|
+
instance = double('Config').extend(described_class)
|
51
|
+
ENV['RAILS_ENV'] = nil
|
52
|
+
|
53
|
+
expect {
|
54
|
+
instance.yaml(env_yaml_file, env: true)
|
55
|
+
}.to raise_error(ConstConf::RequiredValueNotConfigured)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'with custom configuration module' do
|
60
|
+
before do
|
61
|
+
eval %{
|
62
|
+
if Object.const_defined?(:ConstConfTestModuleWithYAML)
|
63
|
+
Object.send(:remove_const, :ConstConfTestModuleWithYAML)
|
64
|
+
end
|
65
|
+
module ::ConstConfTestModuleWithYAML
|
66
|
+
include ConstConf
|
67
|
+
plugin ConstConf::YAMLPlugin
|
68
|
+
|
69
|
+
description 'Module with YAMLPlugin'
|
70
|
+
|
71
|
+
CONFIG_VALUE = set do
|
72
|
+
description 'Configuration from YAML file'
|
73
|
+
default yaml("#{yaml_file}")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'can be used in a ConstConf module' do
|
80
|
+
expect(ConstConfTestModuleWithYAML::CONFIG_VALUE.hello).to eq 'world'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ConstConf do
|
4
|
+
before do
|
5
|
+
if Object.const_defined?(:TestConstConf)
|
6
|
+
Object.send(:remove_const, :TestConstConf)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'in ConstConf module' do
|
11
|
+
context 'reloading' do
|
12
|
+
before do
|
13
|
+
module TestConstConf
|
14
|
+
include ConstConf
|
15
|
+
|
16
|
+
description 'foo'
|
17
|
+
|
18
|
+
TEST = set do
|
19
|
+
description 'bar'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '.register' do
|
25
|
+
it 'registers configurations' do
|
26
|
+
expect(ConstConf.module_files).to include(TestConstConf => __FILE__)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '.destroy' do
|
31
|
+
it 'can be destroyed' do
|
32
|
+
expect(ConstConf.destroy).to include(__FILE__)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '.reload' do
|
37
|
+
it 'first destroys and then reloads' do
|
38
|
+
files = %w[ /path/to/foo.rb ]
|
39
|
+
expect(ConstConf).to receive(:destroy).and_return(files)
|
40
|
+
expect(ConstConf).to receive(:load).with(files.first)
|
41
|
+
ConstConf.reload
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'in including modules' do
|
47
|
+
describe '.plugin' do
|
48
|
+
before do
|
49
|
+
module TestConstConf
|
50
|
+
include ConstConf
|
51
|
+
|
52
|
+
plugin ConstConf::FilePlugin
|
53
|
+
|
54
|
+
description 'foo'
|
55
|
+
|
56
|
+
TEST = set do
|
57
|
+
description 'test'
|
58
|
+
default 'bar'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'can include FilePlugin' do
|
64
|
+
expect(ConstConf::Setting).to receive(:include).
|
65
|
+
with(ConstConf::FilePlugin).
|
66
|
+
and_call_original
|
67
|
+
TestConstConf.plugin(ConstConf::FilePlugin)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '.description' do
|
72
|
+
before do
|
73
|
+
module TestConstConf
|
74
|
+
include ConstConf
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'sets and retrieves module description' do
|
79
|
+
expect { TestConstConf.description "Test Configuration" }
|
80
|
+
.to change { TestConstConf.description }
|
81
|
+
.from(nil)
|
82
|
+
.to("Test Configuration")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'nested configurations' do
|
87
|
+
before do
|
88
|
+
module TestNestedConstConf
|
89
|
+
include ConstConf
|
90
|
+
|
91
|
+
description 'TestConstConf'
|
92
|
+
|
93
|
+
module InnerConstConf
|
94
|
+
description 'InnerConstConf'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '.outer_configuration' do
|
100
|
+
it 'knows about outer_configuration' do
|
101
|
+
expect(TestNestedConstConf::InnerConstConf.outer_configuration).
|
102
|
+
to eq TestNestedConstConf
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe '.all_configurations' do
|
107
|
+
it 'can return all configurations' do
|
108
|
+
expect(TestNestedConstConf.all_configurations).to eq(
|
109
|
+
[ TestNestedConstConf, TestNestedConstConf::InnerConstConf ]
|
110
|
+
)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe '.nested_configurations' do
|
115
|
+
it 'can return nested configurations' do
|
116
|
+
expect(TestNestedConstConf.nested_configurations).to eq(
|
117
|
+
[ TestNestedConstConf::InnerConstConf ]
|
118
|
+
)
|
119
|
+
end
|
120
|
+
|
121
|
+
describe '.each_nested_configuration' do
|
122
|
+
it 'can iterate over nested configurations' do
|
123
|
+
expect(TestNestedConstConf.each_nested_configuration).
|
124
|
+
to be_a Enumerator
|
125
|
+
expect(TestNestedConstConf.each_nested_configuration.to_a).
|
126
|
+
to eq([ TestNestedConstConf, TestNestedConstConf::InnerConstConf ])
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '.env_var_names' do
|
132
|
+
it 'returns the names environment variables' do
|
133
|
+
ENV['FOO'] = 'test_value'
|
134
|
+
module TestConstConf
|
135
|
+
include ConstConf
|
136
|
+
description 'foo foo'
|
137
|
+
FOO = set do
|
138
|
+
prefix ''
|
139
|
+
description 'foo'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
expect(TestConstConf.env_var_names).to eq %w[ FOO ]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '.env_vars' do
|
148
|
+
it 'returns a hash of environment variable values' do
|
149
|
+
ENV['FOO'] = 'test_value'
|
150
|
+
module TestConstConf
|
151
|
+
include ConstConf
|
152
|
+
|
153
|
+
description 'foo foo'
|
154
|
+
FOO = set do
|
155
|
+
prefix ''
|
156
|
+
description 'foo'
|
157
|
+
end
|
158
|
+
end
|
159
|
+
expect(TestConstConf.env_vars).to eq(
|
160
|
+
'FOO' => 'test_value'
|
161
|
+
)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
context 'with a setting' do
|
166
|
+
before do
|
167
|
+
module TestConstConf
|
168
|
+
include ConstConf
|
169
|
+
|
170
|
+
description 'foo'
|
171
|
+
|
172
|
+
TEST = set do
|
173
|
+
description 'test'
|
174
|
+
default 'bar'
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe '.prefix' do
|
180
|
+
it 'sets and retrieves prefix properly' do
|
181
|
+
expect { TestConstConf.prefix "FOO" }.to change { TestConstConf.prefix }.
|
182
|
+
from("TEST_CONST_CONF").to("FOO")
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'modifies settings after set' do
|
187
|
+
expect(TestConstConf.settings['TEST_CONST_CONF_TEST']).
|
188
|
+
to be_a(ConstConf::Setting)
|
189
|
+
end
|
190
|
+
|
191
|
+
describe '.setting_for' do
|
192
|
+
it 'can be returned' do
|
193
|
+
expect(TestConstConf.setting_for('TEST_CONST_CONF_TEST')).
|
194
|
+
to be_a ConstConf::Setting
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
describe '.setting_value_for' do
|
199
|
+
it 'has a value that be returned' do
|
200
|
+
expect(TestConstConf.setting_for('TEST_CONST_CONF_TEST').value).
|
201
|
+
to eq 'bar'
|
202
|
+
end
|
203
|
+
|
204
|
+
describe '.view' do
|
205
|
+
it 'renders tree view' do
|
206
|
+
expect(ConstConf::Tree).to receive(:from_const_conf).with(
|
207
|
+
TestConstConf
|
208
|
+
)
|
209
|
+
TestConstConf.view
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'gem_hadar/simplecov'
|
2
|
+
GemHadar::SimpleCov.start
|
3
|
+
require 'rspec'
|
4
|
+
begin
|
5
|
+
require 'debug'
|
6
|
+
rescue LoadError
|
7
|
+
end
|
8
|
+
require 'const_conf'
|
9
|
+
|
10
|
+
# A module that provides helper methods for asset management within the
|
11
|
+
# application.
|
12
|
+
#
|
13
|
+
# The AssetHelpers module encapsulates functionality related to handling and
|
14
|
+
# processing application assets, such as CSS, JavaScript, and image files. It
|
15
|
+
# offers utilities for managing asset paths, generating URLs, and performing
|
16
|
+
# operations on assets during the application's runtime.
|
17
|
+
module AssetHelpers
|
18
|
+
# The asset method constructs and returns the full path to an asset file.
|
19
|
+
#
|
20
|
+
# This method takes a filename argument and combines it with the assets directory
|
21
|
+
# located within the same directory as the calling file, returning the
|
22
|
+
# complete path to that asset.
|
23
|
+
#
|
24
|
+
# @param name [String] the name of the asset file
|
25
|
+
#
|
26
|
+
# @return [String] the full path to the asset file
|
27
|
+
def asset(name)
|
28
|
+
File.join(__dir__, 'assets', name)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Reads and returns the content of an asset file from the assets directory.
|
32
|
+
#
|
33
|
+
# @param name [String] the name of the asset file to read
|
34
|
+
#
|
35
|
+
# @return [String] the content of the asset file as a string
|
36
|
+
def asset_content(name)
|
37
|
+
File.read(File.join(__dir__, 'assets', name))
|
38
|
+
end
|
39
|
+
|
40
|
+
# The asset_io method retrieves an IO object for a specified asset file.
|
41
|
+
#
|
42
|
+
# This method constructs the path to an asset file within the assets directory
|
43
|
+
# and returns an IO object representing that file. If a block is provided, it
|
44
|
+
# yields the IO object to the block and ensures the file is properly closed
|
45
|
+
# after the block executes.
|
46
|
+
#
|
47
|
+
# @param name [ String ] the name of the asset file to retrieve
|
48
|
+
#
|
49
|
+
# @yield [ io ] yields the IO object for the asset file to the provided block
|
50
|
+
#
|
51
|
+
# @return [ File, nil ] returns the IO object for the asset file, or nil if a
|
52
|
+
# block is provided and the block does not return a value
|
53
|
+
def asset_io(name, &block)
|
54
|
+
io = File.new(File.join(__dir__, 'assets', name))
|
55
|
+
if block
|
56
|
+
begin
|
57
|
+
block.call(io)
|
58
|
+
ensure
|
59
|
+
io.close
|
60
|
+
end
|
61
|
+
else
|
62
|
+
io
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# The asset_json method reads and parses a JSON asset file.
|
67
|
+
#
|
68
|
+
# This method retrieves an asset by name, reads its contents from the
|
69
|
+
# filesystem, and then parses the resulting string as JSON, returning the
|
70
|
+
# parsed data structure.
|
71
|
+
#
|
72
|
+
# @param name [String] the name of the asset to retrieve and parse
|
73
|
+
#
|
74
|
+
# @return [Object] the parsed JSON data structure, typically a Hash or Array
|
75
|
+
def asset_json(name)
|
76
|
+
JSON(JSON(File.read(asset(name))))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# A module that provides functionality for stubbing Ollama server responses.
|
81
|
+
#
|
82
|
+
# The StubOllamaServer module enables developers to simulate Ollama API
|
83
|
+
# interactions in test environments by intercepting requests and returning
|
84
|
+
# predefined responses. This allows for faster, more reliable testing without
|
85
|
+
# requiring external service calls.
|
86
|
+
module StubOllamaServer
|
87
|
+
# The connect_to_ollama_server method establishes a connection to an Ollama
|
88
|
+
# server.
|
89
|
+
#
|
90
|
+
# This method sets up stubbed HTTP requests to simulate responses from an
|
91
|
+
# Ollama server, including API tags, show, and version endpoints. It can
|
92
|
+
# optionally instantiate a chat session after setting up the stubs.
|
93
|
+
#
|
94
|
+
# @param instantiate [Boolean] whether to instantiate a chat session after setting up stubs
|
95
|
+
def connect_to_ollama_server(instantiate: true)
|
96
|
+
before do
|
97
|
+
stub_request(:get, %r(/api/tags\z)).
|
98
|
+
to_return(status: 200, body: asset_json('api_tags.json'))
|
99
|
+
stub_request(:post, %r(/api/show\z)).
|
100
|
+
to_return(status: 200, body: asset_json('api_show.json'))
|
101
|
+
stub_request(:get, %r(/api/version\z)).
|
102
|
+
to_return(status: 200, body: asset_json('api_version.json'))
|
103
|
+
instantiate and chat
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# A module that provides functionality for protecting environment variables during tests.
|
109
|
+
#
|
110
|
+
# This module ensures that environment variable changes made during test execution
|
111
|
+
# are automatically restored to their original values after the test completes.
|
112
|
+
# It is designed to prevent side effects between tests that modify environment
|
113
|
+
# variables, maintaining a clean testing environment.
|
114
|
+
module ProtectEnvVars
|
115
|
+
# The apply method creates a lambda that protects environment variables
|
116
|
+
# during test execution.
|
117
|
+
#
|
118
|
+
# @return [Proc] a lambda that wraps test execution with environment variable
|
119
|
+
# preservation
|
120
|
+
def self.apply
|
121
|
+
-> example do
|
122
|
+
if example.metadata[:protect_env]
|
123
|
+
begin
|
124
|
+
stored_env = ENV.to_h
|
125
|
+
example.run
|
126
|
+
ensure
|
127
|
+
ENV.replace(stored_env)
|
128
|
+
end
|
129
|
+
else
|
130
|
+
example.run
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
RSpec.configure do |config|
|
137
|
+
config.include AssetHelpers
|
138
|
+
|
139
|
+
config.around(&ProtectEnvVars.apply)
|
140
|
+
end
|