ultra_settings 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ff5c5961b5070a9234049d2588bd59150e82d2cf87b46c0686067c6d72dd815
4
- data.tar.gz: 8df668372d8f6c7dd4c72ad55a789c8bd854bf0bfa0509a5e4a1da69bc74e93e
3
+ metadata.gz: 35efcdf391347182451bb26d3740e3d18c6e8c5bb4ff4d0100d0f418bb2eef7c
4
+ data.tar.gz: ed5721d08d451893fbce63317d92d024a7f2cff1e0c68616455b3343b62eb7a6
5
5
  SHA512:
6
- metadata.gz: 7734298e87bd1fa4b7649ee5ddc64a0ef50f0e6e439bda7eb2042af055141ad5147ee07187ea53bbafbd5372430674a442f77e8c0277099cc8ff9ad7e1b20429
7
- data.tar.gz: 565fc7f9071292490d41a634c1b0c89530cbc065f925d52c3dcc25d8e1009bf8fe45ed84aa809fe6ab150d20d79bd9f90f47524ee2a161dfc9d90fc60ae5fb65
6
+ metadata.gz: 4636930f294c4c28389da0ed2e627554b1128a92f7fd622a0ac6eb905e6295ae46c513c449a3552e548b840a24d1c05ea106d32a8896a603ce98c596b0d72313
7
+ data.tar.gz: f8aa279a14715cc6a5a170d43dbaa1e38a55104b92dd25c403fb3b24b4066048bdb69159bf54cf9a3fb44a4280ccd91dba524ddbdca783eabca678ac5f28460e
data/CHANGELOG.md CHANGED
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 2.1.0
8
+
9
+ ### Added
10
+
11
+ - Added option to specify the color scheme for the web UI when mounting the rack app to support dark mode.
12
+
13
+ ### Fixed
14
+
15
+ - Times stored as strings representing the seconds since the epoch are now correctly parsed as Time objects.
16
+
7
17
  ## 2.0.0
8
18
 
9
19
  ### Fixed
data/README.md CHANGED
@@ -346,6 +346,12 @@ mount Rack::Builder.new do
346
346
  end, at: "/ultra_settings"
