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.
@@ -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,61 +53,84 @@
48
53
  <% end %>
49
54
 
50
55
  <div class="ultra-settings-field-sources">
51
- <% if field.env_var && !configuration.class.environment_variables_disabled? %>
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">Currently active</span>
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 field.runtime_setting && !configuration.class.runtime_settings_disabled? %>
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">Currently active</span>
86
+ <span class="ultra-settings-source-indicator">
87
+ Currently active
88
+ </span>
75
89
  <% end %>
76
- <% edit_url = UltraSettings.runtime_settings_url(field.runtime_setting, field.type) %>
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 href="<%= html_escape(edit_url) %>" class="ultra-settings-edit-link" title="Edit <%= html_escape(field.runtime_setting) %>">
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 field.yaml_key && !configuration.class.yaml_config_disabled? %>
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">Currently active</span>
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 !field.default.nil? || source == :default %>
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">Currently active</span>
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
- <button class="ultra-settings-dialog-close" onclick="this.closest('.ultra-settings-dialog').close();">
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
- <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,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 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
+ # 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
- def render(select_class: "ultra-settings-select", table_class: "")
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
- # Usage:
7
- # class TestClass
8
- # extend UltraSettings::ConfigHelper
9
- # configuration_class TestConfiguration
10
- # end
11
- # TestClass.config => TestConfiguration.instance
12
- # TestClass.new.config => TestConfiguration.instance
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 runtime_setting [String, Symbol] The name of the runtime setting to use for the field.
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
- # @param env_var [String, Symbol] The name of the environment variable to use for the field.
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
- # @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.
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: (static ? nil : construct_runtime_setting(name, 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;
@@ -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 cquse unexpected behavior if the runtime settings
7
- # engine has not yet been initialized. For instance, if your runtime settings enging reads from
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 a the runtime settings at the beginning of initialization
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
- # UltraSettings.runtime_settings = UltraSettings::UninitializedRuntimeSettings
18
- # ActiveSupport.on_load(:active_record) do
19
- # UltraSettings.runtime_settings = SuperSettings
20
- # end
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
- # `:dark`, or `:system`. The default is `:light`.
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