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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f277e747e90903a62b41f8e79074dcd6d8790f160eda187cb03a89f8fdf5df40
4
- data.tar.gz: d6054c97b25ed971ec4601e522e5b7c6b1921a5d60d94e761b360b1f09bbc00c
3
+ metadata.gz: 6740e40d7198c0cb0573b24f5e61c0f679e8fb62b0c18bee3ee79a29748bc908
4
+ data.tar.gz: d35bb1603d4acfedcf32c8cc7c6063e0deeabb47e825d646ff104568a30de898
5
5
  SHA512:
6
- metadata.gz: cbf68fab75a43227bcddb7b0db63c42090910a89f55a264c9fd3e731a0dfc1b05cdb834acd549e8215c76cc0b049f57b500e2d1b85081ae75e70efd1eb8a3e53
7
- data.tar.gz: 13302c44130fa23473fddf517c768ef963ee505d9ddb2f71ad3bcd8d24261e2dfce562705490d553ef8b0883519485c615dca0c0c0dad1e47efbcf24ce2be49b
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
- This gem provides a method for managing application settings and loading their values from a variety of sources. It can help you get a handle on you application's configuration by providing a consistent method for accessing settings. It also provides a method for documenting your application's settings.
7
+ ## Introduction
7
8
 
8
- It allows you to define a hierarchy with three layers of sources for your configuration values:
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
- 1. Environment variables
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
- Settings at a higher level will override those set at a lower level. So, for instance, you can override values set in a YAML file with either environment variables or runtime settings. The hierarchy is an optional feature and can be disabled in favor of explicitly defined data sources if you want.
13
+ ## Key Features
15
14
 
16
- Your application code does not need to concern itself with how a setting value is being loaded or from what source. It can just reference configuration settings using plain old Ruby objects and methods.
15
+ This gem supports a three-layer hierarchy for defining configuration sources:
17
16
 