347
347
  ```
348
348
 
349
+ You can specify the color scheme by setting by providing the `color_scheme` option to the `UltraSettings::RackApp` class. The default color scheme is `:light`. You can also set the scheme to `:dark` or `:system`.
350
+
351
+ ```ruby
352
+ UltraSettings::RackApp.new(color_scheme: :dark)
353
+ ```
354
+
349
355
  #### Embedding the Settings View in Admin Tools
350
356
 
351
357
  If you prefer to embed the settings view directly into your own admin tools or dashboard, you can use the `UltraSettings::ApplicationView` class to render the settings interface within your existing views:
@@ -421,6 +427,25 @@ end
421
427
 
422
428
  This approach keeps your tests clean and readable while allowing for flexible configuration management during testing.
423
429
 
430
+ ### Rollout percentages
431
+
432
+ A common usage of configuration is to control rollout of new features by specifying a percentage value and then testing if a random number is less than it. If you implement this pattern in your configuration, then you should use something like the [consistent_random](https://github.com/bdurand/consistent_random) gem to ensure you are generating consistent values without your units of work.
433
+
434
+ ```ruby
435
+ class MyServiceConfiguration < UltraSettings::Configuration
436
+ field :use_http2_percentage,
437
+ type: :float,
438
+ default: 0.0,
439
+ description: "Rollout percentage for using the new HTTP/2 driver"
440
+
441
+ # Using ConsistentRandom#rand instead of Kernel#rand to ensure that we
442
+ # get the same result within a request and don't oscillate back and forth
443
+ # every time we check if this is enabled.
444
+ def use_http2?
445
+ ConsistentRandom.new("MyServiceConfiguration.use_http2").rand < use_http2_percentage
446
+ end
447
+ end
448
+ ```
424
449
 
425
450
  ## Installation
426
451
 
@@ -446,6 +471,24 @@ Open a pull request on [GitHub](https://github.com/bdurand/ultra_settings).
446
471
 
447
472
  Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
448
473
 
474
+ You can start a local rack server to test the web UI by running
475
+
476
+ ```bash
477
+ bundle exec rackup
478
+ ```
479
+
480
+ You can test with some setting set by setting environment variable used in the test configuration.
481
+
482
+ ```bash
483
+ MY_SERVICE_HOST=host.example.com MY_SERVICE_TOKEN=secret bundle exec rspec
484
+ ```
485
+
486
+ You can test dark mode by setting the `COLOR_SCHEME` environment variable.
487
+
488
+ ```bash
489
+ COLOR_SCHEME=dark bundle exec rackup
490
+ ```
491
+
449
492
  ## License
450
493
 
451
494
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.0
1
+ 2.1.0
data/app/application.css CHANGED
@@ -15,29 +15,30 @@
15
15
 
16
16
  .ultra-settings-table thead th {
17
17
  vertical-align: bottom;
18
- border-bottom: 2px solid #dee2e6;
18
+ border-bottom: 2px solid var(--table-border-color);
19
+ background-color: var(--table-header-bg-color);
19
20
  }
20
21
 
21
22
  .ultra-settings-table td, .ultra-settings-table th {
22
23
  padding: 0.75rem;
23
24
  vertical-align: top;
24
- border-top: 1px solid #dee2e6;
25
+ border-top: 1px solid var(--table-border-color);
25
26
  }
26
27
 
27
28
  .ultra-settings-table tbody tr:nth-of-type(odd) {
28
- background-color: rgba(0, 0, 0, .03);
29
+ background-color: var(--alt-row-color);
29
30
  }
30
31
 
31
32
  .ultra-settings-table code {
32
33
  font-family: monospace;
33
34
  font-size: 0.9rem;
34
35
  display: inline;
35
- color: darkred;
36
+ color: var(--code-color);
36
37
  font-weight: 600;
37
38
  }
38
39
 
39
40
  .ultra-settings-table em {
40
- color: gray;
41
+ color: var(--em-color);
41
42
  font-style: italic;
42
43
  font-size: 0.9rem;
43
44
  }
@@ -49,13 +50,13 @@
49
50
  font-size: 1rem;
50
51
  font-weight: 400;
51
52
  line-height: 1.5;
52
- color: #212529;
53
- background-color: #fff;
53
+ color: var(--form-control-color);
54
+ background-color: var(--form-control-bg-color);
54
55
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
55
56
  background-repeat: no-repeat;
56
57
  background-position: right .75rem center;
57
58
  background-size: 16px 12px;
58
- border: 1px solid #ced4da;
59
+ border: 1px solid var(--form-control-border-color);
59
60
  border-radius: .25rem;
60
61
  transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
61
62
  -webkit-appearance: none;
@@ -0,0 +1,31 @@
1
+ <% unless color_scheme == :dark %>
2
+ .ultra-settings-configuration {
3
+ --table-header-bg-color: #fff;
4
+ --table-border-color: #dee2e6;
5
+ --alt-row-color: rgba(0, 0, 0, .05);
6
+ --form-control-color: #495057;
7
+ --form-control-bg-color: #fff;
8
+ --form-control-border-color: #ced4da;
9
+ --code-color: darkred;
10
+ --em-color: gray;
11
+ }
12
+ <% end %>
13
+
14
+ <% if color_scheme == :system %>
15
+ @media (prefers-color-scheme: dark) {
16
+ <% end %>
17
+ <% if color_scheme == :system || color_scheme == :dark %>
18
+ .ultra-settings-configuration {
19
+ --table-header-bg-color: #333;
20
+ --table-border-color: #555;
21
+ --alt-row-color: rgba(0, 0, 0, .30);
22
+ --form-control-color: #eee;
23
+ --form-control-bg-color: #666;
24
+ --form-control-border-color: #555;
25
+ --code-color: pink;
26
+ --em-color: #999;
27
+ }
28
+ <% end %>
29
+ <% if color_scheme == :system %>
30
+ }
31
+ <% end %>
@@ -20,7 +20,7 @@
20
20
  <th>Notes</th>
21
21
  </tr>
22
22
  </thead>
23
- <tbody>
23
+ <tbody translate="no">
24
24
  <% configuration.class.fields.each do |field| %>
25
25
  <% source = configuration.__source__(field.name) %>
26
26
  <tr>
data/app/layout.css CHANGED
@@ -1,12 +1,14 @@
1
- * {box-sizing:border-box;}
1
+ * {
2
+ box-sizing:border-box;
3
+ }
2
4
 
3
5
  body {
4
6
  font-family: sans-serif;
5
7
  font-size: 1rem;
6
8
  line-height: 1.5;
7
9
  text-align: left;
8
- color: #212529;
9
- background-color: #ffffff;
10
+ color: var(--text-color);
11
+ background-color: var(--background-color);
10
12
  margin: 0;
11
13
  padding: 0;
12
14
  }
data/app/layout.html.erb CHANGED
@@ -5,6 +5,7 @@
5
5
  <title>Application Configuration</title>
6
6
  <meta charset="utf-8">
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1">
8
+ <meta name="format-detection" content="telephone=no email=no date=no address=no">
8
9
  <style type="text/css">
9
10
  <%= @layout_css %>
10
11
  <%= css %>
@@ -0,0 +1,19 @@
1
+ <% unless color_scheme != :dark %>
2
+ :root {
3
+ --text-color: #212529;
4
+ --background-color: #ffffff;
5
+ }
6
+ <% end %>
7
+
8
+ <% if color_scheme == :system %>
9
+ @media (prefers-color-scheme: dark) {
10
+ <% end %>
11
+ <% if color_scheme == :system || color_scheme == :dark %>
12
+ :root {
13
+ --text-color: #fff;
14
+ --background-color: #333;
15
+ }
16
+ <% end %>
17
+ <% if color_scheme == :system %>
18
+ }
19
+ <% end %>
@@ -18,6 +18,8 @@ module UltraSettings
18
18
  ]).freeze
19
19
  # rubocop:enable Lint/BooleanSymbol
20
20
 
21
+ NUMERIC_REGEX = /\A-?\d+(?:\.\d+)?\z/
22
+
21
23
  class << self
22
24
  # Cast a value to a specific type.
23
25
  #
@@ -33,13 +35,19 @@ module UltraSettings
33
35
  when :float
34
36
  value.is_a?(Float) ? value : value.to_s&.to_f
35
37
  when :boolean
36
- Coerce.boolean(value)
38
+ boolean(value)
37
39
  when :datetime
38
- Coerce.time(value)
40
+ time(value)
39
41
  when :array
40
42
  Array(value).map(&:to_s)
41
43
  when :symbol
42
44
  value.to_s.to_sym
45
+ when :rollout
46
+ if numeric?(value)
47
+ value.to_f
48
+ else
49
+ boolean(value)
50
+ end
43
51
  else
44
52
  value.to_s
45
53
  end
@@ -50,13 +58,9 @@ module UltraSettings
50
58
  # @param value [Object]
51
59
  # @return [Boolean]
52
60
  def boolean(value)
53
- if value == false
54
- false
55
- elsif blank?(value)
56
- nil
57
- else
58
- !FALSE_VALUES.include?(value)
59
- end
61
+ return nil if blank?(value)
62
+
63
+ !FALSE_VALUES.include?(value)
60
64
  end
61
65
 
62
66
  # Cast a value to a Time object.
@@ -66,8 +70,9 @@ module UltraSettings
66
70
  def time(value)
67
71
  value = nil if value.nil? || value.to_s.empty?
68
72
  return nil if value.nil?
69
- time = if value.is_a?(Numeric)
70
- Time.at(value)
73
+
74
+ time = if numeric?(value)
75
+ Time.at(value.to_f)
71
76
  elsif value.respond_to?(:to_time)
72
77
  value.to_time
73
78
  else
@@ -79,9 +84,15 @@ module UltraSettings
79
84
  time
80
85
  end
81
86
 
87
+ # @return [Boolean] true if the value is a numeric type or a string representing a number.
88
+ def numeric?(value)
89
+ value.is_a?(Numeric) || (value.is_a?(String) && value.to_s.match?(NUMERIC_REGEX))
90
+ end
91
+
82
92
  # @return [Boolean] true if the value is nil or empty.
83
93
  def blank?(value)
84
94
  return true if value.nil?
95
+
85
96
  if value.respond_to?(:empty?)
86
97
  value.empty?
87
98
  else
@@ -64,7 +64,7 @@ module UltraSettings
64
64
  secret: secret
65
65
  )
66
66
 
67
- class_eval <<-RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
67
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1 # rubocop:disable Security/Eval
68
68
  def #{name}
69
69
  __get_value__(#{name.inspect})
70
70
  end
@@ -5,8 +5,9 @@ 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
- def initialize
8
+ def initialize(color_scheme: nil)
9
9
  @webview = nil
10
+ @color_scheme = color_scheme
10
11
  end
11
12
 
12
13
  def call(env)
@@ -19,7 +20,7 @@ module UltraSettings
19
20
  if ENV.fetch("RAILS_ENV", ENV.fetch("RACK_ENV", "development")) == "development"
20
21
  @webview = nil
21
22
  end
22
- @webview ||= WebView.new
23
+ @webview ||= WebView.new(color_scheme: @color_scheme)
23
24
  end
24
25
  end
25
26
  end
@@ -5,10 +5,13 @@ module UltraSettings
5
5
  class WebView
6
6
  attr_reader :css
7
7
 
8
- def initialize
8
+ # @param color_scheme [Symbol] The color scheme to use in the UI. This can be `:light`,
9
+ # `:dark`, or `:system`. The default is `:light`.
10
+ def initialize(color_scheme: :light)
11
+ color_scheme = (color_scheme || :light).to_sym
9
12
  @layout_template = erb_template("layout.html.erb")
10
- @layout_css = read_app_file("layout.css")
11
- @css = read_app_file("application.css")
13
+ @layout_css = layout_css(color_scheme)
14
+ @css = application_css(color_scheme)
12
15
  end
13
16
 
14
17
  def render_settings
@@ -32,5 +35,17 @@ module UltraSettings
32
35
  def app_dir
33
36
  File.expand_path(File.join("..", "..", "app"), __dir__)
34
37
  end
38
+
39
+ def layout_css(color_scheme)
40
+ vars = erb_template("layout_vars.css.erb").result(binding)
41
+ css = read_app_file("layout.css")
42
+ "#{vars}\n#{css}"
43
+ end
44
+
45
+ def application_css(color_scheme)
46
+ vars = erb_template("application_vars.css.erb").result(binding)
47
+ css = read_app_file("application.css")
48
+ "#{vars}\n#{css}"
49
+ end
35
50
  end
36
51
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ultra_settings
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-28 00:00:00.000000000 Z
11
+ date: 2024-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -37,10 +37,12 @@ files:
37
37
  - VERSION
38
38
  - app/application.css
39
39
  - app/application.js
40
+ - app/application_vars.css.erb
40
41
  - app/configuration.html.erb
41
42
  - app/index.html.erb
42
43
  - app/layout.css
43
44
  - app/layout.html.erb
45
+ - app/layout_vars.css.erb
44
46
  - lib/ultra_settings.rb
45
47
  - lib/ultra_settings/application_view.rb
46
48
  - lib/ultra_settings/coerce.rb