ultra_settings 2.6.1 → 2.8.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/CHANGELOG.md +22 -1
- data/README.md +6 -4
- data/VERSION +1 -1
- data/app/_config_description.html.erb +30 -0
- data/app/_config_list.html.erb +14 -0
- data/app/_select_menu.html.erb +53 -0
- data/app/application.css +172 -34
- data/app/application.js +108 -25
- data/app/configuration.html.erb +57 -24
- data/app/index.html.erb +21 -16
- data/lib/ultra_settings/application_view.rb +20 -10
- data/lib/ultra_settings/coerce.rb +3 -0
- data/lib/ultra_settings/config_helper.rb +12 -8
- data/lib/ultra_settings/configuration.rb +41 -8
- data/lib/ultra_settings/configuration_view.rb +12 -6
- data/lib/ultra_settings/field.rb +3 -2
- data/lib/ultra_settings/rack_app.rb +7 -0
- data/lib/ultra_settings/render_helper.rb +28 -0
- data/lib/ultra_settings/uninitialized_runtime_settings.rb +12 -7
- data/lib/ultra_settings/view_helper.rb +19 -0
- data/lib/ultra_settings/web_view.rb +9 -1
- data/lib/ultra_settings/yaml_config.rb +7 -0
- data/lib/ultra_settings.rb +49 -3
- metadata +7 -7
data/app/configuration.html.erb
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
<div class="ultra-settings-block">
|
|
2
|
-
|
|
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>
|
|
8
|
-
<% end %>
|
|
9
|
-
</div>
|
|
10
|
-
<% end %>
|
|
2
|
+
<%= render_partial "config_description", configuration: configuration %>
|
|
11
3
|
|
|
12
4
|
<div class="ultra-settings-fields">
|
|
13
5
|
<% configuration.class.fields.each do |field| %>
|
|
14
6
|
<% source = configuration.__source__(field.name) %>
|
|
7
|
+
|
|
15
8
|
<div class="ultra-settings-field">
|
|
16
9
|
<div class="ultra-settings-field-header">
|
|
17
10
|
<div class="ultra-settings-field-name">
|
|
18
11
|
<code><%= html_escape(field.name) %></code>
|
|
12
|
+
|
|
19
13
|
<% if field.secret? %>
|
|
20
|
-
<span class="ultra-settings-field-badge ultra-settings-badge-secret">
|
|
14
|
+
<span class="ultra-settings-field-badge ultra-settings-badge-secret">
|
|
15
|
+
secret
|
|
16
|
+
</span>
|
|
21
17
|
<% end %>
|
|
18
|
+
|
|
22
19
|
<% if field.static? %>
|
|
23
|
-
<span class="ultra-settings-field-badge ultra-settings-badge-static">
|
|
20
|
+
<span class="ultra-settings-field-badge ultra-settings-badge-static">
|
|
21
|
+
static
|
|
22
|
+
</span>
|
|
24
23
|
<% end %>
|
|
25
24
|
</div>
|
|
25
|
+
|
|
26
26
|
<div class="ultra-settings-field-type">
|
|
27
27
|
<%= html_escape(field.type) %>
|
|
28
28
|
</div>
|
|
@@ -32,15 +32,20 @@
|
|
|
32
32
|
<% if configuration[field.name].nil? %>
|
|
33
33
|
<span class="ultra-settings-nil-value">nil</span>
|
|
34
34
|
<% elsif field.secret? %>
|
|
35
|
-
<code class="ultra-settings-field-data-value"
|
|
35
|
+
<code class="ultra-settings-field-data-value">
|
|
36
|
+
<%= html_escape(secret_value(configuration[field.name])) %>
|
|
37
|
+
</code>
|
|
36
38
|
<% else %>
|
|
37
|
-
<code class="ultra-settings-field-data-value"
|
|
39
|
+
<code class="ultra-settings-field-data-value">
|
|
40
|
+
<%= html_escape(display_value(configuration[field.name])) %>
|
|
41
|
+
</code>
|
|
38
42
|
<% end %>
|
|
39
43
|
</div>
|
|
40
44
|
|
|
41
45
|
<% unless field.description.to_s.empty? %>
|
|
42
46
|
<div class="ultra-settings-field-description">
|
|
43
47
|
<%= info_icon %>
|
|
48
|
+
|
|
44
49
|
<div class="ultra-settings-description-text">
|
|
45
50
|
<%= html_escape(field.description) %>
|
|
46
51
|
</div>
|
|
@@ -48,61 +53,84 @@
|
|
|
48
53
|
<% end %>
|
|
49
54
|
|
|
50
55
|
<div class="ultra-settings-field-sources">
|
|
51
|
-
<%
|
|
56
|
+
<% sources = configuration.__available_sources__(field.name) %>
|
|
57
|
+
<% if sources.include?(:env) %>
|
|
52
58
|
<div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :env %>">
|
|
53
59
|
<span class="ultra-settings-source-type">
|
|
54
60
|
Environment Variable
|
|
55
61
|
</span>
|
|
62
|
+
|
|
56
63
|
<code class="ultra-settings-source-value">
|
|
57
64
|
<%= field.env_var %>
|
|
58
65
|
<%= show_defined_value(field.env_var, configuration.__value_from_source__(field.name, :env), field.secret?) %>
|
|
59
66
|
</code>
|
|
67
|
+
|
|
60
68
|
<% if source == :env %>
|
|
61
|
-
<span class="ultra-settings-source-indicator">
|
|
69
|
+
<span class="ultra-settings-source-indicator">
|
|
70
|
+
Currently active
|
|
71
|
+
</span>
|
|
62
72
|
<% end %>
|
|
63
73
|
</div>
|
|
64
74
|
<% end %>
|
|
65
75
|
|
|
66
|
-
<% if
|
|
76
|
+
<% if sources.include?(:settings) %>
|
|
67
77
|
<div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :settings %>">
|
|
68
78
|
<span class="ultra-settings-source-type">Runtime Setting</span>
|
|
79
|
+
|
|
69
80
|
<code class="ultra-settings-source-value">
|
|
70
81
|
<%= field.runtime_setting %>
|
|
71
82
|
<%= show_defined_value(field.runtime_setting, configuration.__value_from_source__(field.name, :settings), field.secret?) %>
|
|
72
83
|
</code>
|
|
84
|
+
|
|
73
85
|
<% if source == :settings %>
|
|
74
|
-
<span class="ultra-settings-source-indicator">
|
|
86
|
+
<span class="ultra-settings-source-indicator">
|
|
87
|
+
Currently active
|
|
88
|
+
</span>
|
|
75
89
|
<% end %>
|
|
76
|
-
|
|
90
|
+
|
|
91
|
+
<% edit_url = UltraSettings.runtime_settings_url(name: field.runtime_setting, type: field.type, description: field.description) %>
|
|
92
|
+
|
|
77
93
|
<% if edit_url %>
|
|
78
|
-
<a
|
|
94
|
+
<a
|
|
95
|
+
href="<%= html_escape(edit_url) %>"
|
|
96
|
+
class="ultra-settings-edit-link"
|
|
97
|
+
title="Edit <%= html_escape(field.runtime_setting) %>"
|
|
98
|
+
>
|
|
79
99
|
<%= edit_icon %>
|
|
80
100
|
</a>
|
|
81
101
|
<% end %>
|
|
82
102
|
</div>
|
|
83
103
|
<% end %>
|
|
84
104
|
|
|
85
|
-
<% if
|
|
105
|
+
<% if sources.include?(:yaml) %>
|
|
86
106
|
<div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :yaml %>">
|
|
87
107
|
<span class="ultra-settings-source-type">Configuration File</span>
|
|
108
|
+
|
|
88
109
|
<code class="ultra-settings-source-value">
|
|
89
110
|
<%= field.yaml_key %>
|
|
90
111
|
<%= show_defined_value(field.yaml_key, configuration.__value_from_source__(field.name, :yaml), field.secret?) %>
|
|
91
112
|
</code>
|
|
113
|
+
|
|
92
114
|
<% if source == :yaml %>
|
|
93
|
-
<span class="ultra-settings-source-indicator">
|
|
115
|
+
<span class="ultra-settings-source-indicator">
|
|
116
|
+
Currently active
|
|
117
|
+
</span>
|
|
94
118
|
<% end %>
|
|
95
119
|
</div>
|
|
96
120
|
<% end %>
|
|
97
121
|
|
|
98
|
-
<% if
|
|
122
|
+
<% if sources.include?(:default) || source == :default %>
|
|
99
123
|
<div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :default %>">
|
|
100
124
|
<span class="ultra-settings-source-type">Default Value</span>
|
|
125
|
+
|
|
101
126
|
<code class="ultra-settings-source-value">
|
|
102
127
|
<%= show_defined_value("Default Value", field.default, field.secret?) %>
|
|
103
128
|
</code>
|
|
129
|
+
|
|
104
130
|
<% if source == :default %>
|
|
105
|
-
<span class="ultra-settings-source-indicator">
|
|
131
|
+
<span class="ultra-settings-source-indicator">
|
|
132
|
+
Currently active
|
|
133
|
+
</span>
|
|
106
134
|
<% end %>
|
|
107
135
|
</div>
|
|
108
136
|
<% end %>
|
|
@@ -114,10 +142,15 @@
|
|
|
114
142
|
<dialog class="ultra-settings-dialog" closedby="any">
|
|
115
143
|
<div class="ultra-settings-dialog-header">
|
|
116
144
|
<div class="ultra-settings-dialog-title"></div>
|
|
117
|
-
|
|
145
|
+
|
|
146
|
+
<button
|
|
147
|
+
class="ultra-settings-dialog-close"
|
|
148
|
+
onclick="this.closest('.ultra-settings-dialog').close();"
|
|
149
|
+
>
|
|
118
150
|
<%= close_icon %>
|
|
119
151
|
</button>
|
|
120
152
|
</div>
|
|
153
|
+
|
|
121
154
|
<div class="ultra-settings-dialog-body">
|
|
122
155
|
<code class="ultra-settings-field-data-value ultra-settings-dialog-value"></code>
|
|
123
156
|
</div>
|
data/app/index.html.erb
CHANGED
|
@@ -2,25 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
<div class="ultra-settings">
|
|
4
4
|
<div class="ultra-settings-nav">
|
|
5
|
-
|
|
6
|
-
<select class="<%= html_escape(select_class) %>" size="1" id="config-selector">
|
|
7
|
-
<% UltraSettings.__configuration_names__.sort.each do |name| %>
|
|
8
|
-
<% configuration = UltraSettings.send(name) %>
|
|
9
|
-
<% next if configuration.class.fields.empty? %>
|
|
5
|
+
<% configurations = UltraSettings.__configurations__.reject { |config| config.class.fields.empty? }.sort_by { |config| config.class.name.downcase } %>
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
<% if configurations.size == 1 %>
|
|
8
|
+
<h2 class="ultra-settings-title">
|
|
9
|
+
<%= html_escape(configurations.first.class.name) %>
|
|
10
|
+
</h2>
|
|
11
|
+
<% else %>
|
|
12
|
+
<%= render_partial "select_menu", configurations: configurations %>
|
|
13
|
+
<% end %>
|
|
15
14
|
</div>
|
|
16
15
|
|
|
17
|
-
<%
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
<% if configurations.size == 1 %>
|
|
17
|
+
<%= UltraSettings::ConfigurationView.new(configurations.first).render(table_class: table_class) %>
|
|
18
|
+
<% else %>
|
|
19
|
+
<%= render_partial "config_list", configurations: configurations %>
|
|
20
|
+
<% configurations.each do |configuration| %>
|
|
21
|
+
<div
|
|
22
|
+
class="ultra-settings-configuration"
|
|
23
|
+
id="config-<%= html_escape(configuration.class.name) %>"
|
|
24
|
+
style="display:none;"
|
|
25
|
+
>
|
|
26
|
+
<%= UltraSettings::ConfigurationView.new(configuration).render(table_class: table_class) %>
|
|
27
|
+
</div>
|
|
28
|
+
<% end %>
|
|
24
29
|
<% end %>
|
|
25
30
|
</div>
|
|
26
31
|
|
|
@@ -4,44 +4,54 @@ 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
|
|
8
|
-
#
|
|
9
|
-
# the `select_class` option to the `render` method. By default the select element has
|
|
10
|
-
# the class `ultra-settings-select`.
|
|
7
|
+
# The output will be a simple HTML drop down menu that can be used to select the configuration
|
|
8
|
+
# you want to see.
|
|
11
9
|
#
|
|
12
10
|
# @example
|
|
13
11
|
# <h1>Application Configuration</h1>
|
|
14
|
-
# <%= UltraSettings::ApplicationView.new.render
|
|
12
|
+
# <%= UltraSettings::ApplicationView.new.render %>
|
|
15
13
|
class ApplicationView
|
|
14
|
+
include RenderHelper
|
|
15
|
+
|
|
16
16
|
attr_reader :css
|
|
17
17
|
|
|
18
|
+
# Initialize the application view with a color scheme.
|
|
19
|
+
#
|
|
20
|
+
# @param color_scheme [Symbol] The color scheme to use (:light, :dark, or :system).
|
|
18
21
|
def initialize(color_scheme: :light)
|
|
19
22
|
@css = application_css(color_scheme)
|
|
20
23
|
@css = @css.html_safe if @css.respond_to?(:html_safe)
|
|
21
24
|
end
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
# Render the HTML for the configuration settings UI.
|
|
27
|
+
#
|
|
28
|
+
# @param select_class [String] Deprecated; no longer used.
|
|
29
|
+
# @param table_class [String] Deprecated; no longer used.
|
|
30
|
+
# @return [String] The rendered HTML.
|
|
31
|
+
def render(select_class: nil, table_class: nil)
|
|
24
32
|
html = ViewHelper.erb_template("index.html.erb").result(binding)
|
|
25
33
|
html = html.html_safe if html.respond_to?(:html_safe)
|
|
26
34
|
html
|
|
27
35
|
end
|
|
28
36
|
|
|
37
|
+
# Generate an HTML style tag with the CSS for the view.
|
|
38
|
+
#
|
|
39
|
+
# @return [String] The HTML style tag with CSS.
|
|
29
40
|
def style_tag
|
|
30
41
|
tag = "<style type=\"text/css\">\n#{css}\n</style>"
|
|
31
42
|
tag = tag.html_safe if tag.respond_to?(:html_safe)
|
|
32
43
|
tag
|
|
33
44
|
end
|
|
34
45
|
|
|
46
|
+
# Convert the view to a string by rendering it.
|
|
47
|
+
#
|
|
48
|
+
# @return [String] The rendered HTML.
|
|
35
49
|
def to_s
|
|
36
50
|
render
|
|
37
51
|
end
|
|
38
52
|
|
|
39
53
|
private
|
|
40
54
|
|
|
41
|
-
def html_escape(value)
|
|
42
|
-
ERB::Util.html_escape(value)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
55
|
def javascript
|
|
46
56
|
ViewHelper.read_app_file("application.js")
|
|
47
57
|
end
|
|
@@ -93,11 +93,13 @@ module UltraSettings
|
|
|
93
93
|
time
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
+
# @param value [Object] The value to check.
|
|
96
97
|
# @return [Boolean] true if the value is a numeric type or a string representing a number.
|
|
97
98
|
def numeric?(value)
|
|
98
99
|
value.is_a?(Numeric) || (value.is_a?(String) && value.to_s.match?(NUMERIC_REGEX))
|
|
99
100
|
end
|
|
100
101
|
|
|
102
|
+
# @param value [Object] The value to check.
|
|
101
103
|
# @return [Boolean] true if the value is nil or empty.
|
|
102
104
|
def blank?(value)
|
|
103
105
|
return true if value.nil?
|
|
@@ -109,6 +111,7 @@ module UltraSettings
|
|
|
109
111
|
end
|
|
110
112
|
end
|
|
111
113
|
|
|
114
|
+
# @param value [Object] The value to check.
|
|
112
115
|
# @return [Boolean] true if the value is not nil and not empty.
|
|
113
116
|
def present?(value)
|
|
114
117
|
!blank?(value)
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module UltraSettings
|
|
4
|
-
# Helper module for setting up a class to use the config methods
|
|
4
|
+
# Helper module for setting up a class to use the config methods.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
6
|
+
# @example
|
|
7
|
+
# class TestClass
|
|
8
|
+
# extend UltraSettings::ConfigHelper
|
|
9
|
+
# configuration_class TestConfiguration
|
|
10
|
+
# end
|
|
11
|
+
# TestClass.config # => TestConfiguration.instance
|
|
12
|
+
# TestClass.new.config # => TestConfiguration.instance
|
|
13
13
|
module ConfigHelper
|
|
14
|
+
# Define the configuration class and create config methods.
|
|
15
|
+
#
|
|
16
|
+
# @param config_class [Class] The configuration class to use.
|
|
17
|
+
# @return [void]
|
|
14
18
|
def configuration_class(config_class)
|
|
15
19
|
define_singleton_method :config do
|
|
16
20
|
config_class.instance
|
|
@@ -9,8 +9,20 @@ module UltraSettings
|
|
|
9
9
|
|
|
10
10
|
@env_var_prefix = nil
|
|
11
11
|
@runtime_setting_prefix = nil
|
|
12
|
+
@description = nil
|
|
12
13
|
|
|
13
14
|
class << self
|
|
15
|
+
# Set a description for the configuration. This is optional. It will be displayed
|
|
16
|
+
# in the web UI if provided. On large projects with many configurations, this can
|
|
17
|
+
# help identify the purpose of each configuration.
|
|
18
|
+
#
|
|
19
|
+
# @param text [String] The description text.
|
|
20
|
+
# @return [void]
|
|
21
|
+
def description(text = nil)
|
|
22
|
+
@description = text.to_s.strip unless text.nil?
|
|
23
|
+
@description
|
|
24
|
+
end
|
|
25
|
+
|
|
14
26
|
# Define a field on the configuration. This will create a getter method for the field.
|
|
15
27
|
# The field value will be read from the environment, runtime settings, or a YAML file
|
|
16
28
|
# and coerced to the specified type. Empty strings will be converted to nil.
|
|
@@ -27,14 +39,19 @@ module UltraSettings
|
|
|
27
39
|
# @param static [Boolean] If true, the field value should never be changed. This is useful for
|
|
28
40
|
# fields that are used at startup to set static values in the application. Static field cannot
|
|
29
41
|
# be read from runtime settings.
|
|
30
|
-
# @param
|
|
42
|
+
# @param secret [Boolean, Proc] If true, the field value will be obscured in the output of
|
|
43
|
+
# to_hash. If a proc is provided, it will be called to determine if the field is secret.
|
|
44
|
+
# @param runtime_setting [String, Symbol, Boolean] The name of the runtime setting to use for the field.
|
|
31
45
|
# By default this will be the underscored name of the class plus a dot plus the field name
|
|
32
|
-
# (i.e. MyServiceConfiguration#foo becomes "my_service.foo").
|
|
33
|
-
#
|
|
46
|
+
# (i.e. MyServiceConfiguration#foo becomes "my_service.foo"). If set to false, runtime settings
|
|
47
|
+
# will be ignored for this field. This can be set to true to use the default name.
|
|
48
|
+
# @param env_var [String, Symbol, Boolean] The name of the environment variable to use for the field.
|
|
34
49
|
# By default this will be the underscored name of the class plus an underscore plus the field name
|
|
35
|
-
# all in uppercase (i.e. MyServiceConfiguration#foo becomes "MY_SERVICE_FOO").
|
|
36
|
-
#
|
|
37
|
-
#
|
|
50
|
+
# all in uppercase (i.e. MyServiceConfiguration#foo becomes "MY_SERVICE_FOO"). If set to false,
|
|
51
|
+
# environment variables will be ignored for this field. This can be set to true to use the default name.
|
|
52
|
+
# @param yaml_key [String, Symbol, Boolean] The name of the YAML key to use for the field. By default
|
|
53
|
+
# this is the name of the field. If set to false, YAML configuration will be ignored for this field.
|
|
54
|
+
# This can be set to true to use the default name.
|
|
38
55
|
# @return [void]
|
|
39
56
|
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)
|
|
40
57
|
name = name.to_s
|
|
@@ -61,7 +78,7 @@ module UltraSettings
|
|
|
61
78
|
default: default,
|
|
62
79
|
default_if: default_if,
|
|
63
80
|
env_var: construct_env_var(name, env_var),
|
|
64
|
-
runtime_setting:
|
|
81
|
+
runtime_setting: construct_runtime_setting(name, runtime_setting),
|
|
65
82
|
yaml_key: construct_yaml_key(name, yaml_key),
|
|
66
83
|
static: static,
|
|
67
84
|
secret: secret
|
|
@@ -145,7 +162,7 @@ module UltraSettings
|
|
|
145
162
|
# directory (i.e. MyServiceConfiguration has a default config path of
|
|
146
163
|
# "my_service.yml").
|
|
147
164
|
#
|
|
148
|
-
# @param value [String, Pathname]
|
|
165
|
+
# @param value [String, Pathname, false, nil]
|
|
149
166
|
# @return [void]
|
|
150
167
|
def configuration_file=(value)
|
|
151
168
|
value = nil if value == false
|
|
@@ -513,6 +530,22 @@ module UltraSettings
|
|
|
513
530
|
end
|
|
514
531
|
end
|
|
515
532
|
|
|
533
|
+
# Returns an array of the available data sources for the field.
|
|
534
|
+
#
|
|
535
|
+
# @param name [String, Symbol] the name of the field.
|
|
536
|
+
# @return [Array<Symbol>] The available sources (:env, :settings, :yaml, :default).
|
|
537
|
+
def __available_sources__(name)
|
|
538
|
+
field = self.class.send(:defined_fields)[name.to_s]
|
|
539
|
+
raise ArgumentError.new("Unknown field: #{name.inspect}") unless field
|
|
540
|
+
|
|
541
|
+
sources = []
|
|
542
|
+
sources << :env if field.env_var
|
|
543
|
+
sources << :settings if field.runtime_setting && UltraSettings.__runtime_settings__
|
|
544
|
+
sources << :yaml if field.yaml_key && self.class.configuration_file
|
|
545
|
+
sources << :default unless field.default.nil?
|
|
546
|
+
sources
|
|
547
|
+
end
|
|
548
|
+
|
|
516
549
|
# Output the current state of the configuration as a hash. If the field is marked as a secret,
|
|
517
550
|
# then the value will be a secure hash of the value instead of the value itself.
|
|
518
551
|
#
|
|
@@ -11,10 +11,19 @@ module UltraSettings
|
|
|
11
11
|
# <h1>Service Configuration</h1>
|
|
12
12
|
# <%= UltraSettings::ConfigurationView.new(ServiceConfiguration.instance).render %>
|
|
13
13
|
class ConfigurationView
|
|
14
|
+
include RenderHelper
|
|
15
|
+
|
|
16
|
+
# Initialize the configuration view with a configuration instance.
|
|
17
|
+
#
|
|
18
|
+
# @param configuration [UltraSettings::Configuration] The configuration instance to display.
|
|
14
19
|
def initialize(configuration)
|
|
15
20
|
@configuration = configuration
|
|
16
21
|
end
|
|
17
22
|
|
|
23
|
+
# Render the HTML for the configuration view.
|
|
24
|
+
#
|
|
25
|
+
# @param table_class [String] CSS class for the table element (maintained for backwards compatibility).
|
|
26
|
+
# @return [String] The rendered HTML.
|
|
18
27
|
def render(table_class: "")
|
|
19
28
|
configuration = @configuration
|
|
20
29
|
html = ViewHelper.erb_template("configuration.html.erb").result(binding)
|
|
@@ -22,16 +31,15 @@ module UltraSettings
|
|
|
22
31
|
html
|
|
23
32
|
end
|
|
24
33
|
|
|
34
|
+
# Convert the view to a string by rendering it.
|
|
35
|
+
#
|
|
36
|
+
# @return [String] The rendered HTML.
|
|
25
37
|
def to_s
|
|
26
38
|
render
|
|
27
39
|
end
|
|
28
40
|
|
|
29
41
|
private
|
|
30
42
|
|
|
31
|
-
def html_escape(value)
|
|
32
|
-
ERB::Util.html_escape(value)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
43
|
def display_value(value)
|
|
36
44
|
case value
|
|
37
45
|
when Time
|
|
@@ -137,8 +145,6 @@ module UltraSettings
|
|
|
137
145
|
HTML
|
|
138
146
|
end
|
|
139
147
|
|
|
140
|
-
private
|
|
141
|
-
|
|
142
148
|
def open_dialog_script
|
|
143
149
|
<<~JAVASCRIPT.gsub(/\s+/, " ").tr('"', "'")
|
|
144
150
|
this.closest('.ultra-settings-configuration').querySelector('.ultra-settings-dialog-title').textContent = this.dataset.label;
|
data/lib/ultra_settings/field.rb
CHANGED
|
@@ -16,12 +16,12 @@ module UltraSettings
|
|
|
16
16
|
# @param type [Symbol] The type of the field.
|
|
17
17
|
# @param description [String] The description of the field.
|
|
18
18
|
# @param default [Object] The default value of the field.
|
|
19
|
-
# @param default_if [Proc] A proc that returns true if the default value should be used.
|
|
19
|
+
# @param default_if [Proc, Symbol] A proc that returns true if the default value should be used.
|
|
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
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.
|
|
24
|
+
# @param secret [Boolean, Proc] Whether or not the field contains a value that should be kept secret.
|
|
25
25
|
def initialize(
|
|
26
26
|
name:,
|
|
27
27
|
type: :string,
|
|
@@ -118,6 +118,7 @@ module UltraSettings
|
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
def runtime_setting_value(settings)
|
|
121
|
+
return nil if static? || (secret? && !UltraSettings.runtime_settings_secure?)
|
|
121
122
|
return nil unless settings && runtime_setting
|
|
122
123
|
|
|
123
124
|
if type == :array && settings.respond_to?(:array)
|
|
@@ -5,11 +5,18 @@ 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
|
+
# Initialize a new Rack application for displaying settings.
|
|
9
|
+
#
|
|
10
|
+
# @param color_scheme [Symbol, nil] The color scheme to use in the UI (:light, :dark, or :system).
|
|
8
11
|
def initialize(color_scheme: nil)
|
|
9
12
|
@webview = nil
|
|
10
13
|
@color_scheme = color_scheme
|
|
11
14
|
end
|
|
12
15
|
|
|
16
|
+
# Handle Rack requests and return the settings HTML page.
|
|
17
|
+
#
|
|
18
|
+
# @param env [Hash] The Rack environment.
|
|
19
|
+
# @return [Array] A Rack response array [status, headers, body].
|
|
13
20
|
def call(env)
|
|
14
21
|
[200, {"content-type" => "text/html; charset=utf8"}, [webview.render_settings]]
|
|
15
22
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module UltraSettings
|
|
4
|
+
# Helper methods for rendering views.
|
|
5
|
+
module RenderHelper
|
|
6
|
+
# HTML escape a value.
|
|
7
|
+
#
|
|
8
|
+
# @param value [String] The value to escape.
|
|
9
|
+
# @return [String] The escaped value.
|
|
10
|
+
def html_escape(value)
|
|
11
|
+
ERB::Util.html_escape(value)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Render a partial template with the given locals.
|
|
15
|
+
#
|
|
16
|
+
# @param partial_name [String] The name of the partial template (without the leading underscore and file extension).
|
|
17
|
+
# @param locals [Hash] A hash of local variables to pass to the template.
|
|
18
|
+
# @return [String] The rendered HTML of the partial.
|
|
19
|
+
def render_partial(partial_name, locals = {})
|
|
20
|
+
template = ViewHelper.erb_template("_#{partial_name}.html.erb")
|
|
21
|
+
b = binding
|
|
22
|
+
locals.each do |key, value|
|
|
23
|
+
b.local_variable_set(key, value)
|
|
24
|
+
end
|
|
25
|
+
template.result(b)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -3,26 +3,31 @@
|
|
|
3
3
|
module UltraSettings
|
|
4
4
|
# This class is used to represent runtime settings that have not been initialized yet.
|
|
5
5
|
# You can use this to protect your application from accidentally accessing runtime settings
|
|
6
|
-
# before they are initialized. Doing this can
|
|
7
|
-
# engine has not yet been initialized. For instance, if your runtime settings
|
|
6
|
+
# before they are initialized. Doing this can cause unexpected behavior if the runtime settings
|
|
7
|
+
# engine has not yet been initialized. For instance, if your runtime settings engine reads from
|
|
8
8
|
# a database it would not be available until the database connection is established.
|
|
9
9
|
#
|
|
10
|
-
# The intention of this class is to set it
|
|
10
|
+
# The intention of this class is to set it as the runtime settings at the beginning of initialization
|
|
11
11
|
# and then set the actual runtime settings engine after the initialization is complete. It will
|
|
12
12
|
# act as a guard to prevent invalid runtime settings backed configurations from being used during
|
|
13
13
|
# initialization.
|
|
14
14
|
#
|
|
15
15
|
# @example
|
|
16
16
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
17
|
+
# UltraSettings.runtime_settings = UltraSettings::UninitializedRuntimeSettings
|
|
18
|
+
# ActiveSupport.on_load(:active_record) do
|
|
19
|
+
# UltraSettings.runtime_settings = SuperSettings
|
|
20
|
+
# end
|
|
21
21
|
class UninitializedRuntimeSettings
|
|
22
22
|
class Error < StandardError
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
class << self
|
|
26
|
+
# Raises an error when attempting to access runtime settings during initialization.
|
|
27
|
+
#
|
|
28
|
+
# @param key [String] The key being accessed.
|
|
29
|
+
# @return [void]
|
|
30
|
+
# @raise [Error] Always raises an error to prevent access during initialization.
|
|
26
31
|
def [](key)
|
|
27
32
|
raise Error.new("Attempt to call runtime setting #{key} during initialization")
|
|
28
33
|
end
|
|
@@ -6,17 +6,36 @@ module UltraSettings
|
|
|
6
6
|
@cache = {}
|
|
7
7
|
|
|
8
8
|
class << self
|
|
9
|
+
# Get an ERB template for rendering.
|
|
10
|
+
#
|
|
11
|
+
# @param path [String] The path to the template file.
|
|
12
|
+
# @return [ERB] The compiled ERB template.
|
|
9
13
|
def erb_template(path)
|
|
14
|
+
@cache.clear if development_mode?
|
|
10
15
|
@cache["erb:#{path}"] ||= ERB.new(read_app_file(path))
|
|
11
16
|
end
|
|
12
17
|
|
|
18
|
+
# Read a file from the app directory.
|
|
19
|
+
#
|
|
20
|
+
# @param path [String] The path to the file relative to the app directory.
|
|
21
|
+
# @return [String] The contents of the file.
|
|
13
22
|
def read_app_file(path)
|
|
23
|
+
@cache.clear if development_mode?
|
|
14
24
|
@cache["file:#{path}"] ||= File.read(File.join(app_dir, path))
|
|
15
25
|
end
|
|
16
26
|
|
|
27
|
+
# Get the app directory path.
|
|
28
|
+
#
|
|
29
|
+
# @return [String] The absolute path to the app directory.
|
|
17
30
|
def app_dir
|
|
18
31
|
File.expand_path(File.join("..", "..", "app"), __dir__)
|
|
19
32
|
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def development_mode?
|
|
37
|
+
ENV.fetch("RACK_ENV", "development") == "development"
|
|
38
|
+
end
|
|
20
39
|
end
|
|
21
40
|
end
|
|
22
41
|
end
|
|
@@ -5,18 +5,26 @@ module UltraSettings
|
|
|
5
5
|
class WebView
|
|
6
6
|
attr_reader :layout_css
|
|
7
7
|
|
|
8
|
+
# Initialize a new WebView with the specified color scheme.
|
|
9
|
+
#
|
|
8
10
|
# @param color_scheme [Symbol] The color scheme to use in the UI. This can be `:light`,
|
|
9
|
-
#
|
|
11
|
+
# `:dark`, or `:system`. The default is `:light`.
|
|
10
12
|
def initialize(color_scheme: :light)
|
|
11
13
|
@color_scheme = (color_scheme || :light).to_sym
|
|
12
14
|
@layout_template = ViewHelper.erb_template("layout.html.erb")
|
|
13
15
|
@layout_css = scheme_layout_css(@color_scheme)
|
|
14
16
|
end
|
|
15
17
|
|
|
18
|
+
# Render the complete settings page HTML.
|
|
19
|
+
#
|
|
20
|
+
# @return [String] The rendered HTML page.
|
|
16
21
|
def render_settings
|
|
17
22
|
@layout_template.result(binding)
|
|
18
23
|
end
|
|
19
24
|
|
|
25
|
+
# Get the content for the settings page.
|
|
26
|
+
#
|
|
27
|
+
# @return [String] The HTML content for the settings.
|
|
20
28
|
def content
|
|
21
29
|
UltraSettings::ApplicationView.new(color_scheme: @color_scheme).render
|
|
22
30
|
end
|