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.
@@ -1,28 +1,28 @@
1
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>
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">secret</span>
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">static</span>
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"><%= html_escape(secret_value(configuration[field.name])) %></code>
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"><%= html_escape(display_value(configuration[field.name])) %></code>
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
- <% if field.env_var && !configuration.class.environment_variables_disabled? %>
52
- <div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :env %>">
53
- <span class="ultra-settings-source-type">
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 field.runtime_setting && !configuration.class.runtime_settings_disabled? %>
67
- <div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :settings %>">
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 field.yaml_key && !configuration.class.yaml_config_disabled? %>
86
- <div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :yaml %>">
87
- <span class="ultra-settings-source-type">Configuration File</span>
88
- <code class="ultra-settings-source-value">
89
- <%= field.yaml_key %>
90
- <%= show_defined_value(field.yaml_key, configuration.__value_from_source__(field.name, :yaml), field.secret?) %>
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 !field.default.nil? || source == :default %>
99
- <div class="ultra-settings-source <%= 'ultra-settings-source-active' if source == :default %>">
100
- <span class="ultra-settings-source-type">Default Value</span>
101
- <code class="ultra-settings-source-value">
102
- <%= show_defined_value("Default Value", field.default, field.secret?) %>
103
- </code>
104
- <% if source == :default %>
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
- <button class="ultra-settings-dialog-close" onclick="this.closest('.ultra-settings-dialog').close();">
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
- <form onsubmit="return false" style="margin-bottom: 0.5rem;">
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
- <option value="config-<%= html_escape(name) %>"><%= html_escape(UltraSettings.send(name).class.name) %></option>
12
- <% end %>
13
- </select>
14
- </form>
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
- <% UltraSettings.__configuration_names__.sort.each do |name| %>
18
- <% configuration = UltraSettings.send(name) %>
19
- <% next if configuration.class.fields.empty? %>
20
-
21
- <div class="ultra-settings-configuration" id="config-<%= html_escape(name) %>" style="display:none;">
22
- <%= UltraSettings::ConfigurationView.new(configuration).render(table_class: table_class) %>
23
- </div>
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 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`.
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(select_class: 'form-control') %>
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] CSS class for the select element.
29
- # @param table_class [String] CSS class for the table element (for backwards compatibility).
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: "ultra-settings-select", table_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 runtime_setting [String, Symbol] The name of the runtime setting to use for the field.
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
- # @param env_var [String, Symbol] The name of the environment variable to use for the field.
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
- # @param yaml_key [String, Symbol] The name of the YAML key to use for the field. By default
37
- # this is the name of the field.
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
- class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
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 <= Configuration
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 <= Configuration
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
@@ -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