ultra_settings 2.7.0 → 2.8.1
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/AGENTS.md +191 -0
- data/CHANGELOG.md +22 -0
- data/MIT-LICENSE.txt +1 -1
- data/README.md +69 -1
- data/VERSION +1 -1
- data/app/_config_description.html.erb +30 -0
- data/app/_config_list.html.erb +14 -0
- data/app/_data_source.html.erb +39 -0
- data/app/_select_menu.html.erb +53 -0
- data/app/application.css +322 -44
- data/app/application.js +108 -25
- data/app/application_vars.css.erb +10 -0
- data/app/configuration.html.erb +42 -64
- data/app/index.html.erb +21 -16
- data/lib/ultra_settings/application_view.rb +8 -12
- data/lib/ultra_settings/audit_data_sources.rb +98 -0
- data/lib/ultra_settings/configuration.rb +63 -10
- data/lib/ultra_settings/configuration_view.rb +41 -6
- data/lib/ultra_settings/field.rb +2 -2
- data/lib/ultra_settings/railtie.rb +8 -0
- data/lib/ultra_settings/render_helper.rb +28 -0
- data/lib/ultra_settings/tasks/audit_data_sources.rake +76 -0
- data/lib/ultra_settings/tasks/utils.rb +23 -0
- data/lib/ultra_settings/view_helper.rb +8 -0
- data/lib/ultra_settings.rb +14 -0
- data/ultra_settings.gemspec +2 -0
- metadata +12 -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,62 +53,30 @@
|
|
|
48
53
|
<% end %>
|
|
49
54
|
|
|
50
55
|
<div class="ultra-settings-field-sources">
|
|
51
|
-
<%
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
Environment Variable
|
|
55
|
-
</span>
|
|
56
|
-
<code class="ultra-settings-source-value">
|
|
57
|
-
<%= field.env_var %>
|
|
58
|
-
<%= show_defined_value(field.env_var, configuration.__value_from_source__(field.name, :env), field.secret?) %>
|
|
59
|
-
</code>
|
|
60
|
-
<% if source == :env %>
|
|
61
|
-
<span class="ultra-settings-source-indicator">Currently active</span>
|
|
62
|
-
<% end %>
|
|
63
|
-
</div>
|
|
56
|
+
<% sources = configuration.__available_sources__(field.name) %>
|
|
57
|
+
<% if sources.include?(:env) %>
|
|
58
|
+
<%= render_partial "data_source", configuration: configuration, field: field, source: :env, current_source: source, source_type: "Environment Variable", source_name: field.env_var %>
|
|
64
59
|
<% end %>
|
|
65
60
|
|
|
66
|
-
<% if
|
|
67
|
-
|
|
68
|
-
<span class="ultra-settings-source-type">Runtime Setting</span>
|
|
69
|
-
<code class="ultra-settings-source-value">
|
|
70
|
-
<%= field.runtime_setting %>
|
|
71
|
-
<%= show_defined_value(field.runtime_setting, configuration.__value_from_source__(field.name, :settings), field.secret?) %>
|
|
72
|
-
</code>
|
|
73
|
-
<% if source == :settings %>
|
|
74
|
-
<span class="ultra-settings-source-indicator">Currently active</span>
|
|
75
|
-
<% end %>
|
|
76
|
-
<% edit_url = UltraSettings.runtime_settings_url(name: field.runtime_setting, type: field.type, description: field.description) %>
|
|
77
|
-
<% if edit_url %>
|
|
78
|
-
<a href="<%= html_escape(edit_url) %>" class="ultra-settings-edit-link" title="Edit <%= html_escape(field.runtime_setting) %>">
|
|
79
|
-
<%= edit_icon %>
|
|
80
|
-
</a>
|
|
81
|
-
<% end %>
|
|
82
|
-
</div>
|
|
61
|
+
<% if sources.include?(:settings) %>
|
|
62
|
+
<%= render_partial "data_source", configuration: configuration, field: field, source: :settings, current_source: source, source_type: "Runtime Setting", source_name: field.runtime_setting %>
|
|
83
63
|
<% end %>
|
|
84
64
|
|
|
85
|
-
<% if
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
</code>
|
|
92
|
-
<% if source == :yaml %>
|
|
93
|
-
<span class="ultra-settings-source-indicator">Currently active</span>
|
|
94
|
-
<% end %>
|
|
95
|
-
</div>
|
|
65
|
+
<% if sources.include?(:yaml) %>
|
|
66
|
+
<%= render_partial "data_source", configuration: configuration, field: field, source: :yaml, current_source: source, source_type: "Configuration File", source_name: field.yaml_key %>
|
|
67
|
+
<% end %>
|
|
68
|
+
|
|
69
|
+
<% if sources.include?(:default) %>
|
|
70
|
+
<%= render_partial "data_source", configuration: configuration, field: field, source: :default, current_source: source, source_type: "Default Value", source_name: nil %>
|
|
96
71
|
<% end %>
|
|
97
72
|
|
|
98
|
-
<% if
|
|
99
|
-
<div class="ultra-settings-source
|
|
100
|
-
<span class="ultra-settings-source-type">
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
<span class="ultra-settings-source-indicator">Currently active</span>
|
|
106
|
-
<% end %>
|
|
73
|
+
<% if source == :default && configuration[field.name].nil? %>
|
|
74
|
+
<div class="ultra-settings-source ultra-settings-source-active">
|
|
75
|
+
<span class="ultra-settings-source-type">Not Set</span>
|
|
76
|
+
|
|
77
|
+
<span class="ultra-settings-source-indicator">
|
|
78
|
+
Currently active
|
|
79
|
+
</span>
|
|
107
80
|
</div>
|
|
108
81
|
<% end %>
|
|
109
82
|
</div>
|
|
@@ -114,10 +87,15 @@
|
|
|
114
87
|
<dialog class="ultra-settings-dialog" closedby="any">
|
|
115
88
|
<div class="ultra-settings-dialog-header">
|
|
116
89
|
<div class="ultra-settings-dialog-title"></div>
|
|
117
|
-
|
|
90
|
+
|
|
91
|
+
<button
|
|
92
|
+
class="ultra-settings-dialog-close"
|
|
93
|
+
onclick="this.closest('.ultra-settings-dialog').close();"
|
|
94
|
+
>
|
|
118
95
|
<%= close_icon %>
|
|
119
96
|
</button>
|
|
120
97
|
</div>
|
|
98
|
+
|
|
121
99
|
<div class="ultra-settings-dialog-body">
|
|
122
100
|
<code class="ultra-settings-field-data-value ultra-settings-dialog-value"></code>
|
|
123
101
|
</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,15 +4,15 @@ 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
18
|
# Initialize the application view with a color scheme.
|
|
@@ -25,10 +25,10 @@ module UltraSettings
|
|
|
25
25
|
|
|
26
26
|
# Render the HTML for the configuration settings UI.
|
|
27
27
|
#
|
|
28
|
-
# @param select_class [String]
|
|
29
|
-
# @param table_class [String]
|
|
28
|
+
# @param select_class [String] Deprecated; no longer used.
|
|
29
|
+
# @param table_class [String] Deprecated; no longer used.
|
|
30
30
|
# @return [String] The rendered HTML.
|
|
31
|
-
def render(select_class:
|
|
31
|
+
def render(select_class: nil, table_class: nil)
|
|
32
32
|
html = ViewHelper.erb_template("index.html.erb").result(binding)
|
|
33
33
|
html = html.html_safe if html.respond_to?(:html_safe)
|
|
34
34
|
html
|
|
@@ -52,10 +52,6 @@ module UltraSettings
|
|
|
52
52
|
|
|
53
53
|
private
|
|
54
54
|
|
|
55
|
-
def html_escape(value)
|
|
56
|
-
ERB::Util.html_escape(value)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
55
|
def javascript
|
|
60
56
|
ViewHelper.read_app_file("application.js")
|
|
61
57
|
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module UltraSettings
|
|
4
|
+
class AuditDataSources
|
|
5
|
+
class << self
|
|
6
|
+
# Find environment variables that are set but have the same value as their default.
|
|
7
|
+
# These environment variables could potentially be removed since they're not changing behavior.
|
|
8
|
+
#
|
|
9
|
+
# @return [Array<Array<(String, Object)>>] An array of tuples containing environment variable names and their default values
|
|
10
|
+
def unnecessary_env_vars
|
|
11
|
+
env_vars_at_default = []
|
|
12
|
+
each_configuration do |config|
|
|
13
|
+
each_field_using_source(config, :env) do |field|
|
|
14
|
+
value = config[field.name]
|
|
15
|
+
default_value = default_config_value(config, field)
|
|
16
|
+
env_vars_at_default << [field.env_var, default_value] if default_value == value
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
env_vars_at_default
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Find runtime settings that are set but have the same value as their default.
|
|
23
|
+
# These runtime settings could potentially be removed since they're not changing behavior.
|
|
24
|
+
#
|
|
25
|
+
# @return [Array<Array<(String, Object)>>] An array of tuples containing runtime setting names and their default values
|
|
26
|
+
def unnecessary_runtime_settings
|
|
27
|
+
unnecessary_runtime_settings = []
|
|
28
|
+
each_configuration do |config|
|
|
29
|
+
each_field_using_source(config, :settings) do |field|
|
|
30
|
+
value = config[field.name]
|
|
31
|
+
default_value = default_config_value(config, field)
|
|
32
|
+
unnecessary_runtime_settings << [field.runtime_setting, default_value] if default_value == value
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
unnecessary_runtime_settings
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Find environment variables that could be moved to runtime settings.
|
|
39
|
+
# These are non-default environment variable values where a runtime setting is also available.
|
|
40
|
+
#
|
|
41
|
+
# @return [Array<Array<(String, String, Object)>>] An array of tuples containing environment variable name, runtime setting name, and current value
|
|
42
|
+
def env_vars_can_be_runtime_setting
|
|
43
|
+
env_vars_can_be_runtime = []
|
|
44
|
+
each_configuration do |config|
|
|
45
|
+
each_field_using_source(config, :env) do |field|
|
|
46
|
+
value = config[field.name]
|
|
47
|
+
default_value = default_config_value(config, field)
|
|
48
|
+
next unless field.runtime_setting && value != default_value
|
|
49
|
+
|
|
50
|
+
env_vars_can_be_runtime << [field.env_var, field.runtime_setting, value]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
env_vars_can_be_runtime
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Find environment variables being used that don't have default values defined.
|
|
57
|
+
# These configurations require an environment variable to be set.
|
|
58
|
+
#
|
|
59
|
+
# @return [Array<Array<(String, Symbol, String, Object)>>] An array of tuples containing class name, field name, environment variable name, and current value
|
|
60
|
+
def env_vars_without_default
|
|
61
|
+
no_default_env_var_fields = []
|
|
62
|
+
each_configuration do |config|
|
|
63
|
+
each_field_using_source(config, :env) do |field|
|
|
64
|
+
value = default_config_value(config, field)
|
|
65
|
+
if value.nil?
|
|
66
|
+
no_default_env_var_fields << [config.class.name, field.name, field.env_var, config[field.name]]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
no_default_env_var_fields
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
def each_configuration(&_block)
|
|
76
|
+
UltraSettings::Configuration.descendant_configurations.each do |config_class|
|
|
77
|
+
config = config_class.instance
|
|
78
|
+
yield config
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def each_field_using_source(config, source, &_block)
|
|
83
|
+
config.class.fields.each do |field|
|
|
84
|
+
next if field.secret?
|
|
85
|
+
next unless config.__source__(field.name) == source
|
|
86
|
+
|
|
87
|
+
yield field
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def default_config_value(config, field)
|
|
92
|
+
yaml_value = config.__value_from_source__(field.name, :yaml)
|
|
93
|
+
default_value = config.__value_from_source__(field.name, :default)
|
|
94
|
+
yaml_value || default_value
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -9,8 +9,21 @@ module UltraSettings
|
|
|
9
9
|
|
|
10
10
|
@env_var_prefix = nil
|
|
11
11
|
@runtime_setting_prefix = nil
|
|
12
|
+
@description = nil
|
|
13
|
+
@descendants = []
|
|
12
14
|
|
|
13
15
|
class << self
|
|
16
|
+
# Set a description for the configuration. This is optional. It will be displayed
|
|
17
|
+
# in the web UI if provided. On large projects with many configurations, this can
|
|
18
|
+
# help identify the purpose of each configuration.
|
|
19
|
+
#
|
|
20
|
+
# @param text [String] The description text.
|
|
21
|
+
# @return [void]
|
|
22
|
+
def description(text = nil)
|
|
23
|
+
@description = text.to_s.strip unless text.nil?
|
|
24
|
+
@description
|
|
25
|
+
end
|
|
26
|
+
|
|
14
27
|
# Define a field on the configuration. This will create a getter method for the field.
|
|
15
28
|
# The field value will be read from the environment, runtime settings, or a YAML file
|
|
16
29
|
# and coerced to the specified type. Empty strings will be converted to nil.
|
|
@@ -27,14 +40,19 @@ module UltraSettings
|
|
|
27
40
|
# @param static [Boolean] If true, the field value should never be changed. This is useful for
|
|
28
41
|
# fields that are used at startup to set static values in the application. Static field cannot
|
|
29
42
|
# be read from runtime settings.
|
|
30
|
-
# @param
|
|
43
|
+
# @param secret [Boolean, Proc] If true, the field value will be obscured in the output of
|
|
44
|
+
# to_hash. If a proc is provided, it will be called to determine if the field is secret.
|
|
45
|
+
# @param runtime_setting [String, Symbol, Boolean] The name of the runtime setting to use for the field.
|
|
31
46
|
# 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
|
-
#
|
|
47
|
+
# (i.e. MyServiceConfiguration#foo becomes "my_service.foo"). If set to false, runtime settings
|
|
48
|
+
# will be ignored for this field. This can be set to true to use the default name.
|
|
49
|
+
# @param env_var [String, Symbol, Boolean] The name of the environment variable to use for the field.
|
|
34
50
|
# 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
|
-
#
|
|
51
|
+
# all in uppercase (i.e. MyServiceConfiguration#foo becomes "MY_SERVICE_FOO"). If set to false,
|
|
52
|
+
# environment variables will be ignored for this field. This can be set to true to use the default name.
|
|
53
|
+
# @param yaml_key [String, Symbol, Boolean] The name of the YAML key to use for the field. By default
|
|
54
|
+
# this is the name of the field. If set to false, YAML configuration will be ignored for this field.
|
|
55
|
+
# This can be set to true to use the default name.
|
|
38
56
|
# @return [void]
|
|
39
57
|
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
58
|
name = name.to_s
|
|
@@ -67,7 +85,8 @@ module UltraSettings
|
|
|
67
85
|
secret: secret
|
|
68
86
|
)
|
|
69
87
|
|
|
70
|
-
|
|
88
|
+
caller_location = caller_locations(1, 1).first
|
|
89
|
+
class_eval <<~RUBY, caller_location.path, caller_location.lineno # rubocop:disable Security/Eval, Style/EvalWithLocation
|
|
71
90
|
def #{name}
|
|
72
91
|
__get_value__(#{name.inspect})
|
|
73
92
|
end
|
|
@@ -93,7 +112,7 @@ module UltraSettings
|
|
|
93
112
|
name = name.to_s
|
|
94
113
|
return true if defined_fields.include?(name)
|
|
95
114
|
|
|
96
|
-
if superclass
|
|
115
|
+
if superclass < Configuration
|
|
97
116
|
superclass.include_field?(name)
|
|
98
117
|
else
|
|
99
118
|
false
|
|
@@ -145,7 +164,7 @@ module UltraSettings
|
|
|
145
164
|
# directory (i.e. MyServiceConfiguration has a default config path of
|
|
146
165
|
# "my_service.yml").
|
|
147
166
|
#
|
|
148
|
-
# @param value [String, Pathname]
|
|
167
|
+
# @param value [String, Pathname, false, nil]
|
|
149
168
|
# @return [void]
|
|
150
169
|
def configuration_file=(value)
|
|
151
170
|
value = nil if value == false
|
|
@@ -352,12 +371,30 @@ module UltraSettings
|
|
|
352
371
|
YamlConfig.new(configuration_file, yaml_config_env).to_h
|
|
353
372
|
end
|
|
354
373
|
|
|
374
|
+
# Get all descendant configuration classes (subclasses and their subclasses, recursively).
|
|
375
|
+
#
|
|
376
|
+
# @return [Array<Class>] All classes that inherit from this class.
|
|
377
|
+
def descendant_configurations
|
|
378
|
+
@descendants ||= []
|
|
379
|
+
@descendants.flat_map { |subclass| [subclass] + subclass.descendant_configurations }
|
|
380
|
+
end
|
|
381
|
+
|
|
355
382
|
private
|
|
356
383
|
|
|
384
|
+
# Hook called when this class is inherited. Tracks all descendant classes.
|
|
385
|
+
#
|
|
386
|
+
# @param subclass [Class] The subclass that is inheriting from this class.
|
|
387
|
+
# @return [void]
|
|
388
|
+
def inherited(subclass)
|
|
389
|
+
super
|
|
390
|
+
@descendants ||= []
|
|
391
|
+
@descendants << subclass
|
|
392
|
+
end
|
|
393
|
+
|
|
357
394
|
def defined_fields
|
|
358
395
|
unless defined?(@defined_fields)
|
|
359
396
|
fields = {}
|
|
360
|
-
if superclass
|
|
397
|
+
if superclass < Configuration
|
|
361
398
|
fields = superclass.send(:defined_fields).dup
|
|
362
399
|
end
|
|
363
400
|
@defined_fields = fields
|
|
@@ -513,6 +550,22 @@ module UltraSettings
|
|
|
513
550
|
end
|
|
514
551
|
end
|
|
515
552
|
|
|
553
|
+
# Returns an array of the available data sources for the field.
|
|
554
|
+
#
|
|
555
|
+
# @param name [String, Symbol] the name of the field.
|
|
556
|
+
# @return [Array<Symbol>] The available sources (:env, :settings, :yaml, :default).
|
|
557
|
+
def __available_sources__(name)
|
|
558
|
+
field = self.class.send(:defined_fields)[name.to_s]
|
|
559
|
+
raise ArgumentError.new("Unknown field: #{name.inspect}") unless field
|
|
560
|
+
|
|
561
|
+
sources = []
|
|
562
|
+
sources << :env if field.env_var
|
|
563
|
+
sources << :settings if field.runtime_setting && UltraSettings.__runtime_settings__
|
|
564
|
+
sources << :yaml if field.yaml_key && self.class.configuration_file
|
|
565
|
+
sources << :default unless field.default.nil?
|
|
566
|
+
sources
|
|
567
|
+
end
|
|
568
|
+
|
|
516
569
|
# Output the current state of the configuration as a hash. If the field is marked as a secret,
|
|
517
570
|
# then the value will be a secure hash of the value instead of the value itself.
|
|
518
571
|
#
|
|
@@ -11,6 +11,8 @@ module UltraSettings
|
|
|
11
11
|
# <h1>Service Configuration</h1>
|
|
12
12
|
# <%= UltraSettings::ConfigurationView.new(ServiceConfiguration.instance).render %>
|
|
13
13
|
class ConfigurationView
|
|
14
|
+
include RenderHelper
|
|
15
|
+
|
|
14
16
|
# Initialize the configuration view with a configuration instance.
|
|
15
17
|
#
|
|
16
18
|
# @param configuration [UltraSettings::Configuration] The configuration instance to display.
|
|
@@ -38,10 +40,6 @@ module UltraSettings
|
|
|
38
40
|
|
|
39
41
|
private
|
|
40
42
|
|
|
41
|
-
def html_escape(value)
|
|
42
|
-
ERB::Util.html_escape(value)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
43
|
def display_value(value)
|
|
46
44
|
case value
|
|
47
45
|
when Time
|
|
@@ -147,8 +145,6 @@ module UltraSettings
|
|
|
147
145
|
HTML
|
|
148
146
|
end
|
|
149
147
|
|
|
150
|
-
private
|
|
151
|
-
|
|
152
148
|
def open_dialog_script
|
|
153
149
|
<<~JAVASCRIPT.gsub(/\s+/, " ").tr('"', "'")
|
|
154
150
|
this.closest('.ultra-settings-configuration').querySelector('.ultra-settings-dialog-title').textContent = this.dataset.label;
|
|
@@ -157,5 +153,44 @@ module UltraSettings
|
|
|
157
153
|
this.closest('.ultra-settings-configuration').querySelector('.ultra-settings-dialog-close').blur();
|
|
158
154
|
JAVASCRIPT
|
|
159
155
|
end
|
|
156
|
+
|
|
157
|
+
def source_priority
|
|
158
|
+
[:env, :settings, :yaml, :default]
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def source_overridden_by(current_source, active_source)
|
|
162
|
+
return nil if current_source == active_source
|
|
163
|
+
|
|
164
|
+
current_index = source_priority.index(current_source)
|
|
165
|
+
active_index = source_priority.index(active_source)
|
|
166
|
+
|
|
167
|
+
return nil if current_index.nil? || active_index.nil?
|
|
168
|
+
return nil if current_index < active_index
|
|
169
|
+
|
|
170
|
+
active_source
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def override_indicator(overridden_by_source)
|
|
174
|
+
source_names = {
|
|
175
|
+
env: "environment variable",
|
|
176
|
+
settings: "runtime setting",
|
|
177
|
+
yaml: "configuration file",
|
|
178
|
+
default: "default value"
|
|
179
|
+
}
|
|
180
|
+
<<~HTML
|
|
181
|
+
<span class="ultra-settings-source-override" title="Overridden by #{source_names[overridden_by_source]}">
|
|
182
|
+
#{warning_icon(14)}
|
|
183
|
+
<span class="ultra-settings-source-override-text">Overridden by #{source_names[overridden_by_source]}</span>
|
|
184
|
+
</span>
|
|
185
|
+
HTML
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def warning_icon(size = 16)
|
|
189
|
+
<<~HTML
|
|
190
|
+
<svg width="#{size}" height="#{size}" fill="currentColor" viewBox="0 0 16 16">
|
|
191
|
+
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5m.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2"/>
|
|
192
|
+
</svg>
|
|
193
|
+
HTML
|
|
194
|
+
end
|
|
160
195
|
end
|
|
161
196
|
end
|
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,
|
|
@@ -23,6 +23,8 @@ module UltraSettings
|
|
|
23
23
|
|
|
24
24
|
app_config_dir = Rails.root.join(directory)
|
|
25
25
|
app_config_dir.glob("**/*_configuration.rb").each do |file_path|
|
|
26
|
+
next unless file_path.file? && file_path.readable?
|
|
27
|
+
|
|
26
28
|
relative_path = file_path.relative_path_from(app_config_dir).to_s
|
|
27
29
|
class_name = relative_path.chomp(".rb").classify
|
|
28
30
|
unless UltraSettings.added?(class_name)
|
|
@@ -32,5 +34,11 @@ module UltraSettings
|
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
end
|
|
37
|
+
|
|
38
|
+
rake_tasks do
|
|
39
|
+
Dir.glob(File.expand_path("tasks/*.rake", __dir__)).each do |rake_file|
|
|
40
|
+
load rake_file
|
|
41
|
+
end
|
|
42
|
+
end
|
|
35
43
|
end
|
|
36
44
|
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
|