ultra_settings 2.8.1 → 2.9.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -2
  3. data/README.md +40 -1
  4. data/VERSION +1 -1
  5. data/app/AGENTS.md +7 -0
  6. data/app/_config_description.html.erb +22 -25
  7. data/app/_config_list.html.erb +2 -14
  8. data/app/_data_source.html.erb +43 -29
  9. data/app/application.css +1019 -340
  10. data/app/application.js +825 -90
  11. data/app/application_vars.css.erb +136 -91
  12. data/app/configuration.html.erb +59 -51
  13. data/app/index.html.erb +164 -20
  14. data/app/layout.css +81 -16
  15. data/app/layout.html.erb +67 -5
  16. data/app/layout_vars.css.erb +29 -5
  17. data/app/locales/ar.json +71 -0
  18. data/app/locales/cs.json +71 -0
  19. data/app/locales/da.json +71 -0
  20. data/app/locales/de.json +71 -0
  21. data/app/locales/el.json +71 -0
  22. data/app/locales/en.json +85 -0
  23. data/app/locales/es.json +71 -0
  24. data/app/locales/fa.json +71 -0
  25. data/app/locales/fr.json +71 -0
  26. data/app/locales/gd.json +71 -0
  27. data/app/locales/he.json +71 -0
  28. data/app/locales/hi.json +71 -0
  29. data/app/locales/it.json +71 -0
  30. data/app/locales/ja.json +71 -0
  31. data/app/locales/ko.json +71 -0
  32. data/app/locales/lt.json +71 -0
  33. data/app/locales/nb.json +71 -0
  34. data/app/locales/nl.json +71 -0
  35. data/app/locales/pl.json +71 -0
  36. data/app/locales/pt-br.json +71 -0
  37. data/app/locales/pt.json +71 -0
  38. data/app/locales/ru.json +71 -0
  39. data/app/locales/sv.json +71 -0
  40. data/app/locales/ta.json +71 -0
  41. data/app/locales/tr.json +71 -0
  42. data/app/locales/uk.json +71 -0
  43. data/app/locales/ur.json +71 -0
  44. data/app/locales/vi.json +71 -0
  45. data/app/locales/zh-cn.json +71 -0
  46. data/app/locales/zh-tw.json +71 -0
  47. data/lib/ultra_settings/application_view.rb +21 -3
  48. data/lib/ultra_settings/coerce.rb +0 -6
  49. data/lib/ultra_settings/config_helper.rb +4 -4
  50. data/lib/ultra_settings/configuration.rb +14 -4
  51. data/lib/ultra_settings/configuration_view.rb +114 -92
  52. data/lib/ultra_settings/mini_i18n.rb +110 -0
  53. data/lib/ultra_settings/rack_app.rb +51 -1
  54. data/lib/ultra_settings/version.rb +1 -1
  55. data/lib/ultra_settings/web_view.rb +33 -2
  56. data/lib/ultra_settings.rb +56 -22
  57. data/ultra_settings.gemspec +1 -0
  58. metadata +33 -3
  59. data/AGENTS.md +0 -191
  60. data/app/_select_menu.html.erb +0 -53
@@ -8,24 +8,6 @@ require "singleton"
8
8
  require "digest"
9
9
  require "uri"
10
10
 
11
- require_relative "ultra_settings/configuration"
12
- require_relative "ultra_settings/coerce"
13
- require_relative "ultra_settings/config_helper"
14
- require_relative "ultra_settings/field"
15
- require_relative "ultra_settings/rack_app"
16
- require_relative "ultra_settings/view_helper"
17
- require_relative "ultra_settings/render_helper"
18
- require_relative "ultra_settings/web_view"
19
- require_relative "ultra_settings/application_view"
20
- require_relative "ultra_settings/configuration_view"
21
- require_relative "ultra_settings/uninitialized_runtime_settings"
22
- require_relative "ultra_settings/yaml_config"
23
- require_relative "ultra_settings/version"
24
-
25
- if defined?(Rails::Railtie)
26
- require_relative "ultra_settings/railtie"
27
- end
28
-
29
11
  # This is the root namespace for UltraSettings. You can add configurations to
30
12
  # this namespace using the add method.
31
13
  #
@@ -33,13 +15,29 @@ end
33
15
  # UltraSettings.add(:test)
34
16
  # UltraSettings.test # => TestConfiguration.instance
35
17
  module UltraSettings
