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.
@@ -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
@@ -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