ultra_settings 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +150 -67
- data/VERSION +1 -1
- data/app/application.css +4 -25
- data/app/configuration.html.erb +134 -0
- data/app/index.html.erb +1 -73
- data/lib/ultra_settings/configuration.rb +74 -5
- data/lib/ultra_settings/configuration_view.rb +91 -0
- data/lib/ultra_settings/field.rb +19 -4
- data/lib/ultra_settings/rack_app.rb +4 -0
- data/lib/ultra_settings.rb +9 -2
- data/ultra_settings.gemspec +7 -1
- metadata +13 -7
- /data/{MIT-LICENSE → MIT-LICENSE.txt} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6740e40d7198c0cb0573b24f5e61c0f679e8fb62b0c18bee3ee79a29748bc908
|
4
|
+
data.tar.gz: d35bb1603d4acfedcf32c8cc7c6063e0deeabb47e825d646ff104568a30de898
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f4e91264bd4466d0509f813a8b33391a15645ec939395055c353b1d0e162a6de0c89ea78f84139bdffeae3b61033652b58cb373d7cde47b4ee559923e2fe8a5
|
7
|
+
data.tar.gz: 341535915553d7d4d906c79850c4d42630c115ed030fae69b2e851042b999a654982ec7416a120195e65c0b436587b75e330259d675308facea3dedc5db59b98
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## 1.1.0
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Revamped web UI that can now display setting values.
|
12
|
+
- Added option to specify fields as a secret in the configuration to prevent exposing sensitive information in the web interface. By default all fields are considered secrets. This can be changed per configuration by setting the `fields_secret_by_default` property to `false`.
|
13
|
+
- Added `UltraSettings::ConfigurationView` which can be used to embed the HTML table showing the configuration options and values other admin views. So now you can integrate the settings view into your own admin tools.
|
14
|
+
- Add `__to_hash__` method to `UltraSettings::Configuration` which can to serialize the current configuration values as a hash. This value can be used for comparing configuration between environments.
|
15
|
+
|
16
|
+
## 1.0.1
|
17
|
+
|
18
|
+
### Added
|
19
|
+
- Optimize object shapes for the Ruby interpreter by declaring instance variables in constructors.
|
20
|
+
|
7
21
|
## 1.0.0
|
8
22
|
|
9
23
|
### Added
|
data/README.md
CHANGED
@@ -2,20 +2,29 @@
|
|
2
2
|
|
3
3
|
[![Continuous Integration](https://github.com/bdurand/ultra_settings/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/ultra_settings/actions/workflows/continuous_integration.yml)
|
4
4
|
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/ultra_settings.svg)](https://badge.fury.io/rb/ultra_settings)
|
5
6
|
|
6
|
-
|
7
|
+
## Introduction
|
7
8
|
|
8
|
-
|
9
|
+
UltraSettings is a Ruby gem designed for managing application settings from various sources providing a consistent method for accessing configuration values. It simplifies your application's configuration management by allowing settings to be defined, documented, and accessed seamlessly.
|
9
10
|
|
10
|
-
|
11
|
-
2. Runtime settings (i.e. settings updatable from within the running application)
|
12
|
-
3. YAML configuration files
|
11
|
+
UltraSettings emphasizes well-documented configuration. You can include documentation directly in the configuration code. The gem also includes a [web UI](#web-ui) that can be mounted as a Rack app or embedded in other views allowing admin users to easily view configuration settings and documentation.
|
13
12
|
|
14
|
-
|
13
|
+
## Key Features
|
15
14
|
|
16
|
-
|
15
|
+
This gem supports a three-layer hierarchy for defining configuration sources:
|
17
16
|
|
18
|
-
|
17
|
+
1. Environment Variables
|
18
|
+
2. Runtime Settings (modifiable within the running application)
|
19
|
+
3. YAML Configuration Files
|
20
|
+
|
21
|
+
Settings from higher levels override those from lower ones. For example, values defined in environment variables or runtime settings will override those specified in YAML files. This hierarchy is optional — you can disable it and define specific data sources as needed for each configuration field.
|
22
|
+
|
23
|
+
### Simplified Access and Type Safety
|
24
|
+
|
25
|
+
With UltraSettings, your application code does not need to worry about how or from where a setting value is loaded from. Configuration settings can be accessed using plain Ruby objects and methods simplifying the development process.
|
26
|
+
|
27
|
+
The gem also ensures type safety by typecasting settings to specific data types, so you can rely on consistent data formats without manual type coercion. Supported types include:
|
19
28
|
|
20
29
|
- `String`
|
21
30
|
- `Integer`
|
@@ -25,34 +34,39 @@ Settings are also type cast so you can always be assured that values are returne
|
|
25
34
|
- `Symbol`
|
26
35
|
- `Array<String>`
|
27
36
|
|
28
|
-
|
29
|
-
|
30
|
-
Settings are accessed through singleton classes that you define.
|
37
|
+
Additionally, you can define default values for settings, ensuring that a fallback value is available if a configuration is missing or does not meet constraints.
|
31
38
|
|
32
39
|
## Usage
|
33
40
|
|
34
41
|
### Defining Configurations
|
35
42
|
|
36
|
-
Configurations are classes that extend from the `UltraSettings::Configuration` class.
|
43
|
+
Configurations are defined as classes that extend from the `UltraSettings::Configuration` class. These configuration classes are [singleton classes](https://ruby-doc.org/3.2.2/stdlibs/singleton/Singleton.html).
|
37
44
|
|
38
45
|
You can define fields on your configuration classes with the `field` method. This will define a method on your configuration object with the given name.
|
39
46
|
|
40
47
|
```ruby
|
41
48
|
class MyServiceConfiguration < UltraSettings::Configuration
|
42
|
-
|
49
|
+
self.fields_secret_by_default = false
|
50
|
+
|
51
|
+
field :host, type: :string, description: "The hostname for the service"
|
43
52
|
|
44
|
-
field :port, type: :integer, default: 80
|
53
|
+
field :port, type: :integer, default: 80, description: "The port for the service"
|
45
54
|
|
46
|
-
field :protocol, type: :string, default: "https"
|
55
|
+
field :protocol, type: :string, default: "https", description: "The protocol for the service"
|
47
56
|
|
48
|
-
field :timeout,
|
57
|
+
field :timeout,
|
58
|
+
type: :float,
|
59
|
+
default: 1.0,
|
60
|
+
default_if: ->(val) { val <= 0 },
|
61
|
+
description: "Network timeout in seconds for requests to the service."
|
49
62
|
|
50
63
|
field :auth_token,
|
51
64
|
type: :string,
|
52
65
|
env_var: "MY_SERVICE_TOKEN",
|
53
66
|
runtime_setting: false,
|
54
67
|
yaml_key: false,
|
55
|
-
description: "Bearer token for accessing the service"
|
68
|
+
description: "Bearer token for accessing the service",
|
69
|
+
secret: true
|
56
70
|
|
57
71
|
# You aren't limited to just defining fields, you can define other
|
58
72
|
# helper methods to make using the configuration easier.
|
@@ -62,51 +76,67 @@ class MyServiceConfiguration < UltraSettings::Configuration
|
|
62
76
|
end
|
63
77
|
```
|
64
78
|
|
65
|
-
|
79
|
+
#### Field Options
|
80
|
+
|
81
|
+
You can customize the behavior of each field using various options:
|
82
|
+
|
83
|
+
- `:type` - Specifies the type of the field. The value of the setting will be cast to this type. If the value in the data source cannot be cast to the data type, then it will not be used. Supported types are:
|
66
84
|
|
67
85
|
- `:string` (the default)
|
68
86
|
- `:integer`
|
69
87
|
- `:float`
|
70
|
-
- `:boolean`
|
88
|
+
- `:boolean` (will accept case insensitive strings "true", "false", "1", "0", "t", "f", "yes", "no", "y", "n")
|
71
89
|
- `:datetime`
|
72
90
|
- `:symbol`
|
73
91
|
- `:array` (of strings)
|
74
92
|
|
75
|
-
-
|
93
|
+
- `:description` - Provides a description of the field. This is used for documentation purposes.
|
94
|
+
|
95
|
+
- `:default` - Sets a default value for the field. The value will be cast to the specified type.
|
76
96
|
|
77
|
-
-
|
97
|
+
- `:default_if` - Provides a condition for when the default should be used. This should be a Proc or the name of a method within the class. Useful for ensuring values meet specific constraints. This can provide protection from misconfiguration that can break the application. In the above example, the default value for `timeout` will be used if the value is less than or equal to 0.
|
78
98
|
|
79
|
-
-
|
99
|
+
- `:secret` - Marks the field as secret. Secret fields are not displayed in the web UI. By default, all fields are considered secret to avoid accidentally exposing sensitive values. You can change this default behavior by setting `fields_secret_by_default` to `false` either globally or per configuration.
|
80
100
|
|
81
|
-
-
|
101
|
+
- `:env_var` - Overrides the environment variable name used to populate the field. This is useful if the variable name does not follow the conventional pattern. Set this to `false` to disable loading the field from an environment variable.
|
82
102
|
|
83
|
-
-
|
103
|
+
- `:runtime_setting` - Overrides the runtime setting name for the field. Useful if the runtime setting name does not match the conventional pattern. Set this to `false` to disable loading the field from runtime settings.
|
84
104
|
|
85
|
-
-
|
105
|
+
- `:yaml_key` - Overrides the key in the YAML configuration file for this field. This is useful when the key does not match the field name. Set this to `false` to disable loading the field from a YAML file.
|
86
106
|
|
87
|
-
-
|
107
|
+
- `:static` - Marks the field as a static value. Static values cannot be changed once set and are not allowed to be set from runtime settings. Use this for settings that need to be referenced at the application is initializing.
|
88
108
|
|
89
109
|
### Environment Variables
|
90
110
|
|
91
|
-
Settings will first try to load values from environment variables. Environment variables are a good place to define environment specific values.
|
111
|
+
Settings will first try to load values from environment variables. Environment variables are a good place to define environment specific values or sensitive values that you do not want to store in your codebase.
|
112
|
+
|
113
|
+
#### Default Behavior
|
114
|
+
|
115
|
+
By default, environment variables for settings are constructed using a prefix based on the configuration class name, with the field name appended. For example, a class named `Configs::MySettingsConfiguration` will use the prefix `CONFIGS_MY_SETTINGS_`, resulting in environment variables like `CONFIGS_MY_SETTINGS_HOST`.
|
92
116
|
|
93
|
-
|
117
|
+
#### Customizing Environment Variables
|
94
118
|
|
95
|
-
You can
|
119
|
+
You can customize the behavior of environment variable naming in several ways:
|
96
120
|
|
97
|
-
You can
|
121
|
+
- **Explicit Environment Variables:** You can specify the name of the environment variable to use for a field by setting the `env_var` option on the field. This allows you to use a different name than the default.
|
98
122
|
|
99
|
-
|
123
|
+
- **Lowercase Environment Variables:** Set `env_var_upcase` to false in your configuration class to use lowercase environment variable names.
|
100
124
|
|
101
|
-
|
125
|
+
- **Custom Delimiter:** The delimiter between module names and before the setting name can be customized by setting `env_var_delimiter` on your configuration class. For example, using a delimiter of "." in `Configs::MySettingsConfiguration` would produce environment variables like `CONFIGS.MY_SETTINGS.HOST`.
|
126
|
+
|
127
|
+
- **Custom Prefix:** Set `env_var_prefix` on your configuration class to specify an explicit prefix for environment variables. This allows for more flexibility in naming conventions.
|
128
|
+
|
129
|
+
- **Disabling Environment Variables:** You can disable environment variables as a default source for fields by setting `environment_variables_disabled` to `true` in your configuration class. You can disable environent variables on individual fields by setting `env_var` on the field to `false`.
|
102
130
|
|
103
131
|
If a setting value cannot be loaded from an environment variable, then it's value will attempt to be loaded from a runtime setting.
|
104
132
|
|
105
133
|
### Runtime Settings
|
106
134
|
|
107
|
-
Runtime settings are
|
135
|
+
Runtime settings are configurations loaded while your application is running, allowing for dynamic updates without needing to restart the application. This flexibility makes them ideal for settings that may change frequently or need quick adjustments.
|
136
|
+
|
137
|
+
#### Setting Up Runtime Settings
|
108
138
|
|
109
|
-
To
|
139
|
+
To enable runtime settings, set the `UltraSettings.runtime_settings` attribute to an object that implements a `[]` method and accepts a string argument. For example, to load runtime settings from a Redis database, you could use the following implementation:
|
110
140
|
|
111
141
|
```ruby
|
112
142
|
class RedisRuntimeSettings
|
@@ -122,39 +152,57 @@ end
|
|
122
152
|
UltraSettings.runtime_settings = RedisRuntimeSettings.new
|
123
153
|
```
|
124
154
|
|
125
|
-
|
155
|
+
#### Using the `super_settings` gem
|
156
|
+
|
157
|
+
There is a companion gem [super_settings](https://github.com/bdurand/super_settings) that can be used as a drop in implementation for the runtime settings. You just need to set the runtime settings to the `SuperSettings` object.
|
126
158
|
|
127
159
|
```ruby
|
128
160
|
UltraSettings.runtime_settings = SuperSettings
|
129
161
|
```
|
130
162
|
|
131
|
-
|
163
|
+
#### Customizing Runtime Settings
|
164
|
+
|
165
|
+
By default settings will be loaded from runtime settings by constructing a prefix from the configuration class name (i.e. `Configs::MySettingsConfiguration` uses the prefix `configs.my_settings.`) with the field name appended to it (e.g. `configs.my_settings.host`). By default runtime settings will be in all lowercase letters.
|
166
|
+
|
167
|
+
You can customize the behavior of runtime setting names with the following options:
|
132
168
|
|
133
|
-
You can
|
169
|
+
- **Explicit Runtime Setting Names:** You can specify the name of the runtime setting to use for a field by setting the `runtime_setting` option on the field. This allows you to use a different name than the default.
|
134
170
|
|
135
|
-
|
171
|
+
- **Uppercase Runtime Setting Names:** Set `runtime_setting_upcase` to true in your configuration class to use uppercase runtime setting names.
|
136
172
|
|
137
|
-
|
173
|
+
- **Custom Delimiter:** Change the delimiter used between module names and before the setting name by setting `runtime_setting_delimiter` on your configuration class. For example, using a delimiter of "/" would produce runtime settings like `configs/my_settings/host`.
|
138
174
|
|
139
|
-
|
175
|
+
- **Custom Prefix:** Set `runtime_setting_prefix` on your configuration class to specify a custom prefix for runtime settings, giving you flexibility in naming conventions.
|
176
|
+
|
177
|
+
- **Disabling Runtime Settings:** You can disable runtime settings as a default source for fields by setting `runtime_settings_disabled` to `true` in your configuration class. You can disable runtime settings on individual fields by setting `runtime_setting` on the field to `false`.
|
140
178
|
|
141
179
|
If a setting value cannot be loaded from the runtime settings, then it's value will attempt to be loaded from a YAML file.
|
142
180
|
|
143
181
|
### YAML Files
|
144
182
|
|
145
|
-
|
183
|
+
YAML files are the final source UltraSettings will check when loading configuration values. They provide a convenient way to store default values that can be distributed with your application code.
|
184
|
+
|
185
|
+
By default settings will be loaded from a YAML file determined by its class name (i.e. `Configs::MySettingsConfiguration` uses the file `configs/my_settings.yml`). The file is searched for in the path defined by `UltraSettings.yaml_config_path`. If the file does not exist, the YAML source strategy will not be used.
|
186
|
+
|
187
|
+
#### Customizing YAML Files
|
188
|
+
|
189
|
+
- **Explicit YAML Key:** You can specify the key in the YAML file to use for a field by setting the `yaml_key` option on the field. This allows you to use a different key than the default.
|
190
|
+
|
191
|
+
- **Custom YAML File Path:** You can specify an explicit YAML file by setting `configuration_file` on your configuration class to the desired file path.
|
146
192
|
|
147
|
-
|
193
|
+
- **Disable YAML Source:** To disable YAML files as a default source for your fields, set `yaml_config_disabled` to true on your configuration class. You can disable YAML files on individual fields by setting `yaml_key` on the field to `false`.
|
148
194
|
|
149
|
-
|
195
|
+
#### ERB Support
|
150
196
|
|
151
|
-
|
197
|
+
YAML files support ERB markup (i.e., <%= %>) that will be evaluated before the YAML is parsed. This feature allows for dynamically generated values within the YAML file.
|
152
198
|
|
153
|
-
|
199
|
+
#### Environment-Specific Configurations
|
154
200
|
|
155
|
-
YAML files define environment
|
201
|
+
YAML files can define environment-specific configurations. The file must contain a hash where the keys represent the names of your application environments (e.g., `development`, `test`, `production`). You can specify the environment to use by setting `UltraSettings.yaml_config_env` (default is "development").
|
156
202
|
|
157
|
-
|
203
|
+
A special key, `shared`, can be defined in the YAML file. The settings under this key will be merged with the environment-specific settings. Values from the specific environment will always overwrite those from `shared`.
|
204
|
+
|
205
|
+
#### Example YAML File
|
158
206
|
|
159
207
|
```yaml
|
160
208
|
shared:
|
@@ -169,7 +217,7 @@ production:
|
|
169
217
|
host: prod.example.com
|
170
218
|
```
|
171
219
|
|
172
|
-
The values for the
|
220
|
+
The values for the development environment would be the combination of `development` and `shared`:
|
173
221
|
|
174
222
|
```ruby
|
175
223
|
{
|
@@ -179,7 +227,7 @@ The values for the "development" environment would be the combination of develop
|
|
179
227
|
}
|
180
228
|
```
|
181
229
|
|
182
|
-
While for
|
230
|
+
While for production, the values would be the combination of `production` and `shared`:
|
183
231
|
|
184
232
|
```ruby
|
185
233
|
{
|
@@ -189,13 +237,13 @@ While for "production", the values would be the combination of production and sh
|
|
189
237
|
}
|
190
238
|
```
|
191
239
|
|
192
|
-
|
240
|
+
#### Rails Integration
|
193
241
|
|
194
242
|
In a Rails application, the YAML environment will be set to the Rails environment and YAML files will be assumed to exist in the `config` directory.
|
195
243
|
|
196
244
|
### Removing The Hierarchy
|
197
245
|
|
198
|
-
If you
|
246
|
+
If you prefer not to use the default hierarchy of environment variables, runtime settings, and YAML files, you can disable it. This allows you to explicitly define which data sources should be used for each field.
|
199
247
|
|
200
248
|
```ruby
|
201
249
|
class MyServiceConfiguration < UtraSettings::Configuration
|
@@ -219,25 +267,30 @@ UltraSettings.yaml_config_disabled = true
|
|
219
267
|
|
220
268
|
### Accessing settings
|
221
269
|
|
222
|
-
Configurations are singleton objects
|
270
|
+
Configurations in UltraSettings are singleton objects, and settings are accessed by calling methods directly on these objects.
|
223
271
|
|
224
272
|
```ruby
|
225
273
|
MyServiceConfiguration.instance.host
|
226
274
|
```
|
227
275
|
|
228
|
-
|
276
|
+
#### Adding Configurations to UltraSettings
|
277
|
+
|
278
|
+
To simplify access, you can add configurations to the `UltraSettings` object. UltraSettings will derive the configuration class name based on the name provided and define a method that returns the configuraiton object. For example:
|
279
|
+
|
229
280
|
```ruby
|
230
281
|
UltraSettings.add(:my_service)
|
282
|
+
UltraSettings.my_service # => MyServiceConfiguration.instance
|
231
283
|
UltraSettings.my_service.host
|
232
284
|
```
|
233
285
|
|
234
|
-
|
286
|
+
Alternatively, you can explicitly specify the class name to map to a method name:
|
287
|
+
|
235
288
|
```ruby
|
236
289
|
UltraSettings.add(:my, "MyServiceConfiguration")
|
237
290
|
UltraSettings.my.host
|
238
291
|
```
|
239
292
|
|
240
|
-
In a Rails application, you could add
|
293
|
+
In a Rails application, you could add syntactic sugar by exposing the `UltraSettings` object as a helper method in application.rb.
|
241
294
|
|
242
295
|
```ruby
|
243
296
|
module MyApp
|
@@ -251,7 +304,10 @@ end
|
|
251
304
|
Rails.application.settings.my_service.host
|
252
305
|
```
|
253
306
|
|
254
|
-
|
307
|
+
#### Using a Helper Method
|
308
|
+
|
309
|
+
To keep your codebase clean, especially if most configurations are accessed from within a specific class, you can encapsulate the configuration access in a helper method.
|
310
|
+
|
255
311
|
|
256
312
|
```ruby
|
257
313
|
class MyService
|
@@ -268,11 +324,16 @@ end
|
|
268
324
|
|
269
325
|
### Web UI
|
270
326
|
|
271
|
-
|
327
|
+
UltraSettings provides a web UI via a mountable Rack application. You can use this to view the settings values and documentation. The UI will not display the value of any setting marked as secret.
|
272
328
|
|
273
329
|
![Web UI](assets/web_ui.png)
|
274
330
|
|
275
|
-
|
331
|
+
It is strongly recommended to secure the web UI with your application's authorization framework so that it is only visible to internal admin users.
|
332
|
+
|
333
|
+
#### Mounting the Web UI in a Rails Application
|
334
|
+
|
335
|
+
Below is an example of mounting the web UI in a Rails application using HTTP Basic authentication.
|
336
|
+
|
276
337
|
|
277
338
|
```ruby
|
278
339
|
# config/routes.rb
|
@@ -285,13 +346,26 @@ mount Rack::Builder.new do
|
|
285
346
|
end, at: "/ultra_settings"
|
286
347
|
```
|
287
348
|
|
288
|
-
|
349
|
+
#### Embedding the Settings View in Admin Tools
|
350
|
+
|
351
|
+
If you prefer to embed the settings view directly into your own admin tools or dashboard, you can use the `UltraSettings::ConfigurationView` class to render the settings interface within your existing views:
|
352
|
+
|
353
|
+
```erb
|
354
|
+
<h1>My Service Settings</h1>
|
355
|
+
|
356
|
+
<%= UltraSettings::ConfigurationView.new(MyServiceConfiguration.instance).render %>
|
357
|
+
```
|
358
|
+
|
359
|
+
This approach allows for seamless integration of the settings UI into your application's admin interface, leveraging your existing authentication and authorization mechanisms. The settings are rendered in an HTML table which you can format with your own CSS.
|
289
360
|
|
290
|
-
|
361
|
+
### Testing With UltraSettings
|
362
|
+
|
363
|
+
When writing automated tests, you may need to override configuration settings to test different scenarios. UltraSettings provides the `UltraSettings.override!` method to temporarily change settings within a test block. Below are examples of how to override the `TestConfiguration#foo` value in a test.
|
291
364
|
|
292
365
|
```ruby
|
293
|
-
# Override a configuration added on the global namespace
|
366
|
+
# Override a configuration added on the global namespace.
|
294
367
|
|
368
|
+
# Note: you must have already added the configuration with UltraSettings.add(:test)
|
295
369
|
UltraSettings.override!(test: {foo: "bar"}) do
|
296
370
|
expect(TestConfiguration.instance.foo).to eq "bar"
|
297
371
|
end
|
@@ -309,14 +383,17 @@ TestConfiguration.instance.override!(foo: "bar") do
|
|
309
383
|
end
|
310
384
|
```
|
311
385
|
|
312
|
-
|
386
|
+
#### RSpec Integration
|
387
|
+
|
388
|
+
If you are using RSpec, you can simplify overriding settings by setting up a global around hook. This hook will check for the presence of a :settings metadata key and apply the overrides automatically within the test block.
|
389
|
+
|
313
390
|
|
314
391
|
```ruby
|
315
392
|
# RSpec setup
|
316
393
|
RSpec.configure do |config|
|
317
|
-
config.around do |example|
|
318
|
-
if example.metadata[:
|
319
|
-
UltraSettings.override!(example.metadata[:
|
394
|
+
config.around(:each, :settings) do |example|
|
395
|
+
if example.metadata[:settings].is_a?(Hash)
|
396
|
+
UltraSettings.override!(example.metadata[:settings]) do
|
320
397
|
example.run
|
321
398
|
end
|
322
399
|
else
|
@@ -324,13 +401,19 @@ RSpec.configure do |config|
|
|
324
401
|
end
|
325
402
|
end
|
326
403
|
end
|
404
|
+
```
|
405
|
+
|
406
|
+
With this setup, you can easily specify settings overrides within individual test blocks using metadata:
|
327
407
|
|
328
|
-
|
329
|
-
it 'has the settings I want',
|
408
|
+
```ruby
|
409
|
+
it 'has the settings I want', settings: {test: {foo: "bar"}} do
|
330
410
|
expect(UltraSettings.test.foo).to eq("bar")
|
331
411
|
end
|
332
412
|
```
|
333
413
|
|
414
|
+
This approach keeps your tests clean and readable while allowing for flexible configuration management during testing.
|
415
|
+
|
416
|
+
|
334
417
|
## Installation
|
335
418
|
|
336
419
|
Add this line to your application's Gemfile:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.1.0
|
data/app/application.css
CHANGED
@@ -28,39 +28,18 @@
|
|
28
28
|
background-color: rgba(0, 0, 0, .03);
|
29
29
|
}
|
30
30
|
|
31
|
-
.ultra-settings-code {
|
31
|
+
.ultra-settings-table code {
|
32
32
|
font-family: monospace;
|
33
33
|
font-size: 0.9rem;
|
34
34
|
display: inline;
|
35
|
-
|
36
|
-
|
37
|
-
.ultra-settings-static {
|
38
|
-
color: gray;
|
39
|
-
font-style: italic;
|
40
|
-
font-size: 0.9rem;
|
41
|
-
}
|
42
|
-
|
43
|
-
.ultra-settings-current-source {
|
35
|
+
color: darkred;
|
44
36
|
font-weight: 600;
|
45
|
-
color: blue;
|
46
37
|
}
|
47
38
|
|
48
|
-
.ultra-settings-
|
49
|
-
font-size: 0.9rem;
|
39
|
+
.ultra-settings-table em {
|
50
40
|
color: gray;
|
51
|
-
}
|
52
|
-
|
53
|
-
.ultra-settings-info {
|
54
|
-
margin-bottom: 1rem;
|
55
|
-
}
|
56
|
-
|
57
|
-
.ultra-settings-error {
|
58
|
-
color: darkred;
|
59
|
-
}
|
60
|
-
|
61
|
-
.ultra-settings-not-applicable {
|
62
|
-
color: #999;
|
63
41
|
font-style: italic;
|
42
|
+
font-size: 0.9rem;
|
64
43
|
}
|
65
44
|
|
66
45
|
.ultra-settings-select {
|
@@ -0,0 +1,134 @@
|
|
1
|
+
<table class="<%= html_escape(table_class.to_s) %>">
|
2
|
+
<thead>
|
3
|
+
<% unless configuration.class.yaml_config_disabled? || configuration.class.configuration_file.nil? %>
|
4
|
+
<tr>
|
5
|
+
<th colspan="6">
|
6
|
+
Configuration File:
|
7
|
+
<span style="font-weight: normal;">
|
8
|
+
<%= html_escape(relative_path(configuration.class.configuration_file)) %>
|
9
|
+
<% unless configuration.class.configuration_file&.exist? %>
|
10
|
+
<em>(File does not exist)</em>
|
11
|
+
<% end %>
|
12
|
+
</span>
|
13
|
+
</th>
|
14
|
+
</tr>
|
15
|
+
<% end %>
|
16
|
+
<tr>
|
17
|
+
<th>Name</th>
|
18
|
+
<th>Value</th>
|
19
|
+
<th>Type</th>
|
20
|
+
<th>Notes</th>
|
21
|
+
</tr>
|
22
|
+
</thead>
|
23
|
+
<tbody>
|
24
|
+
<% configuration.class.fields.each do |field| %>
|
25
|
+
<% source = configuration.__source__(field.name) %>
|
26
|
+
<tr>
|
27
|
+
<td>
|
28
|
+
<code><%= html_escape(field.name) %></code>
|
29
|
+
</td>
|
30
|
+
|
31
|
+
<td style="word-wrap: break-word; max-width:30em;">
|
32
|
+
<% if configuration[field.name].nil? %>
|
33
|
+
<em>nil</em>
|
34
|
+
<% elsif field.secret? %>
|
35
|
+
<%= html_escape(secret_value(configuration[field.name])) %>
|
36
|
+
<% else %>
|
37
|
+
<%= html_escape(display_value(configuration[field.name])) %>
|
38
|
+
<% end %>
|
39
|
+
</td>
|
40
|
+
|
41
|
+
<td>
|
42
|
+
<%= html_escape(field.type) %>
|
43
|
+
<%
|
44
|
+
options = []
|
45
|
+
options << 'static' if field.static?
|
46
|
+
options << 'secret' if field.secret?
|
47
|
+
%>
|
48
|
+
<% unless options.empty? %>
|
49
|
+
<div>
|
50
|
+
<em><%= html_escape(options.join(', ')) %></em>
|
51
|
+
</div>
|
52
|
+
<% end %>
|
53
|
+
</td>
|
54
|
+
|
55
|
+
<td>
|
56
|
+
<% unless field.description.to_s.empty? %>
|
57
|
+
<div>
|
58
|
+
<%= html_escape(field.description) %>
|
59
|
+
</div>
|
60
|
+
<% end %>
|
61
|
+
|
62
|
+
<ul style="margin: 0; padding: 0;list-style-type: disc; list-style-position: inside;">
|
63
|
+
<% if field.env_var && !configuration.class.environment_variables_disabled? %>
|
64
|
+
<li>
|
65
|
+
<% if source == :env %>
|
66
|
+
<strong>
|
67
|
+
Currently
|
68
|
+
<% else %>
|
69
|
+
Can be
|
70
|
+
<% end %>
|
71
|
+
set with the
|
72
|
+
<code><%= show_defined_value(field.env_var, configuration.__value_from_source__(field.name, :env), field.secret?) %></code>
|
73
|
+
environment variable.
|
74
|
+
<% if source == :env %>
|
75
|
+
</strong>
|
76
|
+
<% end %>
|
77
|
+
</li>
|
78
|
+
<% end %>
|
79
|
+
<% if field.runtime_setting && !configuration.class.runtime_settings_disabled? %>
|
80
|
+
<li>
|
81
|
+
<% if source == :settings %>
|
82
|
+
<strong>
|
83
|
+
Currently
|
84
|
+
<% else %>
|
85
|
+
Can be
|
86
|
+
<% end %>
|
87
|
+
set with the
|
88
|
+
<code><%= show_defined_value(field.runtime_setting, configuration.__value_from_source__(field.name, :settings), field.secret?) %></code>
|
89
|
+
runtime setting.
|
90
|
+
<% if source == :settings %>
|
91
|
+
</strong>
|
92
|
+
<% end %>
|
93
|
+
</li>
|
94
|
+
<% end %>
|
95
|
+
<% if field.yaml_key && !configuration.class.yaml_config_disabled? %>
|
96
|
+
<li>
|
97
|
+
<% if source == :yaml %>
|
98
|
+
<strong>
|
99
|
+
Currently
|
100
|
+
<% else %>
|
101
|
+
Can be
|
102
|
+
<% end %>
|
103
|
+
set with the
|
104
|
+
<code><%= show_defined_value(field.yaml_key, configuration.__value_from_source__(field.name, :yaml), field.secret?) %></code>
|
105
|
+
key in the configuration file.
|
106
|
+
<% if source == :yaml %>
|
107
|
+
</strong>
|
108
|
+
<% end %>
|
109
|
+
</li>
|
110
|
+
<% end %>
|
111
|
+
<% if field.default.nil? %>
|
112
|
+
<% if source == :default %>
|
113
|
+
<li>
|
114
|
+
<strong>Not set</strong>
|
115
|
+
</li>
|
116
|
+
<% end %>
|
117
|
+
<% else %>
|
118
|
+
<li>
|
119
|
+
<% if source == :default %>
|
120
|
+
<strong>
|
121
|
+
Currently set with the
|
122
|
+
<%= show_defined_value("default value", field.default, field.secret?) %>.
|
123
|
+
</strong>
|
124
|
+
<% else %>
|
125
|
+
This field has a <%= show_defined_value("default value", field.default, field.secret?) %>.
|
126
|
+
<% end %>
|
127
|
+
</li>
|
128
|
+
<% end %>
|
129
|
+
</ul>
|
130
|
+
</td>
|
131
|
+
</tr>
|
132
|
+
<% end %>
|
133
|
+
</tbody>
|
134
|
+
</table>
|
data/app/index.html.erb
CHANGED
@@ -12,79 +12,7 @@
|
|
12
12
|
<% configuration = UltraSettings.send(name) %>
|
13
13
|
|
14
14
|
<div class="ultra-settings-configuration" id="config-<%= name %>" style="display:none;">
|
15
|
-
|
16
|
-
<div class="ultra-settings-info">
|
17
|
-
YAML File:
|
18
|
-
<span class="ultra-settings-code <%= 'ultra-settings-error' unless configuration.class.configuration_file %>">
|
19
|
-
<%= configuration.class.configuration_file.to_s.sub(/\A#{Regexp.escape(UltraSettings::Configuration.yaml_config_path.to_s)}\//, "") %>
|
20
|
-
</span>
|
21
|
-
</div>
|
22
|
-
<% end %>
|
23
|
-
|
24
|
-
<table class="ultra-settings-table">
|
25
|
-
<thead>
|
26
|
-
<tr>
|
27
|
-
<th>Name</th>
|
28
|
-
<th>Type</th>
|
29
|
-
<th>Environment Variable</th>
|
30
|
-
<th>Runtime Setting</th>
|
31
|
-
<th>YAML Key</th>
|
32
|
-
<th>Default</th>
|
33
|
-
</tr>
|
34
|
-
</thead>
|
35
|
-
<tbody>
|
36
|
-
<% configuration.class.fields.sort_by(&:name).each do |field| %>
|
37
|
-
<tr>
|
38
|
-
<td>
|
39
|
-
<%= field.name %>
|
40
|
-
<% unless field.description.to_s.empty? %>
|
41
|
-
<div class="ultra-settings-description">
|
42
|
-
<%= field.description %>
|
43
|
-
</div>
|
44
|
-
<% end %>
|
45
|
-
</td>
|
46
|
-
<td>
|
47
|
-
<%= field.type %>
|
48
|
-
<% if field.static? %>
|
49
|
-
<div class="ultra-settings-static">
|
50
|
-
static
|
51
|
-
</div>
|
52
|
-
<% end %>
|
53
|
-
</td>
|
54
|
-
<td>
|
55
|
-
<% if field.env_var && !configuration.class.environment_variables_disabled? %>
|
56
|
-
<pre class="ultra-settings-code <%= 'ultra-settings-current-source' if configuration.__source__(field.name) == :env %>"><%= field.env_var %></pre>
|
57
|
-
<% else %>
|
58
|
-
<span class="ultra-settings-not-applicable">n/a</span>
|
59
|
-
<% end %>
|
60
|
-
</td>
|
61
|
-
<td>
|
62
|
-
<% if field.runtime_setting && !configuration.class.runtime_settings_disabled? %>
|
63
|
-
<pre class="ultra-settings-code <%= 'ultra-settings-current-source' if configuration.__source__(field.name) == :settings %>"><%= field.runtime_setting %></pre>
|
64
|
-
<% else %>
|
65
|
-
<span class="ultra-settings-not-applicable">n/a</span>
|
66
|
-
<% end %>
|
67
|
-
</td>
|
68
|
-
<td>
|
69
|
-
<% if field.yaml_key && !configuration.class.yaml_config_disabled? %>
|
70
|
-
<pre class="ultra-settings-code <%= 'ultra-settings-current-source' if configuration.__source__(field.name) == :yaml %>"><%= field.yaml_key %></pre>
|
71
|
-
<% else %>
|
72
|
-
<span class="ultra-settings-not-applicable">n/a</span>
|
73
|
-
<% end %>
|
74
|
-
</td>
|
75
|
-
<td>
|
76
|
-
<span class="<%= 'ultra-settings-current-source' if configuration.__source__(field.name) == :default %>">
|
77
|
-
<% if field.default.nil? %>
|
78
|
-
<em>nil</em>
|
79
|
-
<% else %>
|
80
|
-
✔
|
81
|
-
<% end %>
|
82
|
-
</span>
|
83
|
-
</td>
|
84
|
-
</tr>
|
85
|
-
<% end %>
|
86
|
-
</tbody>
|
87
|
-
</table>
|
15
|
+
<%= UltraSettings::ConfigurationView.new(configuration) %>
|
88
16
|
</div>
|
89
17
|
<% end %>
|
90
18
|
|
@@ -33,10 +33,11 @@ module UltraSettings
|
|
33
33
|
# @param yaml_key [String, Symbol] The name of the YAML key to use for the field. By default
|
34
34
|
# this is the name of the field.
|
35
35
|
# @return [void]
|
36
|
-
def field(name, type: :string, description: nil, default: nil, default_if: nil, static: nil, runtime_setting: nil, env_var: nil, yaml_key: nil)
|
36
|
+
def field(name, type: :string, description: nil, default: nil, default_if: nil, static: nil, secret: nil, runtime_setting: nil, env_var: nil, yaml_key: nil)
|
37
37
|
name = name.to_s
|
38
38
|
type = type.to_sym
|
39
39
|
static = !!static
|
40
|
+
secret = lambda { fields_secret_by_default? } if secret.nil?
|
40
41
|
|
41
42
|
unless name.match?(ALLOWED_NAME_PATTERN)
|
42
43
|
raise ArgumentError.new("Invalid name: #{name.inspect}")
|
@@ -59,7 +60,8 @@ module UltraSettings
|
|
59
60
|
env_var: construct_env_var(name, env_var),
|
60
61
|
runtime_setting: (static ? nil : construct_runtime_setting(name, runtime_setting)),
|
61
62
|
yaml_key: construct_yaml_key(name, yaml_key),
|
62
|
-
static: static
|
63
|
+
static: static,
|
64
|
+
secret: secret
|
63
65
|
)
|
64
66
|
|
65
67
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
|
@@ -69,7 +71,7 @@ module UltraSettings
|
|
69
71
|
RUBY
|
70
72
|
|
71
73
|
if type == :boolean
|
72
|
-
alias_method "#{name}?", name
|
74
|
+
alias_method :"#{name}?", name
|
73
75
|
end
|
74
76
|
end
|
75
77
|
|
@@ -306,6 +308,23 @@ module UltraSettings
|
|
306
308
|
get_inheritable_class_attribute(:@yaml_config_env, "development")
|
307
309
|
end
|
308
310
|
|
311
|
+
# Sets the default value for the secret property of fields. Individual fields can still
|
312
|
+
# override this value by explicitly setting the secret property. By default, fields are
|
313
|
+
# considered secret.
|
314
|
+
#
|
315
|
+
# @param value [Boolean]
|
316
|
+
# @return [void]
|
317
|
+
def fields_secret_by_default=(value)
|
318
|
+
set_inheritable_class_attribute(:@fields_secret_by_default, !!value)
|
319
|
+
end
|
320
|
+
|
321
|
+
# Check if fields are considered secret by default.
|
322
|
+
#
|
323
|
+
# @return [Boolean]
|
324
|
+
def fields_secret_by_default?
|
325
|
+
get_inheritable_class_attribute(:@fields_secret_by_default, true)
|
326
|
+
end
|
327
|
+
|
309
328
|
# Override field values within a block.
|
310
329
|
#
|
311
330
|
# @param values [Hash<Symbol, Object>]] List of fields with the values they
|
@@ -419,6 +438,7 @@ module UltraSettings
|
|
419
438
|
@mutex = Mutex.new
|
420
439
|
@memoized_values = {}
|
421
440
|
@override_values = {}
|
441
|
+
@yaml_config = nil
|
422
442
|
end
|
423
443
|
|
424
444
|
def [](name)
|
@@ -449,12 +469,61 @@ module UltraSettings
|
|
449
469
|
end
|
450
470
|
end
|
451
471
|
|
472
|
+
# Get the current source for the field.
|
473
|
+
#
|
474
|
+
# @param name [String, Symbol] the name of the field.
|
475
|
+
# @return [Symbol, nil] The source of the value (:env, :settings, :yaml, or :default).
|
452
476
|
def __source__(name)
|
453
|
-
field = self.class.send(:defined_fields)[name]
|
477
|
+
field = self.class.send(:defined_fields)[name.to_s]
|
478
|
+
raise ArgumentError.new("Unknown field: #{name.inspect}") unless field
|
479
|
+
|
454
480
|
source = field.source(env: ENV, settings: UltraSettings.__runtime_settings__, yaml_config: __yaml_config__)
|
455
481
|
source || :default
|
456
482
|
end
|
457
483
|
|
484
|
+
# Get the value of the field from the specified source.
|
485
|
+
#
|
486
|
+
# @param name [String, Symbol] the name of the field.
|
487
|
+
# @param source [Symbol] the source of the value (:env, :settings, :yaml, or :default).
|
488
|
+
# @return [Object] The value of the field.
|
489
|
+
def __value_from_source__(name, source)
|
490
|
+
field = self.class.send(:defined_fields)[name.to_s]
|
491
|
+
raise ArgumentError.new("Unknown field: #{name.inspect}") unless field
|
492
|
+
|
493
|
+
case source
|
494
|
+
when :env
|
495
|
+
field.value(env: ENV)
|
496
|
+
when :settings
|
497
|
+
field.value(settings: UltraSettings.__runtime_settings__)
|
498
|
+
when :yaml
|
499
|
+
field.value(yaml_config: __yaml_config__)
|
500
|
+
when :default
|
501
|
+
field.default
|
502
|
+
else
|
503
|
+
raise ArgumentError.new("Unknown source: #{source.inspect}")
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
# Output the current state of the configuration as a hash. If the field is marked as a secret,
|
508
|
+
# then the value will be a secure hash of the value instead of the value itself.
|
509
|
+
#
|
510
|
+
# The intent of this method is to provide a serializable value that captures the current state
|
511
|
+
# of the configuration without exposing any secrets. You could, for instance, use the output
|
512
|
+
# to compare the configuration of you application between two different environments.
|
513
|
+
#
|
514
|
+
# @return [Hash]
|
515
|
+
def __to_hash__
|
516
|
+
payload = {}
|
517
|
+
self.class.fields.each do |field|
|
518
|
+
value = self[field.name]
|
519
|
+
if field.secret? && !value.nil?
|
520
|
+
value = "securehash:#{Digest::MD5.hexdigest(Digest::SHA256.hexdigest(value.to_s))}"
|
521
|
+
end
|
522
|
+
payload[field.name] = value
|
523
|
+
end
|
524
|
+
payload
|
525
|
+
end
|
526
|
+
|
458
527
|
private
|
459
528
|
|
460
529
|
def __get_value__(name)
|
@@ -509,7 +578,7 @@ module UltraSettings
|
|
509
578
|
end
|
510
579
|
|
511
580
|
def __yaml_config__
|
512
|
-
@yaml_config ||=
|
581
|
+
@yaml_config ||= self.class.load_yaml_config || {}
|
513
582
|
end
|
514
583
|
end
|
515
584
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UltraSettings
|
4
|
+
# This class can render information about a configuration in an HTML table. It is used by the
|
5
|
+
# bundled web UI, but you can use it to embed the configuration information in your own web pages.
|
6
|
+
#
|
7
|
+
# The output will be an HTML table. You can specify the CSS class for the table by passing the
|
8
|
+
# `table_class` option to the `render` method. By default the table will have the class
|
9
|
+
# `ultra-settings-table`.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# <h1>Service Configuration</h1>
|
13
|
+
# <%= UltraSettings::ConfigurationView.new(ServiceConfiguration.instance).render(table_class: "table table-striped") %>
|
14
|
+
class ConfigurationView
|
15
|
+
@template = nil
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def template
|
19
|
+
@template ||= ERB.new(read_app_file("configuration.html.erb"))
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def read_app_file(path)
|
25
|
+
File.read(File.join(app_dir, path))
|
26
|
+
end
|
27
|
+
|
28
|
+
def app_dir
|
29
|
+
File.expand_path(File.join("..", "..", "app"), __dir__)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(configuration)
|
34
|
+
@configuration = configuration
|
35
|
+
end
|
36
|
+
|
37
|
+
def render(table_class: "ultra-settings-table")
|
38
|
+
configuration = @configuration
|
39
|
+
html = self.class.template.result(binding)
|
40
|
+
html = html.html_safe if html.respond_to?(:html_safe)
|
41
|
+
html
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
render
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def html_escape(value)
|
51
|
+
ERB::Util.html_escape(value)
|
52
|
+
end
|
53
|
+
|
54
|
+
def display_value(value)
|
55
|
+
case value
|
56
|
+
when Time
|
57
|
+
value.iso8601
|
58
|
+
else
|
59
|
+
value.inspect
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def show_defined_value(label, value, secret)
|
64
|
+
title = if value.nil?
|
65
|
+
"Not set"
|
66
|
+
elsif secret
|
67
|
+
"Secret value"
|
68
|
+
else
|
69
|
+
"Value: #{display_value(value)}"
|
70
|
+
end
|
71
|
+
"<dfn title=\"#{html_escape(title)}\">#{html_escape(label)}</dfn>"
|
72
|
+
end
|
73
|
+
|
74
|
+
def secret_value(value)
|
75
|
+
if value.nil?
|
76
|
+
"nil"
|
77
|
+
else
|
78
|
+
"••••••••••••••••"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def relative_path(path)
|
83
|
+
root_path = Pathname.new(Dir.pwd)
|
84
|
+
config_path = UltraSettings::Configuration.yaml_config_path
|
85
|
+
unless config_path.realpath.to_s.start_with?("#{root_path.realpath}#{File::SEPARATOR}")
|
86
|
+
root_path = config_path
|
87
|
+
end
|
88
|
+
path.relative_path_from(root_path)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/ultra_settings/field.rb
CHANGED
@@ -20,6 +20,8 @@ module UltraSettings
|
|
20
20
|
# @param env_var [String, Symbol] The name of the environment variable to use for the field.
|
21
21
|
# @param runtime_setting [String, Symbol] The name of the setting to use for the field.
|
22
22
|
# @param yaml_key [String, Symbol] The name of the YAML key to use for the field.
|
23
|
+
# @param static [Boolean] Whether or not the field is static and cannot be changed at runtime.
|
24
|
+
# @param secret [Boolean] Whether or not the field contains a value that should be kept secret.
|
23
25
|
def initialize(
|
24
26
|
name:,
|
25
27
|
type: :string,
|
@@ -29,7 +31,8 @@ module UltraSettings
|
|
29
31
|
env_var: nil,
|
30
32
|
runtime_setting: nil,
|
31
33
|
yaml_key: nil,
|
32
|
-
static: false
|
34
|
+
static: false,
|
35
|
+
secret: false
|
33
36
|
)
|
34
37
|
@name = name.to_s.freeze
|
35
38
|
@type = type.to_sym
|
@@ -40,13 +43,14 @@ module UltraSettings
|
|
40
43
|
@runtime_setting = runtime_setting&.to_s&.freeze
|
41
44
|
@yaml_key = yaml_key&.to_s&.freeze
|
42
45
|
@static = !!static
|
46
|
+
@secret = (secret.respond_to?(:call) ? secret : !!secret)
|
43
47
|
end
|
44
48
|
|
45
49
|
# Get the value for the field from the passed in state.
|
46
50
|
#
|
47
|
-
# @param env [
|
48
|
-
# @param settings [
|
49
|
-
# @param yaml_config [
|
51
|
+
# @param env [Hash, nil] The environment variables.
|
52
|
+
# @param settings [Hash, nil] The runtime settings.
|
53
|
+
# @param yaml_config [Hash, nil] The YAML configuration.
|
50
54
|
def value(env: nil, settings: nil, yaml_config: nil)
|
51
55
|
fetch_value_and_source(env: env, settings: settings, yaml_config: yaml_config).first
|
52
56
|
end
|
@@ -76,6 +80,17 @@ module UltraSettings
|
|
76
80
|
@static
|
77
81
|
end
|
78
82
|
|
83
|
+
# Returns true if the field is marked as having a secret value.
|
84
|
+
#
|
85
|
+
# @return [Boolean]
|
86
|
+
def secret?
|
87
|
+
if @secret.respond_to?(:call)
|
88
|
+
!!@secret.call
|
89
|
+
else
|
90
|
+
@secret
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
79
94
|
private
|
80
95
|
|
81
96
|
def fetch_value_and_source(env:, settings:, yaml_config:)
|
@@ -5,6 +5,10 @@ module UltraSettings
|
|
5
5
|
# No setting values are displayed, but you should still add some
|
6
6
|
# sort of authentication if you want to use this in production.
|
7
7
|
class RackApp
|
8
|
+
def initialize
|
9
|
+
@webview = nil
|
10
|
+
end
|
11
|
+
|
8
12
|
def call(env)
|
9
13
|
[200, {"content-type" => "text/html; charset=utf8"}, [webview.render_settings]]
|
10
14
|
end
|
data/lib/ultra_settings.rb
CHANGED
@@ -5,12 +5,14 @@ require "yaml"
|
|
5
5
|
require "time"
|
6
6
|
require "pathname"
|
7
7
|
require "singleton"
|
8
|
+
require "digest"
|
8
9
|
|
9
10
|
require_relative "ultra_settings/configuration"
|
10
11
|
require_relative "ultra_settings/coerce"
|
11
12
|
require_relative "ultra_settings/field"
|
12
13
|
require_relative "ultra_settings/rack_app"
|
13
14
|
require_relative "ultra_settings/web_view"
|
15
|
+
require_relative "ultra_settings/configuration_view"
|
14
16
|
require_relative "ultra_settings/yaml_config"
|
15
17
|
require_relative "ultra_settings/version"
|
16
18
|
|
@@ -29,6 +31,7 @@ module UltraSettings
|
|
29
31
|
|
30
32
|
@configurations = {}
|
31
33
|
@mutex = Mutex.new
|
34
|
+
@runtime_settings = nil
|
32
35
|
|
33
36
|
class << self
|
34
37
|
# Adds a configuration to the root namespace. The configuration will be
|
@@ -160,10 +163,14 @@ module UltraSettings
|
|
160
163
|
# @return [Object, nil]
|
161
164
|
# @api private
|
162
165
|
def __runtime_settings__
|
163
|
-
@runtime_settings
|
166
|
+
@runtime_settings
|
164
167
|
end
|
165
168
|
|
166
|
-
|
169
|
+
def fields_secret_by_default=(value)
|
170
|
+
Configuration.fields_secret_by_default = value
|
171
|
+
end
|
172
|
+
|
173
|
+
# Explicitly set values for setting within a block. This is useful for testing
|
167
174
|
# or other situations where you want hard code a specific set of values.
|
168
175
|
#
|
169
176
|
# @param settings [Hash] The settings to set.
|
data/ultra_settings.gemspec
CHANGED
@@ -4,11 +4,17 @@ Gem::Specification.new do |spec|
|
|
4
4
|
spec.authors = ["Brian Durand"]
|
5
5
|
spec.email = ["bbdurand@gmail.com"]
|
6
6
|
|
7
|
-
spec.summary = "
|
7
|
+
spec.summary = "UltraSettings is a Ruby gem that provides a flexible and documented approach to managing application configurations from multiple sources, including environment variables, runtime settings, and YAML files, with an optional web UI for easy documentation."
|
8
8
|
|
9
9
|
spec.homepage = "https://github.com/bdurand/ultra_settings"
|
10
10
|
spec.license = "MIT"
|
11
11
|
|
12
|
+
spec.metadata = {
|
13
|
+
"homepage_uri" => spec.homepage,
|
14
|
+
"source_code_uri" => spec.homepage,
|
15
|
+
"changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md"
|
16
|
+
}
|
17
|
+
|
12
18
|
# Specify which files should be added to the gem when it is released.
|
13
19
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
14
20
|
ignore_files = %w[
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ultra_settings
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Durand
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-09-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -32,17 +32,19 @@ extensions: []
|
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
34
|
- CHANGELOG.md
|
35
|
-
- MIT-LICENSE
|
35
|
+
- MIT-LICENSE.txt
|
36
36
|
- README.md
|
37
37
|
- VERSION
|
38
38
|
- app/application.css
|
39
39
|
- app/application.js
|
40
|
+
- app/configuration.html.erb
|
40
41
|
- app/index.html.erb
|
41
42
|
- app/layout.css
|
42
43
|
- app/layout.html.erb
|
43
44
|
- lib/ultra_settings.rb
|
44
45
|
- lib/ultra_settings/coerce.rb
|
45
46
|
- lib/ultra_settings/configuration.rb
|
47
|
+
- lib/ultra_settings/configuration_view.rb
|
46
48
|
- lib/ultra_settings/field.rb
|
47
49
|
- lib/ultra_settings/rack_app.rb
|
48
50
|
- lib/ultra_settings/railtie.rb
|
@@ -53,7 +55,10 @@ files:
|
|
53
55
|
homepage: https://github.com/bdurand/ultra_settings
|
54
56
|
licenses:
|
55
57
|
- MIT
|
56
|
-
metadata:
|
58
|
+
metadata:
|
59
|
+
homepage_uri: https://github.com/bdurand/ultra_settings
|
60
|
+
source_code_uri: https://github.com/bdurand/ultra_settings
|
61
|
+
changelog_uri: https://github.com/bdurand/ultra_settings/blob/main/CHANGELOG.md
|
57
62
|
post_install_message:
|
58
63
|
rdoc_options: []
|
59
64
|
require_paths:
|
@@ -69,9 +74,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
74
|
- !ruby/object:Gem::Version
|
70
75
|
version: '0'
|
71
76
|
requirements: []
|
72
|
-
rubygems_version: 3.4.
|
77
|
+
rubygems_version: 3.4.10
|
73
78
|
signing_key:
|
74
79
|
specification_version: 4
|
75
|
-
summary:
|
76
|
-
|
80
|
+
summary: UltraSettings is a Ruby gem that provides a flexible and documented approach
|
81
|
+
to managing application configurations from multiple sources, including environment
|
82
|
+
variables, runtime settings, and YAML files, with an optional web UI for easy documentation.
|
77
83
|
test_files: []
|
File without changes
|