36
- VALID_NAME__PATTERN = /\A[a-z_][a-zA-Z0-9_]*\z/
18
+ autoload :Configuration, File.join(__dir__, "ultra_settings/configuration")
19
+ autoload :Coerce, File.join(__dir__, "ultra_settings/coerce")
20
+ autoload :ConfigHelper, File.join(__dir__, "ultra_settings/config_helper")
21
+ autoload :Field, File.join(__dir__, "ultra_settings/field")
22
+ autoload :MiniI18n, File.join(__dir__, "ultra_settings/mini_i18n")
23
+ autoload :RackApp, File.join(__dir__, "ultra_settings/rack_app")
24
+ autoload :ViewHelper, File.join(__dir__, "ultra_settings/view_helper")
25
+ autoload :RenderHelper, File.join(__dir__, "ultra_settings/render_helper")
26
+ autoload :WebView, File.join(__dir__, "ultra_settings/web_view")
27
+ autoload :ApplicationView, File.join(__dir__, "ultra_settings/application_view")
28
+ autoload :ConfigurationView, File.join(__dir__, "ultra_settings/configuration_view")
29
+ autoload :UninitializedRuntimeSettings, File.join(__dir__, "ultra_settings/uninitialized_runtime_settings")
30
+ autoload :YamlConfig, File.join(__dir__, "ultra_settings/yaml_config")
31
+ autoload :VERSION, File.join(__dir__, "ultra_settings/version")
32
+
33
+ VALID_NAME_PATTERN = /\A[a-z_][a-zA-Z0-9_]*\z/
37
34
 
38
35
  @configurations = {}
39
36
  @mutex = Mutex.new
40
37
  @runtime_settings = nil
41
38
  @runtime_settings_url = nil
42
39
  @runtime_settings_secure = true
40
+ @super_settings_api_path = nil
43
41
 
44
42
  class << self
45
43
  # Adds a configuration to the root namespace. The configuration will be
@@ -52,7 +50,7 @@ module UltraSettings
52
50
  # @return [void]
53
51
  def add(name, klass = nil)
54
52
  name = name.to_s
55
- unless name.match?(VALID_NAME__PATTERN)
53
+ unless name.match?(VALID_NAME_PATTERN)
56
54
  raise ArgumentError.new("Invalid configuration name: #{name.inspect}")
57
55
  end
58
56
 
@@ -219,6 +217,38 @@ module UltraSettings
219
217
  @runtime_settings_secure
220
218
  end
221
219
 
220
+ # Set the URL path where the SuperSettings API is mounted. When this is set,
221
+ # the web UI will check the SuperSettings API at this path for edit access
222
+ # and load the SuperSettings api.js client library to enable inline editing
223
+ # of runtime settings.
224
+ #
225
+ # The path must be a relative URL path starting with "/". Authorization is
226
+ # handled entirely by SuperSettings — the browser makes a GET request to
227
+ # the /authorized endpoint to check access and all API calls go directly
228
+ # to the SuperSettings endpoint.
229
+ #
230
+ # @param value [String, nil] The relative URL path where SuperSettings is mounted.
231
+ # @return [void]
232
+ def super_settings_api_path=(value)
233
+ raise "super_settings gem is required for super_settings_api_path" unless defined?(::SuperSettings)
234
+
235
+ if value.nil?
236
+ @super_settings_api_path = nil
237
+ return
238
+ end
239
+
240
+ value = value.to_s
241
+ raise ArgumentError, "super_settings_api_path must be a relative URL path starting with '/'" unless value.start_with?("/")
242
+
243
+ @super_settings_api_path = value.chomp("/")
244
+ self.runtime_settings = ::SuperSettings
245
+ end
246
+
247
+ # Get the URL path where the SuperSettings API is mounted.
248
+ #
249
+ # @return [String, nil]
250
+ attr_reader :super_settings_api_path
251
+
222
252
  # Set whether fields should be considered secret by default.
223
253
  #
224
254
  # @param value [Boolean] Whether fields should be secret by default.
@@ -304,14 +334,18 @@ module UltraSettings
304
334
  if name.respond_to?(:classify)
305
335
  name.classify
306
336
  else
307
- name.split("_").map(&:capitalize).join.gsub("/", "::")
337
+ name.to_s.split("_").map(&:capitalize).join.gsub("/", "::")
308
338
  end
309
339
  end
310
340
 
311
341
  def constantize(class_name)
312
- class_name.split("::").reduce(Object) do |mod, name|
342
+ class_name.to_s.split("::").reduce(Object) do |mod, name|
313
343
  mod.const_get(name)
314
344
  end