18
- Settings are also type cast so you can always be assured that values are returned as a predetermined class and your application does not need to worry about type coercion. The supported types are:
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
- You can also define default values to be returned in case the configured value is missing or it fails to match a constraint.
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. Configuration classes are [singleton classes](https://ruby-doc.org/3.2.2/stdlibs/singleton/Singleton.html).
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
- field :host, type: :string
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, type: :float, default: 1.0, default_if: ->(val) { val <= 0 }
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
- - You can specify a return type with the `:type` option. The value of the setting will be cast to this type. Valid types are:
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
- - You can specify a default value with the `:default` option. Note that this value will still be cast to the defined type.
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
- - You can specify a trigger of when the default should be used with the `:default_if` option. If this option is provided, then it should be either a `Proc` or the name of a method in the class to call with the value from the settings. You can use this feature, for example, to always ensure that a value meets certain constraints.
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
- - You can describe what your setting does with the `:description` option. This value is only for documentation purposes.
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
- - You can override the environment variable used to populate the setting with the `:env_var` option. You can use this to point to an environment variable name that does not match the conventional pattern. You can also set this to `false` to disable loading the field from an environment variable.
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
- - You can override the key in the YAML file with the `:yaml_key` option. You can use this to map a setting to a key in the YAML hash if the key doesn't match the field name. You can also set this to `false` to disable loading the field from a YAML file.
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
- - You can override the name of the runtime setting used to populate the setting with the `:runtime_setting` option. You can use this to point to a setting whose name does not match the conventional pattern. You can also set this to `false` to disable loading the field from runtime settings.
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
- - You can define a value as a static value by setting the `:static` option to true. Static values will not be changed once they are set. Static values also cannot be set from runtime settings. If you are referencing a setting during your application's initialization, then you should declare it as a static field.
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
- By default settings will be loaded from environment variables 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. By default environment variables will be in all uppercase letters.
117
+ #### Customizing Environment Variables
94
118
 
95
- You can use lowercase environment variable names by setting `env_var_upcase` to `false` on your configuration class.
119
+ You can customize the behavior of environment variable naming in several ways:
96
120
 
97
- You can use a different delimiter by setting `env_var_delimiter` on your configuration class. The delimiter is used between modules and before the setting name so a delimiter of "." on `Configs::MySettingsConfiguration#setting` would produce "CONFIGS.MY_SETTINGS.SETTING".
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
- You can set an explicit prefix by setting `env_var_prefix` on your configuration class.
123
+ - **Lowercase Environment Variables:** Set `env_var_upcase` to false in your configuration class to use lowercase environment variable names.
100
124
 
101
- You can disable environment variables as a default source on your fields by setting `environment_variables_disabled` to `true` on your configuration class.
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 settings that are loaded at runtime while your application is running. The advantage to this kind of setting is that your application does not need to restart in order to get an updated value.
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 use runtime settings, you need to set the `UltraSettings.runtime_settings` attribute to an object that defines the `[]` method and takes a string as the argument. For instance, if you wanted to load runtime settings from a Redis database, you could implement them like this.
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
- 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 itself.
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
- 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. By default runtime settings will be in all lowercase letters.
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 use uppercase runtime setting names by setting `runtime_setting_upcase` to `true` on your configuration class.
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
- You can use a different delimiter by setting `runtime_setting_delimiter` on your configuration class. The delimiter is used between modules and before the setting name so a delimiter of "/" on `Configs::MySettingsConfiguration#setting` would produce "configs/my_settings/setting".
171
+ - **Uppercase Runtime Setting Names:** Set `runtime_setting_upcase` to true in your configuration class to use uppercase runtime setting names.
136
172
 
137
- You can set an explicit prefix by setting `runtime_setting_prefix` on your configuration class.
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
- You can disable runtime settings as a default source on your fields by setting `runtime_settings_disabled` to `true` on your configuration class.
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
- The last place settings will be loaded from are from static YAML files. These can provide a good place to store default values for you application since they can be distributed with your application code.
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
- 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 will be searched for in the path defined by `UltraSettings.yaml_config_path`. If the file does not exist, then settings will not use the YAML source strategy.
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
- You can specify an explicit YAML file to use by setting `configuration_file` on your configuration class to the path to the file.
195
+ #### ERB Support
150
196
 
151
- You can disable YAML files as a default source on your fields by setting `yaml_config_disabled` to `true` on your configuration class.
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
- YAML files will be evaluated for an ERB markup (i.e. `<%= %>`) before the YAML itself is evaluated. You can use this feature to dynamically generate values within the YAML file.
199
+ #### Environment-Specific Configurations
154
200
 
155
- YAML files define environment specific configurations. YAML files must define a hash where the keys are the names of your application environments (i.e. development, test, production, etc.). You define which environment to use with `UltraSettings.yaml_config_env` (the default environment is "development"). There is also a special key `"shared"` which, if defined, will be merged with the environment hash.
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
- So, for this YAML file:
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 "development" environment would be the combination of development and shared:
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 "production", the values would be the combination of production and shared:
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
- Values for the environment will always overwrite values from the shared hash.
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 don't like the default behavior of the hierarchy of environment variables, runtime settings, and YAML files, you can disable it and then explicitly enable only the appropriate data source on each field.
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. Settings are accessed by calling methods.
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
- You can add configurations as methods onto the `UltraSettings` object. By default the configuration class name will be guessed (i.e. "my_service" maps to `MyServiceConfiguration`).
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
- You can also specify the class name to map to a different method name.
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 some syntactic sugar and expose the `UltraSettings` object as a helper method in application.rb.
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
- You can also keep things clean if your configuration is mostly accessed from within another class.
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
- There is a web UI available via a mountable Rack application. The UI will only expose the source of where settings are being loaded from. For security reasons, it will not show any of the setting values. It is still highly advised to put it behind whatever authorization framework you application uses.
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
- Here is a simple example of how to mount in a Rails application behind HTTP Basic authentication with hard coded credentials.
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
- ### Testing
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
- You can use the `UltraSettings.override!` method to force different configuration settings in you automated tests. Here's examples of overriding the `TestConfiguration#foo` value in a test block:
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
- If you are using RSpec, you can set up a global before handler to make it easier to specify settings within your test blocks.
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[:ultra_settings].is_a?(Hash)
319
- UltraSettings.override!(example.metadata[:ultra_settings]) do
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
- # In a test
329
- it 'has the settings I want', ultra_settings: {test: {foo: "bar"}} do
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.0.0
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-description {
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
- <% unless configuration.class.yaml_config_disabled? %>
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
- &#x2714;
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 ||= (self.class.load_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
@@ -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 [#[]] The environment variables.
48
- # @param settings [#[]] The runtime settings.
49
- # @param yaml_config [#[]] The YAML configuration.
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
@@ -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 ||= nil
166
+ @runtime_settings
164
167
  end
165
168
 
166
- # Explicitly set setting values within a block. This is useful for testing
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.
@@ -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 = "Unified application configuration that allows for configuration via environment variables, YAML files, and dynamic runtime setting."
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.0.0
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: 2023-09-30 00:00:00.000000000 Z
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.12
77
+ rubygems_version: 3.4.10
73
78
  signing_key:
74
79
  specification_version: 4
75
- summary: Unified application configuration that allows for configuration via environment
76
- variables, YAML files, and dynamic runtime setting.
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