ultra_settings 2.1.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +25 -5
- data/VERSION +1 -1
- data/app/configuration.html.erb +15 -8
- data/lib/ultra_settings/coerce.rb +52 -6
- data/lib/ultra_settings/configuration.rb +21 -18
- data/lib/ultra_settings.rb +20 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a9fddef64ed0564d8ead06ae79465a37fdb50a55407139cacc121714475ac6f
|
4
|
+
data.tar.gz: a26020c7d81154d3e000bba327e3a6cb53ab00ae9847ed2f96999ff18d431241
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 066b922fcc7232a33eee6e6b52924d1efc51d3a4e500f4680f8e03a1feb7a6414a22b55f9b5672e35db7aeb958566fc1685ba39fed8a9f568840af2b60a7b3e2
|
7
|
+
data.tar.gz: b95942f92c5010decb55da413fdd2c9f070ebafef2815ab5bd4f781f803b744c502b98598fc14b4e5338ec7026fd7a1c5b11011136afe4507a0eeb436dac632b
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,22 @@ 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.3.0
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Added logic for parsing arrays from environment variables. Array fields can now be set as comma delimited strings in an environment variable.
|
12
|
+
|
13
|
+
### Fixed
|
14
|
+
|
15
|
+
- Mixed case boolean values are now handled properly so that "False" is interpreted as `false` and "True" is interpreted as `true`.
|
16
|
+
|
17
|
+
## 2.2.0
|
18
|
+
|
19
|
+
### Added
|
20
|
+
|
21
|
+
- Added option for `UltraSettings.runtime_settings_url` to allow configuring a link for editing runtime settings from the web UI.
|
22
|
+
|
7
23
|
## 2.1.0
|
8
24
|
|
9
25
|
### Added
|
data/README.md
CHANGED
@@ -10,6 +10,24 @@ UltraSettings is a Ruby gem designed for managing application settings from vari
|
|
10
10
|
|
11
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.
|
12
12
|
|
13
|
+
## Table Of Contents
|
14
|
+
|
15
|
+
- [Key Features](#key-features)
|
16
|
+
- [Usage](#usage)
|
17
|
+
- [Defining Configurations](#defining-configurations)
|
18
|
+
- [Field Options](#field-options)
|
19
|
+
- [Environment Variables](#environment-variables)
|
20
|
+
- [Runtime Settings](#runtime-settings)
|
21
|
+
- [YAML Files](#yaml-files)
|
22
|
+
- [Removing The Hierarchy](#removing-the-hierarchy)
|
23
|
+
- [Accessing Settings](#accessing-settings)
|
24
|
+
- [Web UI](#web-ui)
|
25
|
+
- [Testing With UltraSettings](#testing-with-ultrasettings)
|
26
|
+
- [Rollout Percentages](#rollout-percentages)
|
27
|
+
- [Installation](#installation)
|
28
|
+
- [Contributing](#contributing)
|
29
|
+
- [License](#license)
|
30
|
+
|
13
31
|
## Key Features
|
14
32
|
|
15
33
|
This gem supports a three-layer hierarchy for defining configuration sources:
|
@@ -82,13 +100,13 @@ You can customize the behavior of each field using various options:
|
|
82
100
|
|
83
101
|
- `: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:
|
84
102
|
|
85
|
-
- `:string`
|
103
|
+
- `:string` - This is the default type.
|
86
104
|
- `:integer`
|
87
105
|
- `:float`
|
88
|
-
- `:boolean`
|
89
|
-
- `:datetime`
|
106
|
+
- `:boolean` - Will accept case insensitive strings "true", "false", "1", "0", "t", "f", "yes", "no", "y", "n".
|
107
|
+
- `:datetime` - Values should be specified in ISO 8601 format.
|
90
108
|
- `:symbol`
|
91
|
-
- `:array`
|
109
|
+
- `:array` - The array type will return an array of strings. If the raw value is a string (i.e. from an environment variable), it will be iterpreted as a comma separated list of values. You can use double quotes to group values that contain commas and backslashes to escape values. Leading and trailing whitespace will be stripped from each value.
|
92
110
|
|
93
111
|
- `:description` - Provides a description of the field. This is used for documentation purposes.
|
94
112
|
|
@@ -176,6 +194,8 @@ You can customize the behavior of runtime setting names with the following optio
|
|
176
194
|
|
177
195
|
- **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`.
|
178
196
|
|
197
|
+
- **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 placeholder `${name}` in the URL which will be replaced with the name of the runtime setting. If you are using the `super_settings` gem for runtime settings, then you can target a setting by adding `#edit=${name}` to the root URL where `super_settings` is mounted.
|
198
|
+
|
179
199
|
If a setting value cannot be loaded from the runtime settings, then it's value will attempt to be loaded from a YAML file.
|
180
200
|
|
181
201
|
### YAML Files
|
@@ -480,7 +500,7 @@ bundle exec rackup
|
|
480
500
|
You can test with some setting set by setting environment variable used in the test configuration.
|
481
501
|
|
482
502
|
```bash
|
483
|
-
MY_SERVICE_HOST=host.example.com MY_SERVICE_TOKEN=secret bundle exec
|
503
|
+
MY_SERVICE_HOST=host.example.com MY_SERVICE_TOKEN=secret bundle exec rackup
|
484
504
|
```
|
485
505
|
|
486
506
|
You can test dark mode by setting the `COLOR_SCHEME` environment variable.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.3.0
|
data/app/configuration.html.erb
CHANGED
@@ -52,14 +52,14 @@
|
|
52
52
|
<% end %>
|
53
53
|
</td>
|
54
54
|
|
55
|
-
<td>
|
55
|
+
<td style="word-wrap: break-word;">
|
56
56
|
<% unless field.description.to_s.empty? %>
|
57
57
|
<div>
|
58
58
|
<%= html_escape(field.description) %>
|
59
59
|
</div>
|
60
60
|
<% end %>
|
61
61
|
|
62
|
-
<ul style="margin: 0; padding: 0;list-style-type: disc; list-style-position:
|
62
|
+
<ul style="margin: 0 0 0 1rem; padding: 0; list-style-type: disc; list-style-position: outside;">
|
63
63
|
<% if field.env_var && !configuration.class.environment_variables_disabled? %>
|
64
64
|
<li>
|
65
65
|
<% if source == :env %>
|
@@ -68,9 +68,8 @@
|
|
68
68
|
<% else %>
|
69
69
|
Can be
|
70
70
|
<% end %>
|
71
|
-
set with the
|
71
|
+
set with the environment variable
|
72
72
|
<code><%= show_defined_value(field.env_var, configuration.__value_from_source__(field.name, :env), field.secret?) %></code>
|
73
|
-
environment variable.
|
74
73
|
<% if source == :env %>
|
75
74
|
</strong>
|
76
75
|
<% end %>
|
@@ -84,12 +83,21 @@
|
|
84
83
|
<% else %>
|
85
84
|
Can be
|
86
85
|
<% end %>
|
87
|
-
set with the
|
86
|
+
set with the runtime setting
|
88
87
|
<code><%= show_defined_value(field.runtime_setting, configuration.__value_from_source__(field.name, :settings), field.secret?) %></code>
|
89
|
-
runtime setting.
|
90
88
|
<% if source == :settings %>
|
91
89
|
</strong>
|
92
90
|
<% end %>
|
91
|
+
|
92
|
+
<% edit_url = UltraSettings.runtime_settings_url(field.runtime_setting) %>
|
93
|
+
<% if edit_url %>
|
94
|
+
<a href="<%= html_escape(edit_url) %>" title="Edit <%= html_escape(field.runtime_setting) %>" style="text-decoration: none; color: inherit; vertical-align: middle;">
|
95
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square" viewBox="0 0 16 16">
|
96
|
+
<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"/>
|
97
|
+
<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"/>
|
98
|
+
</svg>
|
99
|
+
</a>
|
100
|
+
<% end %>
|
93
101
|
</li>
|
94
102
|
<% end %>
|
95
103
|
<% if field.yaml_key && !configuration.class.yaml_config_disabled? %>
|
@@ -100,9 +108,8 @@
|
|
100
108
|
<% else %>
|
101
109
|
Can be
|
102
110
|
<% end %>
|
103
|
-
set with the
|
111
|
+
set with the configuration file key
|
104
112
|
<code><%= show_defined_value(field.yaml_key, configuration.__value_from_source__(field.name, :yaml), field.secret?) %></code>
|
105
|
-
key in the configuration file.
|
106
113
|
<% if source == :yaml %>
|
107
114
|
</strong>
|
108
115
|
<% end %>
|
@@ -10,11 +10,8 @@ module UltraSettings
|
|
10
10
|
false, 0,
|
11
11
|
"0", :"0",
|
12
12
|
"f", :f,
|
13
|
-
"F", :F,
|
14
13
|
"false", :false,
|
15
|
-
"
|
16
|
-
"off", :off,
|
17
|
-
"OFF", :OFF
|
14
|
+
"off", :off
|
18
15
|
]).freeze
|
19
16
|
# rubocop:enable Lint/BooleanSymbol
|
20
17
|
|
@@ -39,7 +36,7 @@ module UltraSettings
|
|
39
36
|
when :datetime
|
40
37
|
time(value)
|
41
38
|
when :array
|
42
|
-
|
39
|
+
array(value).map(&:to_s)
|
43
40
|
when :symbol
|
44
41
|
value.to_s.to_sym
|
45
42
|
when :rollout
|
@@ -53,14 +50,26 @@ module UltraSettings
|
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
53
|
+
# Cast value of array
|
54
|
+
#
|
55
|
+
# @param value [Object]
|
56
|
+
# @return [Array]
|
57
|
+
def array(value)
|
58
|
+
return [] if blank?(value)
|
59
|
+
return value.collect(&:to_s) if value.is_a?(Array)
|
60
|
+
|
61
|
+
parse_csv_line(value.to_s)
|
62
|
+
end
|
63
|
+
|
56
64
|
# Cast variations of booleans (i.e. "true", "false", 1, 0, etc.) to actual boolean objects.
|
57
65
|
#
|
58
66
|
# @param value [Object]
|
59
67
|
# @return [Boolean]
|
60
68
|
def boolean(value)
|
61
69
|
return nil if blank?(value)
|
70
|
+
return false if value == false
|
62
71
|
|
63
|
-
!FALSE_VALUES.include?(value)
|
72
|
+
!FALSE_VALUES.include?(value.to_s.downcase)
|
64
73
|
end
|
65
74
|
|
66
75
|
# Cast a value to a Time object.
|
@@ -104,6 +113,43 @@ module UltraSettings
|
|
104
113
|
def present?(value)
|
105
114
|
!blank?(value)
|
106
115
|
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
# Parse a line of CSV data to an array of strings. Elements are separated by commas and
|
120
|
+
# characters can be escaped with a backslash.
|
121
|
+
def parse_csv_line(line)
|
122
|
+
values = []
|
123
|
+
current_value = +""
|
124
|
+
in_quotes = false
|
125
|
+
|
126
|
+
i = 0
|
127
|
+
while i < line.length
|
128
|
+
char = line[i]
|
129
|
+
|
130
|
+
if char == "\\"
|
131
|
+
if i + 1 < line.length
|
132
|
+
current_value << line[i + 1]
|
133
|
+
i += 1
|
134
|
+
else
|
135
|
+
current_value << "\\"
|
136
|
+
end
|
137
|
+
elsif char == '"'
|
138
|
+
in_quotes = !in_quotes
|
139
|
+
elsif char == "," && !in_quotes
|
140
|
+
values << current_value.strip
|
141
|
+
current_value = +""
|
142
|
+
else
|
143
|
+
current_value << char
|
144
|
+
end
|
145
|
+
|
146
|
+
i += 1
|
147
|
+
end
|
148
|
+
|
149
|
+
values << current_value.strip unless current_value.empty?
|
150
|
+
|
151
|
+
values
|
152
|
+
end
|
107
153
|
end
|
108
154
|
end
|
109
155
|
end
|
@@ -7,6 +7,9 @@ module UltraSettings
|
|
7
7
|
ALLOWED_NAME_PATTERN = /\A[a-z_][a-zA-Z0-9_]*\z/
|
8
8
|
ALLOWED_TYPES = [:string, :symbol, :integer, :float, :boolean, :datetime, :array].freeze
|
9
9
|
|
10
|
+
@env_var_prefix = nil
|
11
|
+
@runtime_setting_prefix = nil
|
12
|
+
|
10
13
|
class << self
|
11
14
|
# Define a field on the configuration. This will create a getter method for the field.
|
12
15
|
# The field value will be read from the environment, runtime settings, or a YAML file
|
@@ -435,10 +438,10 @@ module UltraSettings
|
|
435
438
|
end
|
436
439
|
|
437
440
|
def initialize
|
438
|
-
@
|
439
|
-
@
|
440
|
-
@
|
441
|
-
@
|
441
|
+
@ultra_settings_mutex = Mutex.new
|
442
|
+
@ultra_settings_memoized_values = {}
|
443
|
+
@ultra_settings_override_values = {}
|
444
|
+
@ultra_settings_yaml_config = nil
|
442
445
|
end
|
443
446
|
|
444
447
|
def [](name)
|
@@ -450,7 +453,7 @@ module UltraSettings
|
|
450
453
|
end
|
451
454
|
|
452
455
|
def override!(values, &block)
|
453
|
-
save_val = @
|
456
|
+
save_val = @ultra_settings_override_values[Thread.current.object_id]
|
454
457
|
|
455
458
|
temp_values = (save_val || {}).dup
|
456
459
|
values.each do |key, value|
|
@@ -458,13 +461,13 @@ module UltraSettings
|
|
458
461
|
end
|
459
462
|
|
460
463
|
begin
|
461
|
-
@
|
462
|
-
@
|
464
|
+
@ultra_settings_mutex.synchronize do
|
465
|
+
@ultra_settings_override_values[Thread.current.object_id] = temp_values
|
463
466
|
end
|
464
467
|
yield
|
465
468
|
ensure
|
466
|
-
@
|
467
|
-
@
|
469
|
+
@ultra_settings_mutex.synchronize do
|
470
|
+
@ultra_settings_override_values[Thread.current.object_id] = save_val
|
468
471
|
end
|
469
472
|
end
|
470
473
|
end
|
@@ -530,12 +533,12 @@ module UltraSettings
|
|
530
533
|
field = self.class.send(:defined_fields)[name]
|
531
534
|
return nil unless field
|
532
535
|
|
533
|
-
if field.static? && @
|
534
|
-
return @
|
536
|
+
if field.static? && @ultra_settings_memoized_values.include?(name)
|
537
|
+
return @ultra_settings_memoized_values[name]
|
535
538
|
end
|
536
539
|
|
537
|
-
if @
|
538
|
-
value = field.coerce(@
|
540
|
+
if @ultra_settings_override_values[Thread.current.object_id]&.include?(name)
|
541
|
+
value = field.coerce(@ultra_settings_override_values[Thread.current.object_id][name])
|
539
542
|
else
|
540
543
|
env = ENV if field.env_var
|
541
544
|
settings = UltraSettings.__runtime_settings__ if field.runtime_setting
|
@@ -549,11 +552,11 @@ module UltraSettings
|
|
549
552
|
end
|
550
553
|
|
551
554
|
if field.static?
|
552
|
-
@
|
553
|
-
if @
|
554
|
-
value = @
|
555
|
+
@ultra_settings_mutex.synchronize do
|
556
|
+
if @ultra_settings_memoized_values.include?(name)
|
557
|
+
value = @ultra_settings_memoized_values[name]
|
555
558
|
else
|
556
|
-
@
|
559
|
+
@ultra_settings_memoized_values[name] = value
|
557
560
|
end
|
558
561
|
end
|
559
562
|
end
|
@@ -578,7 +581,7 @@ module UltraSettings
|
|
578
581
|
end
|
579
582
|
|
580
583
|
def __yaml_config__
|
581
|
-
@
|
584
|
+
@ultra_settings_yaml_config ||= self.class.load_yaml_config || {}
|
582
585
|
end
|
583
586
|
end
|
584
587
|
end
|
data/lib/ultra_settings.rb
CHANGED
@@ -6,6 +6,7 @@ require "time"
|
|
6
6
|
require "pathname"
|
7
7
|
require "singleton"
|
8
8
|
require "digest"
|
9
|
+
require "uri"
|
9
10
|
|
10
11
|
require_relative "ultra_settings/configuration"
|
11
12
|
require_relative "ultra_settings/coerce"
|
@@ -33,6 +34,7 @@ module UltraSettings
|
|
33
34
|
@configurations = {}
|
34
35
|
@mutex = Mutex.new
|
35
36
|
@runtime_settings = nil
|
37
|
+
@runtime_settings_url = nil
|
36
38
|
|
37
39
|
class << self
|
38
40
|
# Adds a configuration to the root namespace. The configuration will be
|
@@ -167,6 +169,24 @@ module UltraSettings
|
|
167
169
|
@runtime_settings
|
168
170
|
end
|
169
171
|
|
172
|
+
# Set the URL for changing runtime settings. If this is set, then a link to the
|
173
|
+
# URL will be displayed in the web view for fields that support runtime settings.
|
174
|
+
# The URL may contain a `${name}` placeholder that will be replaced with the name
|
175
|
+
# of the setting.
|
176
|
+
attr_writer :runtime_settings_url
|
177
|
+
|
178
|
+
# Get the URL for changing runtime settings.
|
179
|
+
#
|
180
|
+
# @param name [String] The name of the setting.
|
181
|
+
# @return [String, nil]
|
182
|
+
# @api private
|
183
|
+
def runtime_settings_url(name)
|
184
|
+
url = @runtime_settings_url.to_s
|
185
|
+
return nil if url.empty?
|
186
|
+
|
187
|
+
url.gsub("${name}", URI.encode_www_form_component(name))
|
188
|
+
end
|
189
|
+
|
170
190
|
def fields_secret_by_default=(value)
|
171
191
|
Configuration.fields_secret_by_default = value
|
172
192
|
end
|
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.3.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: 2024-
|
11
|
+
date: 2024-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|