ultra_settings 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ARCHITECTURE.md +186 -0
- data/CHANGELOG.md +10 -0
- data/README.md +12 -4
- data/VERSION +1 -1
- data/app/application.css +193 -20
- data/app/application_vars.css.erb +73 -14
- data/app/configuration.html.erb +101 -130
- data/app/index.html.erb +16 -0
- data/app/layout.html.erb +1 -2
- data/lib/ultra_settings/application_view.rb +24 -28
- data/lib/ultra_settings/configuration_view.rb +6 -25
- data/lib/ultra_settings/railtie.rb +0 -1
- data/lib/ultra_settings/view_helper.rb +22 -0
- data/lib/ultra_settings/web_view.rb +8 -27
- data/lib/ultra_settings.rb +4 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4682a4cd5e85642db5e79cfbb156109d6701b5ad09f6744a0b092bdfc3d9277c
|
4
|
+
data.tar.gz: 8c4a0d4350ce43b7ce4b1c0187bf80c27e2d33261d9bd349ce9155d8fb80a069
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00c9249324a0c6752dd1449d7b94101050e55aa1d27ba0c47cb3527bb0e8c5909233a64b98a0a0674528acae2413c5578c0097fabe9aaeda6a5b78c47a1854d7
|
7
|
+
data.tar.gz: a46516c99004973b58496e44c38c375b6698be941aba85fd2e3c0bf08db032c8b9c71ce371aef051bd8649419e5dc6752be0e1f259c2019ce3baa270c8b52460
|
data/ARCHITECTURE.md
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
# UltraSettings Configuration Architecture
|
2
|
+
|
3
|
+
This document describes the architecture of the UltraSettings Configuration and Field classes, which form the core of the configuration management system.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
The UltraSettings library provides a flexible configuration management system built around two primary classes:
|
8
|
+
|
9
|
+
- **Configuration**: A singleton-based configuration manager that supports multiple data sources
|
10
|
+
- **Field**: A field definition that encapsulates metadata and value resolution logic
|
11
|
+
|
12
|
+
## Architecture Diagram
|
13
|
+
|
14
|
+
```mermaid
|
15
|
+
classDiagram
|
16
|
+
class Configuration {
|
17
|
+
<<Singleton>>
|
18
|
+
+ALLOWED_NAME_PATTERN : Regex
|
19
|
+
+ALLOWED_TYPES : Array~Symbol~
|
20
|
+
-@env_var_prefix : String
|
21
|
+
-@runtime_setting_prefix : String
|
22
|
+
-@ultra_settings_mutex : Mutex
|
23
|
+
-@ultra_settings_memoized_values : Hash
|
24
|
+
-@ultra_settings_override_values : Hash
|
25
|
+
-@ultra_settings_yaml_config : Hash
|
26
|
+
|
27
|
+
+field(name, **options) void
|
28
|
+
+fields() Array~Field~
|
29
|
+
+include_field?(name) Boolean
|
30
|
+
+env_var_prefix=(value) void
|
31
|
+
+env_var_prefix() String
|
32
|
+
+runtime_setting_prefix=(value) void
|
33
|
+
+runtime_setting_prefix() String
|
34
|
+
+configuration_file=(value) void
|
35
|
+
+configuration_file() Pathname
|
36
|
+
+override!(values, &block) Object
|
37
|
+
+load_yaml_config() Hash
|
38
|
+
+[](name) Object
|
39
|
+
+include?(name) Boolean
|
40
|
+
+__source__(name) Symbol
|
41
|
+
+__value_from_source__(name, source) Object
|
42
|
+
+__to_hash__() Hash
|
43
|
+
-__get_value__(name) Object
|
44
|
+
-__use_default?(value, default_if) Boolean
|
45
|
+
-__yaml_config__() Hash
|
46
|
+
-defined_fields() Hash~String, Field~
|
47
|
+
-root_name() String
|
48
|
+
-construct_env_var(name, env_var) String
|
49
|
+
-construct_runtime_setting(name, runtime_setting) String
|
50
|
+
-construct_yaml_key(name, yaml_key) String
|
51
|
+
}
|
52
|
+
|
53
|
+
class Field {
|
54
|
+
+name : String
|
55
|
+
+type : Symbol
|
56
|
+
+description : String
|
57
|
+
+default : Object
|
58
|
+
+default_if : Proc|Symbol
|
59
|
+
+env_var : String
|
60
|
+
+runtime_setting : String
|
61
|
+
+yaml_key : String
|
62
|
+
-@static : Boolean
|
63
|
+
-@secret : Boolean|Proc
|
64
|
+
|
65
|
+
+initialize(**options) void
|
66
|
+
+value(env, settings, yaml_config) Object
|
67
|
+
+source(env, settings, yaml_config) Symbol
|
68
|
+
+coerce(value) Object
|
69
|
+
+static?() Boolean
|
70
|
+
+secret?() Boolean
|
71
|
+
-fetch_value_and_source(**sources) Array
|
72
|
+
-runtime_setting_value(settings) Object
|
73
|
+
-yaml_value(yaml_config) Object
|
74
|
+
}
|
75
|
+
|
76
|
+
class Coerce {
|
77
|
+
<<Utility>>
|
78
|
+
+coerce_value(value, type) Object
|
79
|
+
}
|
80
|
+
|
81
|
+
Configuration "1" --> "*" Field : manages
|
82
|
+
Field --> Coerce : uses
|
83
|
+
Configuration --> Field : creates via field()
|
84
|
+
|
85
|
+
note for Configuration "Singleton pattern ensures single instance per class.\nSupports inheritance for configuration hierarchies.\nThread-safe with mutex protection."
|
86
|
+
|
87
|
+
note for Field "Immutable once created.\nSupports multiple data sources with precedence:\n1. Environment Variables\n2. Runtime Settings\n3. YAML Configuration\n4. Default Values"
|
88
|
+
```
|
89
|
+
|
90
|
+
## Key Components
|
91
|
+
|
92
|
+
### Configuration Class
|
93
|
+
|
94
|
+
The `Configuration` class serves as the primary interface for defining and accessing configuration values. Key characteristics:
|
95
|
+
|
96
|
+
#### Singleton Pattern
|
97
|
+
- Uses Ruby's `Singleton` module to ensure one instance per configuration class
|
98
|
+
- Supports inheritance, allowing configuration subclasses to have their own singleton instances
|
99
|
+
|
100
|
+
#### Field Definition
|
101
|
+
- **`field(name, **options)`**: DSL method for defining configuration fields
|
102
|
+
- Dynamically creates getter methods using `class_eval`
|
103
|
+
- Supports various field types: `:string`, `:symbol`, `:integer`, `:float`, `:boolean`, `:datetime`, `:array`
|
104
|
+
- Validates field names against `ALLOWED_NAME_PATTERN`
|
105
|
+
|
106
|
+
#### Multi-Source Value Resolution
|
107
|
+
Fields can pull values from multiple sources in order of precedence:
|
108
|
+
1. **Environment Variables** - Highest priority
|
109
|
+
2. **Runtime Settings** - Dynamic configuration
|
110
|
+
3. **YAML Files** - File-based configuration
|
111
|
+
4. **Default Values** - Fallback values
|
112
|
+
|
113
|
+
#### Thread Safety
|
114
|
+
- Uses `Mutex` for thread-safe operations
|
115
|
+
- Protects memoized values and override values from race conditions
|
116
|
+
|
117
|
+
#### Value Overrides
|
118
|
+
- **`override!(values, &block)`**: Temporarily override field values within a block
|
119
|
+
- Thread-local overrides allow different values per thread
|
120
|
+
|
121
|
+
### Field Class
|
122
|
+
|
123
|
+
The `Field` class encapsulates the metadata and behavior for individual configuration fields:
|
124
|
+
|
125
|
+
#### Immutable Design
|
126
|
+
- All attributes are frozen upon initialization
|
127
|
+
- Ensures consistent behavior throughout the application lifecycle
|
128
|
+
|
129
|
+
#### Value Resolution Logic
|
130
|
+
- **`value(env:, settings:, yaml_config:)`**: Resolves field value from available sources
|
131
|
+
- **`source(env:, settings:, yaml_config:)`**: Identifies which source provided the value
|
132
|
+
- **`fetch_value_and_source()`**: Core resolution algorithm with source precedence
|
133
|
+
|
134
|
+
#### Type Coercion
|
135
|
+
- **`coerce(value)`**: Converts string values to appropriate types
|
136
|
+
- Delegates to `Coerce` utility class for type-specific conversion logic
|
137
|
+
|
138
|
+
#### Security Features
|
139
|
+
- **`secret?`**: Marks fields containing sensitive data
|
140
|
+
- Supports both boolean and callable (lambda) secret detection
|
141
|
+
- Secret fields are masked in serialization methods
|
142
|
+
|
143
|
+
#### Special Behaviors
|
144
|
+
- **Static Fields**: Values cached and never change after first resolution
|
145
|
+
- **Default Conditions**: Custom logic for when to use default values via `default_if`
|
146
|
+
|
147
|
+
## Data Flow
|
148
|
+
|
149
|
+
1. **Field Definition**: Developer calls `Configuration.field()` to define a configuration field
|
150
|
+
2. **Field Creation**: A new `Field` instance is created and stored in the configuration class
|
151
|
+
3. **Dynamic Method**: A getter method is dynamically created using `class_eval`
|
152
|
+
4. **Value Access**: When the getter is called, it delegates to `__get_value__`
|
153
|
+
5. **Source Resolution**: The field's `value()` method checks sources in precedence order
|
154
|
+
6. **Type Coercion**: Raw string values are coerced to the appropriate type
|
155
|
+
7. **Caching**: Static fields are memoized; others may use override values
|
156
|
+
|
157
|
+
## Key Design Patterns
|
158
|
+
|
159
|
+
### Factory Pattern
|
160
|
+
The `field()` method acts as a factory for creating `Field` instances with appropriate defaults and validations.
|
161
|
+
|
162
|
+
### Strategy Pattern
|
163
|
+
Different data sources (environment, runtime settings, YAML) are handled through a consistent interface but with source-specific logic.
|
164
|
+
|
165
|
+
### Template Method
|
166
|
+
The value resolution follows a template where the algorithm is defined in `Field#fetch_value_and_source`, but source-specific retrieval is delegated to private methods.
|
167
|
+
|
168
|
+
### Decorator Pattern
|
169
|
+
Field overrides decorate the normal value resolution with temporary alternative values.
|
170
|
+
|
171
|
+
## Inheritance Support
|
172
|
+
|
173
|
+
Configuration classes support inheritance:
|
174
|
+
- Subclasses inherit field definitions from parent classes
|
175
|
+
- Each class maintains its own singleton instance
|
176
|
+
- Prefixes and settings can be overridden per class
|
177
|
+
- Field definitions are merged from parent to child
|
178
|
+
|
179
|
+
## Thread Safety Considerations
|
180
|
+
|
181
|
+
- **Mutex Protection**: Critical sections are protected with mutex locks
|
182
|
+
- **Thread-Local Overrides**: Override values are stored per thread ID
|
183
|
+
- **Immutable Fields**: Field definitions are immutable after creation
|
184
|
+
- **Atomic Operations**: Value resolution is designed to be atomic
|
185
|
+
|
186
|
+
This architecture provides a robust, flexible, and thread-safe configuration management system that can adapt to various deployment environments and configuration sources.
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,16 @@ 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
|
+
## 2.6.0
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Added support for passing the type in `UltraSettings.runtime_settings_url` as `${type}` in the URL.
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
|
15
|
+
- Significantly updated the web UI to improve the layout and usability. The UI is no longer an HTML table and has a cleaner, more modern design.
|
16
|
+
|
7
17
|
## 2.5.0
|
8
18
|
|
9
19
|
### Added
|
data/README.md
CHANGED
@@ -211,7 +211,7 @@ You can customize the behavior of runtime setting names with the following optio
|
|
211
211
|
|
212
212
|
- **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`.
|
213
213
|
|
214
|
-
- **Editing Links** You can specify a URL for editing runtime settings from the web UI by setting `UltraSettings.runtime_settings_url` to the desired URL. This will add links to the runtime settings in the web UI. You can use the
|
214
|
+
- **Editing Links** You can specify a URL for editing runtime settings from the web UI by setting `UltraSettings.runtime_settings_url` to the desired URL. This will add links to the runtime settings in the web UI. You can use the placeholders `${name}` and `${type}` in the URL which will be replaced with the name and type of the runtime setting, respectively. If you are using the `super_settings` gem for runtime settings, then you can target a setting by adding `#edit=${name}&type=${type}` to the root URL where `super_settings` is mounted.
|
215
215
|
|
216
216
|
If a setting value cannot be loaded from the runtime settings, then it's value will attempt to be loaded from a YAML file.
|
217
217
|
|
@@ -422,17 +422,25 @@ If you prefer to embed the settings view directly into your own admin tools or d
|
|
422
422
|
```erb
|
423
423
|
<h1>Configuration</h1>
|
424
424
|
|
425
|
-
<%= UltraSettings::ApplicationView.new.render(select_class: "form-select"
|
425
|
+
<%= UltraSettings::ApplicationView.new.render(select_class: "form-select") %>
|
426
426
|
```
|
427
427
|
|
428
|
-
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
|
428
|
+
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 with navigation handled by an HTML select element. You can specify the CSS classes for the select element to match your own application styles..
|
429
429
|
|
430
430
|
You can also embed the view for individual configurations within your own views using the `UltraSettings::ConfigurationView` class if you want more customization:
|
431
431
|
|
432
432
|
```erb
|
433
433
|
<h1>My Service Settings</h1>
|
434
434
|
|
435
|
-
<%= UltraSettings::ConfigurationView.new(MyServiceConfiguration.instance).render
|
435
|
+
<%= UltraSettings::ConfigurationView.new(MyServiceConfiguration.instance).render %>
|
436
|
+
```
|
437
|
+
|
438
|
+
You'll also need to include the CSS for the configuration view on your page.
|
439
|
+
|
440
|
+
```erb
|
441
|
+
<head>
|
442
|
+
<%= UltraSettings::ApplicationView.new.style_tag %>
|
443
|
+
</head>
|
436
444
|
```
|
437
445
|
|
438
446
|
### Testing With UltraSettings
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.6.0
|
data/app/application.css
CHANGED
@@ -6,41 +6,214 @@
|
|
6
6
|
margin: 0;
|
7
7
|
}
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
/* Configuration file header */
|
10
|
+
.ultra-settings-config-file {
|
11
|
+
margin-bottom: 1.5rem;
|
12
|
+
padding: 1rem;
|
13
|
+
background-color: var(--config-file-bg-color);
|
14
|
+
border: 1px solid var(--config-file-border-color);
|
15
|
+
border-radius: 0.375rem;
|
16
|
+
}
|
17
|
+
|
18
|
+
.ultra-settings-config-file-label {
|
19
|
+
font-weight: 600;
|
20
|
+
color: var(--text-color);
|
21
|
+
margin-right: 0.5rem;
|
22
|
+
}
|
23
|
+
|
24
|
+
.ultra-settings-config-file-path {
|
25
|
+
font-family: monospace;
|
26
|
+
font-size: 0.9rem;
|
27
|
+
color: var(--code-color);
|
28
|
+
font-weight: 600;
|
29
|
+
}
|
30
|
+
|
31
|
+
.ultra-settings-file-not-found {
|
32
|
+
color: var(--warning-color);
|
33
|
+
font-style: italic;
|
34
|
+
margin-left: 0.5rem;
|
35
|
+
}
|
36
|
+
|
37
|
+
/* Fields container */
|
38
|
+
.ultra-settings-fields {
|
39
|
+
display: flex;
|
40
|
+
flex-direction: column;
|
41
|
+
gap: 1rem;
|
14
42
|
}
|
15
43
|
|
16
|
-
|
17
|
-
|
18
|
-
border
|
19
|
-
|
44
|
+
/* Individual field card */
|
45
|
+
.ultra-settings-field {
|
46
|
+
border: 1px solid var(--field-border-color);
|
47
|
+
border-radius: 0.5rem;
|
48
|
+
background-color: var(--field-bg-color);
|
49
|
+
overflow: hidden;
|
20
50
|
}
|
21
51
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
52
|
+
/* Field header */
|
53
|
+
.ultra-settings-field-header {
|
54
|
+
display: flex;
|
55
|
+
justify-content: space-between;
|
56
|
+
align-items: center;
|
57
|
+
padding: 0.75rem 1rem;
|
58
|
+
background-color: var(--field-header-bg-color);
|
59
|
+
border-bottom: 1px solid var(--field-border-color);
|
26
60
|
}
|
27
61
|
|
28
|
-
.ultra-settings-
|
29
|
-
|
62
|
+
.ultra-settings-field-name {
|
63
|
+
display: flex;
|
64
|
+
align-items: center;
|
65
|
+
gap: 0.5rem;
|
30
66
|
}
|
31
67
|
|
32
|
-
.ultra-settings-
|
68
|
+
.ultra-settings-field-name code {
|
33
69
|
font-family: monospace;
|
34
|
-
font-size:
|
35
|
-
|
70
|
+
font-size: 1rem;
|
71
|
+
font-weight: 600;
|
36
72
|
color: var(--code-color);
|
73
|
+
}
|
74
|
+
|
75
|
+
.ultra-settings-field-type {
|
76
|
+
font-size: 0.875rem;
|
37
77
|
font-weight: 600;
|
78
|
+
color: var(--type-color);
|
79
|
+
text-transform: uppercase;
|
80
|
+
letter-spacing: 0.025em;
|
81
|
+
}
|
82
|
+
|
83
|
+
/* Field badges */
|
84
|
+
.ultra-settings-field-badge {
|
85
|
+
display: inline-block;
|
86
|
+
padding: 0.125rem 0.5rem;
|
87
|
+
font-size: 0.75rem;
|
88
|
+
font-weight: 500;
|
89
|
+
text-transform: uppercase;
|
90
|
+
letter-spacing: 0.025em;
|
91
|
+
border-radius: 0.25rem;
|
92
|
+
}
|
93
|
+
|
94
|
+
.ultra-settings-badge-secret {
|
95
|
+
background-color: var(--secret-badge-bg-color);
|
96
|
+
color: var(--secret-badge-text-color);
|
97
|
+
}
|
98
|
+
|
99
|
+
.ultra-settings-badge-static {
|
100
|
+
background-color: var(--static-badge-bg-color);
|
101
|
+
color: var(--static-badge-text-color);
|
102
|
+
}
|
103
|
+
|
104
|
+
/* Field value */
|
105
|
+
.ultra-settings-field-value {
|
106
|
+
padding: 1rem;
|
107
|
+
background-color: var(--value-bg-color);
|
108
|
+
border-bottom: 1px solid var(--field-border-color);
|
109
|
+
}
|
110
|
+
|
111
|
+
.ultra-settings-field-value code {
|
112
|
+
font-family: monospace;
|
113
|
+
font-size: 0.95rem;
|
114
|
+
word-break: break-all;
|
115
|
+
color: var(--value-text-color);
|
116
|
+
background-color: var(--value-code-bg-color);
|
117
|
+
padding: 0.25rem 0.5rem;
|
118
|
+
border-radius: 0.25rem;
|
119
|
+
border: 1px solid var(--value-code-border-color);
|
38
120
|
}
|
39
121
|
|
40
|
-
.ultra-settings-
|
41
|
-
color: var(--em-color);
|
122
|
+
.ultra-settings-nil-value {
|
42
123
|
font-style: italic;
|
124
|
+
color: var(--nil-color);
|
125
|
+
font-size: 0.9rem;
|
126
|
+
}
|
127
|
+
|
128
|
+
/* Field description */
|
129
|
+
.ultra-settings-field-description {
|
130
|
+
padding: 0.75rem 1rem;
|
131
|
+
color: var(--description-color);
|
43
132
|
font-size: 0.9rem;
|
133
|
+
line-height: 1.5;
|
134
|
+
border-bottom: 1px solid var(--field-border-color);
|
135
|
+
display: flex;
|
136
|
+
align-items: flex-start;
|
137
|
+
gap: 0.5rem;
|
138
|
+
}
|
139
|
+
|
140
|
+
.ultra-settings-description-text {
|
141
|
+
flex: 1;
|
142
|
+
}
|
143
|
+
|
144
|
+
.ultra-settings-info-icon {
|
145
|
+
width: 16px;
|
146
|
+
height: 16px;
|
147
|
+
flex-shrink: 0;
|
148
|
+
margin-top: 0.125rem; /* Align with first line of text based on line-height */
|
149
|
+
color: var(--description-color);
|
150
|
+
}
|
151
|
+
|
152
|
+
/* Sources section */
|
153
|
+
.ultra-settings-field-sources {
|
154
|
+
padding: 0.5rem;
|
155
|
+
}
|
156
|
+
|
157
|
+
.ultra-settings-source {
|
158
|
+
display: flex;
|
159
|
+
align-items: center;
|
160
|
+
justify-content: space-between;
|
161
|
+
padding: 0.5rem 0.75rem;
|
162
|
+
margin-bottom: 0.25rem;
|
163
|
+
border-radius: 0.25rem;
|
164
|
+
background-color: var(--source-bg-color);
|
165
|
+
border: 1px solid var(--source-border-color);
|
166
|
+
transition: all 0.2s ease;
|
167
|
+
}
|
168
|
+
|
169
|
+
.ultra-settings-source-active {
|
170
|
+
background-color: var(--source-active-bg-color);
|
171
|
+
border-color: var(--source-active-border-color);
|
172
|
+
}
|
173
|
+
|
174
|
+
.ultra-settings-source-type {
|
175
|
+
font-size: 0.8rem;
|
176
|
+
font-weight: 500;
|
177
|
+
color: var(--source-type-color);
|
178
|
+
text-transform: uppercase;
|
179
|
+
letter-spacing: 0.025em;
|
180
|
+
min-width: 120px;
|
181
|
+
}
|
182
|
+
|
183
|
+
.ultra-settings-source-value {
|
184
|
+
font-family: monospace;
|
185
|
+
font-size: 0.85rem;
|
186
|
+
color: var(--source-value-color);
|
187
|
+
flex: 1;
|
188
|
+
margin: 0 0.75rem;
|
189
|
+
word-break: break-all;
|
190
|
+
}
|
191
|
+
|
192
|
+
.ultra-settings-source-indicator {
|
193
|
+
font-size: 0.75rem;
|
194
|
+
font-weight: 600;
|
195
|
+
color: var(--source-indicator-color);
|
196
|
+
text-transform: uppercase;
|
197
|
+
letter-spacing: 0.025em;
|
198
|
+
}
|
199
|
+
|
200
|
+
.ultra-settings-edit-link {
|
201
|
+
color: var(--edit-link-color);
|
202
|
+
text-decoration: none;
|
203
|
+
margin-left: 0.5rem;
|
204
|
+
opacity: 0.7;
|
205
|
+
transition: opacity 0.2s ease;
|
206
|
+
display: inline-flex;
|
207
|
+
align-items: center;
|
208
|
+
}
|
209
|
+
|
210
|
+
.ultra-settings-edit-link:hover {
|
211
|
+
opacity: 1;
|
212
|
+
}
|
213
|
+
|
214
|
+
.ultra-settings-edit-icon {
|
215
|
+
width: 16px;
|
216
|
+
height: 16px;
|
44
217
|
}
|
45
218
|
|
46
219
|
.ultra-settings-select {
|
@@ -1,13 +1,42 @@
|
|
1
1
|
<% unless color_scheme == :dark %>
|
2
|
-
.ultra-settings {
|
3
|
-
--
|
4
|
-
--table-border-color: #dee2e6;
|
5
|
-
--alt-row-color: rgba(0, 0, 0, .05);
|
6
|
-
--form-control-color: #495057;
|
2
|
+
.ultra-settings, .ultra-settings-block {
|
3
|
+
--form-control-color: #484848;
|
7
4
|
--form-control-bg-color: #fff;
|
8
|
-
--form-control-border-color: #
|
9
|
-
--code-color:
|
10
|
-
|
5
|
+
--form-control-border-color: #e8e8e8;
|
6
|
+
--code-color: #d63384;
|
7
|
+
|
8
|
+
/* Card layout colors */
|
9
|
+
--config-file-bg-color: #f8f8f8;
|
10
|
+
--config-file-border-color: #e8e8e8;
|
11
|
+
--field-bg-color: #ffffff;
|
12
|
+
--field-border-color: #b8b8b8;
|
13
|
+
--field-header-bg-color: #f8f8f8;
|
14
|
+
--value-bg-color: #fdfdfe;
|
15
|
+
--value-text-color: #212529;
|
16
|
+
--value-code-bg-color: #f8f8f8;
|
17
|
+
--value-code-border-color: #e8e8e8;
|
18
|
+
--type-color: #6c757d;
|
19
|
+
--description-color: #4a83b5;
|
20
|
+
--nil-color: #6c757d;
|
21
|
+
--warning-color: #dc3545;
|
22
|
+
|
23
|
+
/* Source colors */
|
24
|
+
--source-bg-color: #f8f9fa;
|
25
|
+
--source-border-color: #e9ecef;
|
26
|
+
--source-active-bg-color: #e7f3ff;
|
27
|
+
--source-active-border-color: #0d6efd;
|
28
|
+
--source-type-color: #666666;
|
29
|
+
--source-value-color: #666666;
|
30
|
+
--source-indicator-color: #0d6efd;
|
31
|
+
|
32
|
+
/* Badge colors */
|
33
|
+
--secret-badge-bg-color: #dc3545;
|
34
|
+
--secret-badge-text-color: #ffffff;
|
35
|
+
--static-badge-bg-color: #888888;
|
36
|
+
--static-badge-text-color: #ffffff;
|
37
|
+
|
38
|
+
/* Edit link */
|
39
|
+
--edit-link-color: #0d6efd;
|
11
40
|
}
|
12
41
|
<% end %>
|
13
42
|
|
@@ -15,15 +44,45 @@
|
|
15
44
|
@media (prefers-color-scheme: dark) {
|
16
45
|
<% end %>
|
17
46
|
<% if color_scheme == :system || color_scheme == :dark %>
|
18
|
-
.ultra-settings {
|
19
|
-
--table-header-bg-color: #333;
|
20
|
-
--table-border-color: #555;
|
21
|
-
--alt-row-color: rgba(0, 0, 0, .30);
|
47
|
+
.ultra-settings, .ultra-settings-block {
|
22
48
|
--form-control-color: #eee;
|
23
49
|
--form-control-bg-color: #666;
|
24
50
|
--form-control-border-color: #555;
|
25
|
-
--code-color:
|
26
|
-
--em-color: #
|
51
|
+
--code-color: #ff6b9d;
|
52
|
+
--em-color: #adb5bd;
|
53
|
+
|
54
|
+
/* Card layout colors */
|
55
|
+
--config-file-bg-color: #2b2b2b;
|
56
|
+
--config-file-border-color: #444;
|
57
|
+
--field-bg-color: #1e1e1e;
|
58
|
+
--field-border-color: #444;
|
59
|
+
--field-header-bg-color: #2b2b2b;
|
60
|
+
--value-bg-color: #252525;
|
61
|
+
--value-text-color: #e9ecef;
|
62
|
+
--value-code-bg-color: #2b2b2b;
|
63
|
+
--value-code-border-color: #444;
|
64
|
+
--type-color: #adb5bd;
|
65
|
+
--description-color: #a9d9f8;
|
66
|
+
--nil-color: #cdcecfff;
|
67
|
+
--warning-color: #dc3545;
|
68
|
+
|
69
|
+
/* Source colors */
|
70
|
+
--source-bg-color: #2b2b2b;
|
71
|
+
--source-border-color: #444444;
|
72
|
+
--source-active-bg-color: #1a3a52;
|
73
|
+
--source-active-border-color: #0d6efd;
|
74
|
+
--source-type-color: #adb5bd;
|
75
|
+
--source-value-color: #ced4da;
|
76
|
+
--source-indicator-color: #6ea8fe;
|
77
|
+
|
78
|
+
/* Badge colors */
|
79
|
+
--secret-badge-bg-color: #dc3545;
|
80
|
+
--secret-badge-text-color: #ffffff;
|
81
|
+
--static-badge-bg-color: #6c757d;
|
82
|
+
--static-badge-text-color: #ffffff;
|
83
|
+
|
84
|
+
/* Edit link */
|
85
|
+
--edit-link-color: #a7c6f5;
|
27
86
|
}
|
28
87
|
<% end %>
|
29
88
|
<% if color_scheme == :system %>
|
data/app/configuration.html.erb
CHANGED
@@ -1,141 +1,112 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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>
|
1
|
+
<div class="ultra-settings-block">
|
2
|
+
<% if !configuration.class.yaml_config_disabled? && configuration.class.configuration_file.is_a?(Pathname) %>
|
3
|
+
<div class="ultra-settings-config-file">
|
4
|
+
<span class="ultra-settings-config-file-label">Configuration File:</span>
|
5
|
+
<code class="ultra-settings-config-file-path"><%= html_escape(relative_path(configuration.class.configuration_file)) %></code>
|
6
|
+
<% unless configuration.class.configuration_file&.exist? %>
|
7
|
+
<span class="ultra-settings-file-not-found">(File does not exist)</span>
|
15
8
|
<% end %>
|
16
|
-
|
17
|
-
|
18
|
-
<th>Value</th>
|
19
|
-
<th>Type</th>
|
20
|
-
<th>Notes</th>
|
21
|
-
</tr>
|
22
|
-
</thead>
|
23
|
-
<tbody translate="no">
|
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>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
30
11
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
12
|
+
<div class="ultra-settings-fields">
|
13
|
+
<% configuration.class.fields.each do |field| %>
|
14
|
+
<% source = configuration.__source__(field.name) %>
|
15
|
+
<div class="ultra-settings-field">
|
16
|
+
<div class="ultra-settings-field-header">
|
17
|
+
<div class="ultra-settings-field-name">
|
18
|
+
<code><%= html_escape(field.name) %></code>
|
19
|
+
<% if field.secret? %>
|
20
|
+
<span class="ultra-settings-field-badge ultra-settings-badge-secret">secret</span>
|
38
21
|
<% end %>
|
39
|
-
|
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>
|
22
|
+
<% if field.static? %>
|
23
|
+
<span class="ultra-settings-field-badge ultra-settings-badge-static">static</span>
|
52
24
|
<% end %>
|
53
|
-
</
|
25
|
+
</div>
|
26
|
+
<div class="ultra-settings-field-type">
|
27
|
+
<%= html_escape(field.type) %>
|
28
|
+
</div>
|
29
|
+
</div>
|
54
30
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
31
|
+
<div class="ultra-settings-field-value">
|
32
|
+
<% if configuration[field.name].nil? %>
|
33
|
+
<span class="ultra-settings-nil-value">nil</span>
|
34
|
+
<% elsif field.secret? %>
|
35
|
+
<code><%= html_escape(secret_value(configuration[field.name])) %></code>
|
36
|
+
<% else %>
|
37
|
+
<code><%= html_escape(display_value(configuration[field.name])) %></code>
|
38
|
+
<% end %>
|
39
|
+
</div>
|
40
|
+
|
41
|
+
<% unless field.description.to_s.empty? %>
|
42
|
+
<div class="ultra-settings-field-description">
|
43
|
+
<svg class="ultra-settings-info-icon" width="16" height="16" fill="currentColor">
|
44
|
+
<use href="#info-icon"/>
|
45
|
+
</svg>
|
46
|
+
<div class="ultra-settings-description-text">
|
47
|
+
<%= html_escape(field.description) %>
|
48
|
+
</div>
|
49
|
+
</div>
|
50
|
+
<% end %>
|
61
51
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
Can be
|
70
|
-
<% end %>
|
71
|
-
set with the environment variable
|
72
|
-
<code><%= show_defined_value(field.env_var, configuration.__value_from_source__(field.name, :env), field.secret?) %></code>
|
73
|
-
<% if source == :env %>
|
74
|
-
</strong>
|
75
|
-
<% end %>
|
76
|
-
</li>
|
52
|
+
<div class="ultra-settings-field-sources">
|
53
|
+
<% if field.env_var && !configuration.class.environment_variables_disabled? %>
|
54
|
+
<div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :env %>">
|
55
|
+
<span class="ultra-settings-source-type">Environment Variable</span>
|
56
|
+
<code class="ultra-settings-source-value"><%= show_defined_value(field.env_var, configuration.__value_from_source__(field.name, :env), field.secret?) %></code>
|
57
|
+
<% if source == :env %>
|
58
|
+
<span class="ultra-settings-source-indicator">Currently active</span>
|
77
59
|
<% end %>
|
78
|
-
|
79
|
-
|
80
|
-
<% if source == :settings %>
|
81
|
-
<strong>
|
82
|
-
Currently
|
83
|
-
<% else %>
|
84
|
-
Can be
|
85
|
-
<% end %>
|
86
|
-
set with the runtime setting
|
87
|
-
<code><%= show_defined_value(field.runtime_setting, configuration.__value_from_source__(field.name, :settings), field.secret?) %></code>
|
88
|
-
<% if source == :settings %>
|
89
|
-
</strong>
|
90
|
-
<% end %>
|
60
|
+
</div>
|
61
|
+
<% end %>
|
91
62
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
</svg>
|
99
|
-
</a>
|
100
|
-
<% end %>
|
101
|
-
</li>
|
63
|
+
<% if field.runtime_setting && !configuration.class.runtime_settings_disabled? %>
|
64
|
+
<div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :settings %>">
|
65
|
+
<span class="ultra-settings-source-type">Runtime Setting</span>
|
66
|
+
<code class="ultra-settings-source-value"><%= show_defined_value(field.runtime_setting, configuration.__value_from_source__(field.name, :settings), field.secret?) %></code>
|
67
|
+
<% if source == :settings %>
|
68
|
+
<span class="ultra-settings-source-indicator">Currently active</span>
|
102
69
|
<% end %>
|
103
|
-
<%
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
<% end %>
|
111
|
-
set with the configuration file key
|
112
|
-
<code><%= show_defined_value(field.yaml_key, configuration.__value_from_source__(field.name, :yaml), field.secret?) %></code>
|
113
|
-
<% if source == :yaml %>
|
114
|
-
</strong>
|
115
|
-
<% end %>
|
116
|
-
</li>
|
70
|
+
<% edit_url = UltraSettings.runtime_settings_url(field.runtime_setting, field.type) %>
|
71
|
+
<% if edit_url %>
|
72
|
+
<a href="<%= html_escape(edit_url) %>" class="ultra-settings-edit-link" title="Edit <%= html_escape(field.runtime_setting) %>">
|
73
|
+
<svg class="ultra-settings-edit-icon" width="16" height="16" fill="currentColor">
|
74
|
+
<use href="#edit-icon"/>
|
75
|
+
</svg>
|
76
|
+
</a>
|
117
77
|
<% end %>
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
<strong>
|
128
|
-
Currently set with the
|
129
|
-
<%= show_defined_value("default value", field.default, field.secret?) %>.
|
130
|
-
</strong>
|
131
|
-
<% else %>
|
132
|
-
This field has a <%= show_defined_value("default value", field.default, field.secret?) %>.
|
133
|
-
<% end %>
|
134
|
-
</li>
|
78
|
+
</div>
|
79
|
+
<% end %>
|
80
|
+
|
81
|
+
<% if field.yaml_key && !configuration.class.yaml_config_disabled? %>
|
82
|
+
<div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :yaml %>">
|
83
|
+
<span class="ultra-settings-source-type">Configuration File</span>
|
84
|
+
<code class="ultra-settings-source-value"><%= show_defined_value(field.yaml_key, configuration.__value_from_source__(field.name, :yaml), field.secret?) %></code>
|
85
|
+
<% if source == :yaml %>
|
86
|
+
<span class="ultra-settings-source-indicator">Currently active</span>
|
135
87
|
<% end %>
|
136
|
-
</
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
88
|
+
</div>
|
89
|
+
<% end %>
|
90
|
+
|
91
|
+
<% if field.default.nil? %>
|
92
|
+
<% if source == :default %>
|
93
|
+
<div class="ultra-settings-source ultra-settings-source-active">
|
94
|
+
<span class="ultra-settings-source-type">Default Value</span>
|
95
|
+
<span class="ultra-settings-source-value"><em>Not set</em></span>
|
96
|
+
<span class="ultra-settings-source-indicator">Currently active</span>
|
97
|
+
</div>
|
98
|
+
<% end %>
|
99
|
+
<% else %>
|
100
|
+
<div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :default %>">
|
101
|
+
<span class="ultra-settings-source-type">Default Value</span>
|
102
|
+
<code class="ultra-settings-source-value"><%= show_defined_value("", field.default, field.secret?) %></code>
|
103
|
+
<% if source == :default %>
|
104
|
+
<span class="ultra-settings-source-indicator">Currently active</span>
|
105
|
+
<% end %>
|
106
|
+
</div>
|
107
|
+
<% end %>
|
108
|
+
</div>
|
109
|
+
</div>
|
110
|
+
<% end %>
|
111
|
+
</div>
|
112
|
+
</div>
|
data/app/index.html.erb
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
<%= style_tag %>
|
2
|
+
|
3
|
+
<!-- SVG Icons -->
|
4
|
+
<svg style="display: none;" xmlns="http://www.w3.org/2000/svg">
|
5
|
+
<defs>
|
6
|
+
<symbol id="edit-icon" viewBox="0 0 16 16">
|
7
|
+
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
|
8
|
+
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5z"/>
|
9
|
+
</symbol>
|
10
|
+
<symbol id="info-icon" viewBox="0 0 16 16">
|
11
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/>
|
12
|
+
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0"/>
|
13
|
+
</symbol>
|
14
|
+
</defs>
|
15
|
+
</svg>
|
16
|
+
|
1
17
|
<div class="ultra-settings">
|
2
18
|
<div class="ultra-settings-nav">
|
3
19
|
<form onsubmit="return false" style="margin-bottom: 0.5rem;">
|
data/app/layout.html.erb
CHANGED
@@ -7,8 +7,7 @@
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
8
8
|
<meta name="format-detection" content="telephone=no email=no date=no address=no">
|
9
9
|
<style type="text/css">
|
10
|
-
<%=
|
11
|
-
<%= css %>
|
10
|
+
<%= layout_css %>
|
12
11
|
</style>
|
13
12
|
</head>
|
14
13
|
<body>
|
@@ -4,44 +4,34 @@ module UltraSettings
|
|
4
4
|
# This class can render information about all configurations. It is used by the bundled
|
5
5
|
# web UI, but you can use it to embed the configuration information in your own web pages.
|
6
6
|
#
|
7
|
-
# The output will be a simple HTML drop down list that can be used to display an HTML
|
8
|
-
# showing each configuration. You can specify the CSS class for the select element
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# `ultra-settings-table`.
|
7
|
+
# The output will be a simple HTML drop down list that can be used to display an HTML element
|
8
|
+
# showing each configuration. You can specify the CSS class for the select element by passing
|
9
|
+
# the `select_class` option to the `render` method. By default the select element has
|
10
|
+
# the class `ultra-settings-select`.
|
12
11
|
#
|
13
12
|
# @example
|
14
13
|
# <h1>Application Configuration</h1>
|
15
|
-
# <%= UltraSettings::ApplicationView.new.render(select_class: 'form-control'
|
14
|
+
# <%= UltraSettings::ApplicationView.new.render(select_class: 'form-control') %>
|
16
15
|
class ApplicationView
|
17
|
-
|
16
|
+
attr_reader :css
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
def javascript
|
25
|
-
@javascript = read_app_file("application.js")
|
26
|
-
end
|
27
|
-
|
28
|
-
private
|
29
|
-
|
30
|
-
def read_app_file(path)
|
31
|
-
File.read(File.join(app_dir, path))
|
32
|
-
end
|
33
|
-
|
34
|
-
def app_dir
|
35
|
-
File.expand_path(File.join("..", "..", "app"), __dir__)
|
36
|
-
end
|
18
|
+
def initialize(color_scheme: :light)
|
19
|
+
@css = application_css(color_scheme)
|
20
|
+
@css = @css.html_safe if @css.respond_to?(:html_safe)
|
37
21
|
end
|
38
22
|
|
39
|
-
def render(select_class: "ultra-settings-select", table_class: "
|
40
|
-
html =
|
23
|
+
def render(select_class: "ultra-settings-select", table_class: "")
|
24
|
+
html = ViewHelper.erb_template("index.html.erb").result(binding)
|
41
25
|
html = html.html_safe if html.respond_to?(:html_safe)
|
42
26
|
html
|
43
27
|
end
|
44
28
|
|
29
|
+
def style_tag
|
30
|
+
tag = "<style type=\"text/css\">\n#{css}\n</style>"
|
31
|
+
tag = tag.html_safe if tag.respond_to?(:html_safe)
|
32
|
+
tag
|
33
|
+
end
|
34
|
+
|
45
35
|
def to_s
|
46
36
|
render
|
47
37
|
end
|
@@ -53,7 +43,13 @@ module UltraSettings
|
|
53
43
|
end
|
54
44
|
|
55
45
|
def javascript
|
56
|
-
|
46
|
+
ViewHelper.read_app_file("application.js")
|
47
|
+
end
|
48
|
+
|
49
|
+
def application_css(color_scheme)
|
50
|
+
vars = ViewHelper.erb_template("application_vars.css.erb").result(binding).strip
|
51
|
+
css = ViewHelper.read_app_file("application.css").strip
|
52
|
+
"#{vars}\n#{css}"
|
57
53
|
end
|
58
54
|
end
|
59
55
|
end
|
@@ -1,42 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module UltraSettings
|
4
|
-
# This class can render information about a configuration in
|
4
|
+
# This class can render information about a configuration in a clean card-based layout. It is used by the
|
5
5
|
# bundled web UI, but you can use it to embed the configuration information in your own web pages.
|
6
6
|
#
|
7
|
-
# The output will be
|
8
|
-
#
|
9
|
-
# `ultra-settings-table`.
|
7
|
+
# The output will be HTML with a card-based layout for better readability. The `table_class` option is
|
8
|
+
# still supported for backward compatibility but is no longer used in the new card layout.
|
10
9
|
#
|
11
10
|
# @example
|
12
11
|
# <h1>Service Configuration</h1>
|
13
|
-
# <%= UltraSettings::ConfigurationView.new(ServiceConfiguration.instance).render
|
12
|
+
# <%= UltraSettings::ConfigurationView.new(ServiceConfiguration.instance).render %>
|
14
13
|
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
14
|
def initialize(configuration)
|
34
15
|
@configuration = configuration
|
35
16
|
end
|
36
17
|
|
37
|
-
def render(table_class: "
|
18
|
+
def render(table_class: "")
|
38
19
|
configuration = @configuration
|
39
|
-
html =
|
20
|
+
html = ViewHelper.erb_template("configuration.html.erb").result(binding)
|
40
21
|
html = html.html_safe if html.respond_to?(:html_safe)
|
41
22
|
html
|
42
23
|
end
|
@@ -9,7 +9,6 @@ module UltraSettings
|
|
9
9
|
config.ultra_settings = ActiveSupport::OrderedOptions.new
|
10
10
|
config.ultra_settings.auto_load_directories ||= [File.join("app", "configurations")]
|
11
11
|
|
12
|
-
# initializer "ultra_settings.before_bootstrap", before: :bootstrap_hook do
|
13
12
|
config.before_configuration do
|
14
13
|
UltraSettings::Configuration.yaml_config_env ||= Rails.env
|
15
14
|
UltraSettings::Configuration.yaml_config_path ||= Rails.root.join("config")
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module UltraSettings
|
4
|
+
# Base class for rendering views.
|
5
|
+
module ViewHelper
|
6
|
+
@cache = {}
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def erb_template(path)
|
10
|
+
@cache["erb:#{path}"] ||= ERB.new(read_app_file(path))
|
11
|
+
end
|
12
|
+
|
13
|
+
def read_app_file(path)
|
14
|
+
@cache["file:#{path}"] ||= File.read(File.join(app_dir, path))
|
15
|
+
end
|
16
|
+
|
17
|
+
def app_dir
|
18
|
+
File.expand_path(File.join("..", "..", "app"), __dir__)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -3,15 +3,14 @@
|
|
3
3
|
module UltraSettings
|
4
4
|
# Helper class for rendering the settings information in an HTML page.
|
5
5
|
class WebView
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :layout_css
|
7
7
|
|
8
8
|
# @param color_scheme [Symbol] The color scheme to use in the UI. This can be `:light`,
|
9
9
|
# `:dark`, or `:system`. The default is `:light`.
|
10
10
|
def initialize(color_scheme: :light)
|
11
|
-
color_scheme = (color_scheme || :light).to_sym
|
12
|
-
@layout_template = erb_template("layout.html.erb")
|
13
|
-
@layout_css =
|
14
|
-
@css = application_css(color_scheme)
|
11
|
+
@color_scheme = (color_scheme || :light).to_sym
|
12
|
+
@layout_template = ViewHelper.erb_template("layout.html.erb")
|
13
|
+
@layout_css = scheme_layout_css(@color_scheme)
|
15
14
|
end
|
16
15
|
|
17
16
|
def render_settings
|
@@ -19,32 +18,14 @@ module UltraSettings
|
|
19
18
|
end
|
20
19
|
|
21
20
|
def content
|
22
|
-
UltraSettings::ApplicationView.new.render
|
21
|
+
UltraSettings::ApplicationView.new(color_scheme: @color_scheme).render
|
23
22
|
end
|
24
23
|
|
25
24
|
private
|
26
25
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
def read_app_file(path)
|
32
|
-
File.read(File.join(app_dir, path))
|
33
|
-
end
|
34
|
-
|
35
|
-
def app_dir
|
36
|
-
File.expand_path(File.join("..", "..", "app"), __dir__)
|
37
|
-
end
|
38
|
-
|
39
|
-
def layout_css(color_scheme)
|
40
|
-
vars = erb_template("layout_vars.css.erb").result(binding)
|
41
|
-
css = read_app_file("layout.css")
|
42
|
-
"#{vars}\n#{css}"
|
43
|
-
end
|
44
|
-
|
45
|
-
def application_css(color_scheme)
|
46
|
-
vars = erb_template("application_vars.css.erb").result(binding)
|
47
|
-
css = read_app_file("application.css")
|
26
|
+
def scheme_layout_css(color_scheme)
|
27
|
+
vars = ViewHelper.erb_template("layout_vars.css.erb").result(binding)
|
28
|
+
css = ViewHelper.read_app_file("layout.css")
|
48
29
|
"#{vars}\n#{css}"
|
49
30
|
end
|
50
31
|
end
|
data/lib/ultra_settings.rb
CHANGED
@@ -13,6 +13,7 @@ require_relative "ultra_settings/coerce"
|
|
13
13
|
require_relative "ultra_settings/config_helper"
|
14
14
|
require_relative "ultra_settings/field"
|
15
15
|
require_relative "ultra_settings/rack_app"
|
16
|
+
require_relative "ultra_settings/view_helper"
|
16
17
|
require_relative "ultra_settings/web_view"
|
17
18
|
require_relative "ultra_settings/application_view"
|
18
19
|
require_relative "ultra_settings/configuration_view"
|
@@ -182,11 +183,12 @@ module UltraSettings
|
|
182
183
|
# @param name [String] The name of the setting.
|
183
184
|
# @return [String, nil]
|
184
185
|
# @api private
|
185
|
-
def runtime_settings_url(name)
|
186
|
+
def runtime_settings_url(name, type)
|
186
187
|
url = @runtime_settings_url.to_s
|
187
188
|
return nil if url.empty?
|
188
189
|
|
189
|
-
url.gsub("${name}", URI.encode_www_form_component(name))
|
190
|
+
url = url.gsub("${name}", URI.encode_www_form_component(name.to_s))
|
191
|
+
url.gsub("${type}", URI.encode_www_form_component(type.to_s))
|
190
192
|
end
|
191
193
|
|
192
194
|
def fields_secret_by_default=(value)
|
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: 2.
|
4
|
+
version: 2.6.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: 2025-
|
11
|
+
date: 2025-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -31,6 +31,7 @@ executables: []
|
|
31
31
|
extensions: []
|
32
32
|
extra_rdoc_files: []
|
33
33
|
files:
|
34
|
+
- ARCHITECTURE.md
|
34
35
|
- CHANGELOG.md
|
35
36
|
- MIT-LICENSE.txt
|
36
37
|
- README.md
|
@@ -54,6 +55,7 @@ files:
|
|
54
55
|
- lib/ultra_settings/railtie.rb
|
55
56
|
- lib/ultra_settings/uninitialized_runtime_settings.rb
|
56
57
|
- lib/ultra_settings/version.rb
|
58
|
+
- lib/ultra_settings/view_helper.rb
|
57
59
|
- lib/ultra_settings/web_view.rb
|
58
60
|
- lib/ultra_settings/yaml_config.rb
|
59
61
|
- ultra_settings.gemspec
|