315
345
  end
316
346
  end
317
347
  end
348
+
349
+ if defined?(Rails::Railtie)
350
+ require_relative "ultra_settings/railtie"
351
+ end
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
20
  ignore_files = %w[
21
21
  .
22
+ AGENTS.md
22
23
  Appraisals
23
24
  Gemfile
24
25
  Gemfile.lock
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ultra_settings
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.1
4
+ version: 2.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand
@@ -29,16 +29,15 @@ executables: []
29
29
  extensions: []
30
30
  extra_rdoc_files: []
31
31
  files:
32
- - AGENTS.md
33
32
  - ARCHITECTURE.md
34
33
  - CHANGELOG.md
35
34
  - MIT-LICENSE.txt
36
35
  - README.md
37
36
  - VERSION
37
+ - app/AGENTS.md
38
38
  - app/_config_description.html.erb
39
39
  - app/_config_list.html.erb
40
40
  - app/_data_source.html.erb
41
- - app/_select_menu.html.erb
42
41
  - app/application.css
43
42
  - app/application.js
44
43
  - app/application_vars.css.erb
@@ -47,6 +46,36 @@ files:
47
46
  - app/layout.css
48
47
  - app/layout.html.erb
49
48
  - app/layout_vars.css.erb
49
+ - app/locales/ar.json
50
+ - app/locales/cs.json
51
+ - app/locales/da.json
52
+ - app/locales/de.json
53
+ - app/locales/el.json
54
+ - app/locales/en.json
55
+ - app/locales/es.json
56
+ - app/locales/fa.json
57
+ - app/locales/fr.json
58
+ - app/locales/gd.json
59
+ - app/locales/he.json
60
+ - app/locales/hi.json
61
+ - app/locales/it.json
62
+ - app/locales/ja.json
63
+ - app/locales/ko.json
64
+ - app/locales/lt.json
65
+ - app/locales/nb.json
66
+ - app/locales/nl.json
67
+ - app/locales/pl.json
68
+ - app/locales/pt-br.json
69
+ - app/locales/pt.json
70
+ - app/locales/ru.json
71
+ - app/locales/sv.json
72
+ - app/locales/ta.json
73
+ - app/locales/tr.json
74
+ - app/locales/uk.json
75
+ - app/locales/ur.json
76
+ - app/locales/vi.json
77
+ - app/locales/zh-cn.json
78
+ - app/locales/zh-tw.json
50
79
  - lib/ultra_settings.rb
51
80
  - lib/ultra_settings/application_view.rb
52
81
  - lib/ultra_settings/audit_data_sources.rb
@@ -55,6 +84,7 @@ files:
55
84
  - lib/ultra_settings/configuration.rb
56
85
  - lib/ultra_settings/configuration_view.rb
57
86
  - lib/ultra_settings/field.rb
87
+ - lib/ultra_settings/mini_i18n.rb
58
88
  - lib/ultra_settings/rack_app.rb
59
89
  - lib/ultra_settings/railtie.rb
60
90
  - lib/ultra_settings/render_helper.rb
data/AGENTS.md DELETED
@@ -1,191 +0,0 @@
1
- # UltraSettings AI Coding Agent Instructions
2
-
3
- ## Project Overview
4
- UltraSettings is a Ruby gem for managing application configuration from multiple sources (environment variables, runtime settings, YAML files) with built-in type safety, validation, and a web UI for documentation.
5
-
6
- ## Core Architecture
7
-
8
- ### Configuration Classes (Singleton Pattern)
9
- - All configuration classes extend `UltraSettings::Configuration` and use Ruby's `Singleton` module
10
- - Each configuration class has one instance accessible via `.instance`
11
- - Register configurations globally: `UltraSettings.add(:test)` creates `UltraSettings.test` accessor
12
- - Configuration classes support inheritance; subclasses maintain separate singleton instances
13
-
14
- ### Field Definition DSL
15
- Fields are defined using the `field` class method with extensive options:
16
- ```ruby
17
- field :timeout, type: :float, default: 1.0,
18
- default_if: ->(val) { val <= 0 },
19
- description: "Network timeout in seconds",
20
- secret: false
21
- ```
22
-
23
- ### Multi-Source Value Resolution (Precedence Order)
24
- 1. **Environment Variables** - Highest priority (e.g., `MY_SERVICE_TIMEOUT`)
25
- 2. **Runtime Settings** - Dynamic configuration from external stores (e.g., Redis, `super_settings` gem)
26
- 3. **YAML Files** - File-based defaults with ERB support
27
- 4. **Default Values** - Fallback specified in field definition
28
-
29
- ### Type System
30
- Supported types: `:string` (default), `:symbol`, `:integer`, `:float`, `:boolean`, `:datetime`, `:array`
31
- - Boolean fields auto-generate `?` predicate methods (e.g., `enabled?`)
32
- - Array type parses CSV strings from env vars, supports proper arrays from YAML
33
- - Empty strings coerce to `nil` across all sources
34
-
35
- ## Key Conventions
36
-
37
- ### Naming Patterns
38
- - **Configuration class names**: Must end in `Configuration` (e.g., `MyServiceConfiguration`)
39
- - **Field names**: Match `/\A[a-z_][a-zA-Z0-9_]*\z/` pattern (snake_case)
40
- - **Environment variable defaults**: `MY_SERVICE_FOO` for `MyServiceConfiguration#foo`
41
- - **Runtime setting defaults**: `my_service.foo` (lowercase with dots)
42
- - **YAML keys**: Match field name by default
43
-
44
- ### Customization Attributes
45
- Set on configuration classes to override defaults:
46
- - `env_var_prefix`, `env_var_delimiter`, `env_var_upcase` - Control environment variable naming
47
- - `runtime_setting_prefix`, `runtime_setting_delimiter`, `runtime_setting_upcase` - Control runtime setting naming
48
- - `configuration_file` - Specify explicit YAML file path
49
- - `fields_secret_by_default` - Default secret status (true by default for security)
50
- - Disable sources entirely: `environment_variables_disabled`, `runtime_settings_disabled`, `yaml_config_disabled`
51
-
52
- ### Thread Safety
53
- - All memoized values protected by `Mutex`
54
- - Override values are thread-local (keyed by `Thread.current.object_id`)
55
- - Static fields are cached permanently after first access
56
-
57
- ## Testing Patterns
58
-
59
- ### Test Setup (spec/spec_helper.rb)
60
- ```ruby
61
- # Configure YAML path and environment
62
- UltraSettings.yaml_config_path = Pathname.new(__dir__) + "config"
63
- UltraSettings.yaml_config_env = "test"
64
-
65
- # Register configurations
66
- UltraSettings.add(:test)
67
- ```
68
-
69
- ### Overriding Configuration Values
70
- Use `override!` method for temporary value changes in tests:
71
- ```ruby
72
- # Via UltraSettings module
73
- UltraSettings.override!(test: {foo: "bar"}) { ... }
74
-
75
- # Via configuration class
76
- TestConfiguration.override!(foo: "bar") { ... }
77
-
78
- # Via configuration instance
79
- TestConfiguration.instance.override!(foo: "bar") { ... }
80
- ```
81
-
82
- ### RSpec Integration Example
83
- ```ruby
84
- RSpec.configure do |config|
85
- config.around do |example|
86
- if example.metadata[:ultra_settings]
87
- UltraSettings.override!(example.metadata[:ultra_settings]) do
88
- example.run
89
- end
90
- end
91
- end
92
- end
93
- ```
94
-
95
- ### Climate Control for Environment Variables
96
- Uses `climate_control` gem to safely modify environment variables in tests:
97
- ```ruby
98
- RSpec.describe "config", env: {TIMEOUT: "5"} do
99
- # Test with environment variable set
100
- end
101
- ```
102
-
103
- ## Web UI Features
104
-
105
- ### Mounting the Rack App
106
- ```ruby
107
- # config.ru or Rails routes
108
- mount UltraSettings::RackApp.new(color_scheme: :system), at: "/settings"
109
- ```
110
-
111
- ### Key Capabilities
112
- - Displays all registered configurations with descriptions
113
- - Shows field metadata: type, description, sources, but **NOT actual values**
114
- - Respects secret field marking (values hidden)
115
- - Includes links to edit runtime settings via `UltraSettings.runtime_settings_url`
116
- - Views in `lib/ultra_settings/*_view.rb` use ERB templates from `app/` directory
117
-
118
- ## Common Development Workflows
119
-
120
- ### Running Tests
121
- ```bash
122
- bundle exec rake spec # Default task runs all specs
123
- bundle exec rspec spec/ultra_settings/configuration_spec.rb # Specific file
124
- ```
125
-
126
- ### Checking Code Style
127
- Uses Standard Ruby style guide (testdouble/standard):
128
- ```bash
129
- bundle exec standardrb --fix
130
- ```
131
-
132
- ### Release Process
133
- - Can only release from `main` branch (enforced in Rakefile)
134
- - Version stored in `VERSION` file (no hardcoded version in gemspec)
135
-
136
- ## Special Features
137
-
138
- ### Static Fields
139
- - Marked with `static: true`, values never change after first access
140
- - Cannot be set from runtime settings (initialization-time only)
141
- - Use for settings referenced during app boot
142
-
143
- ### Secret Fields
144
- - All fields secret by default unless `fields_secret_by_default = false`
145
- - Masked in `__to_hash__` output
146
- - Runtime settings disabled on secret fields unless `UltraSettings.runtime_settings_secure = true`
147
- - Secret status can be dynamic via Proc
148
-
149
- ### Conditional Defaults
150
- Use `default_if` with Proc or method name to apply defaults based on loaded value:
151
- ```ruby
152
- field :timeout, default: 1.0, default_if: ->(val) { val <= 0 }
153
- ```
154
-
155
- ### Introspection Methods
156
- - `__source__(name)` - Returns which source provided the value (`:env`, `:runtime`, `:yaml`, `:default`)
157
- - `__value_from_source__(name, source)` - Fetch value from specific source
158
- - `__to_hash__` - Serialize current configuration as hash (secrets masked)
159
-
160
- ## Important Implementation Details
161
-
162
- ### Dynamic Method Generation
163
- Fields create getter methods via `class_eval` for performance (avoids `method_missing`)
164
-
165
- ### YAML Configuration
166
- - Supports environment-specific sections (e.g., `development`, `test`, `production`)
167
- - Special `shared` section merged with environment-specific config
168
- - ERB templates evaluated before YAML parsing
169
- - Files searched in `UltraSettings.yaml_config_path`
170
-
171
- ### Runtime Settings Integration
172
- - Must implement `[]` method accepting string argument
173
- - Optional `array` method for native array support
174
- - Use `UninitializedRuntimeSettings` during boot to catch premature access
175
- - `super_settings` gem is recommended companion implementation
176
-
177
- ## File Organization
178
-
179
- - `lib/ultra_settings/configuration.rb` - Core configuration class (630 lines)
180
- - `lib/ultra_settings/field.rb` - Field metadata and resolution logic
181
- - `lib/ultra_settings/coerce.rb` - Type coercion utilities
182
- - `lib/ultra_settings/*.rb` - Supporting classes for web UI, YAML, helpers
183
- - `spec/test_configs/` - Example configuration classes for testing
184
- - `spec/config/` - YAML configuration files for tests
185
- - `app/` - ERB templates and assets for web UI
186
-
187
- ## Key Dependencies
188
- - Ruby >= 2.5
189
- - Development: `rspec`, `climate_control`, `nokogiri`, `bundler`
190
- - Optional: `super_settings` gem for runtime settings store
191
- - Rails integration via `railtie.rb` when Rails is detected
@@ -1,53 +0,0 @@
1
- <div class="ultra-settings-dropdown" id="config-dropdown">
2
- <button
3
- type="button"
4
- class="ultra-settings-dropdown-button"
5
- id="config-dropdown-button"
6
- >
7
- Select Configuration
8
- </button>
9
-
10
- <div
11
- class="ultra-settings-dropdown-menu"
12
- id="config-dropdown-menu"
13
- style="display: none;"
14
- >
15
- <div class="ultra-settings-search-container">
16
- <input
17
- type="text"
18
- id="config-search"
19
- placeholder="Search..."
20
- autocomplete="off"
21
- >
22
- </div>
23
-
24
- <ul class="ultra-settings-dropdown-list" id="config-list">
25
- <% configurations.each do |config| %>
26
- <% config_text = [
27
- config.class.name,
28
- config.class.description
29
- ]
30
- config.class.fields.each do |field|
31
- config_text << field.name
32
- config_text << field.description
33
- config_text << field.env_var
34
- config_text << field.runtime_setting
35
- end
36
- search_text = config_text.compact.join(" ").downcase %>
37
-
38
- <li
39
- class="ultra-settings-dropdown-item"
40
- data-value="config-<%= html_escape(config.class.name) %>"
41
- data-label="<%= html_escape(config.class.name) %>"
42
- data-search="<%= html_escape(search_text) %>"
43
- >
44
- <span class="ultra-settings-check-icon"></span>
45
-
46
- <span class="ultra-settings-config-name">
47
- <%= html_escape(config.class.name) %>
48
- </span>
49
- </li>
50
- <% end %>
51
- </ul>
52
- </div>
53
- </div>