super_settings 1.0.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -2
- data/README.md +121 -16
- data/VERSION +1 -1
- data/app/helpers/super_settings/settings_helper.rb +13 -3
- data/app/views/layouts/super_settings/settings.html.erb +1 -1
- data/config/routes.rb +1 -1
- data/db/migrate/20210414004553_create_super_settings.rb +1 -7
- data/lib/super_settings/application/api.js +4 -1
- data/lib/super_settings/application/helper.rb +56 -17
- data/lib/super_settings/application/images/arrow-down-short.svg +3 -0
- data/lib/super_settings/application/images/arrow-up-short.svg +3 -0
- data/lib/super_settings/application/images/info-circle.svg +4 -0
- data/lib/super_settings/application/images/pencil-square.svg +4 -0
- data/lib/super_settings/application/images/plus.svg +3 -1
- data/lib/super_settings/application/images/trash3.svg +3 -0
- data/lib/super_settings/application/images/x-circle.svg +4 -0
- data/lib/super_settings/application/index.html.erb +54 -37
- data/lib/super_settings/application/layout.html.erb +5 -2
- data/lib/super_settings/application/layout_styles.css +7 -151
- data/lib/super_settings/application/layout_vars.css.erb +21 -0
- data/lib/super_settings/application/scripts.js +100 -21
- data/lib/super_settings/application/style_vars.css.erb +62 -0
- data/lib/super_settings/application/styles.css +183 -14
- data/lib/super_settings/application.rb +18 -11
- data/lib/super_settings/attributes.rb +1 -8
- data/lib/super_settings/configuration.rb +9 -0
- data/lib/super_settings/context/current.rb +33 -0
- data/lib/super_settings/context.rb +3 -0
- data/lib/super_settings/controller_actions.rb +2 -2
- data/lib/super_settings/engine.rb +1 -3
- data/lib/super_settings/history_item.rb +1 -1
- data/lib/super_settings/http_client.rb +165 -0
- data/lib/super_settings/local_cache.rb +0 -15
- data/lib/super_settings/rack_application.rb +3 -3
- data/lib/super_settings/rest_api.rb +5 -4
- data/lib/super_settings/setting.rb +14 -3
- data/lib/super_settings/storage/active_record_storage/models.rb +28 -0
- data/lib/super_settings/storage/active_record_storage.rb +10 -20
- data/lib/super_settings/storage/history_attributes.rb +31 -0
- data/lib/super_settings/storage/http_storage.rb +60 -184
- data/lib/super_settings/storage/json_storage.rb +201 -0
- data/lib/super_settings/storage/mongodb_storage.rb +238 -0
- data/lib/super_settings/storage/redis_storage.rb +50 -111
- data/lib/super_settings/storage/s3_storage.rb +165 -0
- data/lib/super_settings/storage/storage_attributes.rb +64 -0
- data/lib/super_settings/storage/test_storage.rb +3 -5
- data/lib/super_settings/storage/transaction.rb +67 -0
- data/lib/super_settings/storage.rb +17 -8
- data/lib/super_settings/time_precision.rb +36 -0
- data/lib/super_settings.rb +48 -13
- data/super_settings.gemspec +11 -2
- metadata +30 -12
- data/lib/super_settings/application/images/edit.svg +0 -1
- data/lib/super_settings/application/images/info.svg +0 -1
- data/lib/super_settings/application/images/slash.svg +0 -1
- data/lib/super_settings/application/images/trash.svg +0 -1
- /data/{MIT-LICENSE → MIT-LICENSE.txt} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 83d20aa4f55a73597f12a1fed76db8d01bd81ec128b5208ed60ff8acc82a273b
|
4
|
+
data.tar.gz: c2bf00fe10c179d9ddc6849fbc6b2be2eb511b8132463a143e202ea42996581b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6fec2ccebbc1f84a7bb1878c4b6ea5f9fb5b36eacb0fddc3fcc2520ce3e738d895bf540be762a225aab970cc102287f5598b99588f4ad82494b726306b3f4601
|
7
|
+
data.tar.gz: 4d9d0641b7b47a845c26f6e72fccdd996c81b5e5f25c88d93b9148ca1c1767fe4c4d97681b558af0490028cbac3ac9e1ac447271b971829f2e8f3ea2a11311fd
|
data/CHANGELOG.md
CHANGED
@@ -4,12 +4,44 @@ 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
|
-
##
|
7
|
+
## 2.0.0
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Added controls for sorting settings in the web UI by keys or last modified time.
|
12
|
+
- Isolated of CSS classes in the web UI to prevent conflicts with other CSS libraries.
|
13
|
+
- Dark mode support in web UI.
|
14
|
+
- Added ability to embed the web UI in a view to allow tighter integration with your application's UI.
|
15
|
+
- Added storage adapter for storing settings in an S3 object.
|
16
|
+
- Added storage adapter for storing settings in MongoDB.
|
17
|
+
- HTTP storage adapter now uses keep-alive connections to improve performance.
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
|
21
|
+
- Changing a key now works as expected. Previously, a new setting was created with the new key and the old setting was left unchanged. Now, the old setting is properly marked as deleted.
|
22
|
+
- Consistently handle converting floating point number to timestamps in Redis storage.
|
23
|
+
|
24
|
+
### Removed
|
25
|
+
|
26
|
+
- Rails 4.2, 5.0, and 5.1 support has been removed.
|
27
|
+
- Removed support for Ruby 2.5.
|
28
|
+
|
29
|
+
## 1.0.2
|
30
|
+
|
31
|
+
### Added
|
32
|
+
|
33
|
+
- Added SuperSetting.rand method that can return a consistent random number inside of a context block.
|
34
|
+
|
35
|
+
### Changed
|
36
|
+
|
37
|
+
- Lazy load non-required classes.
|
38
|
+
|
39
|
+
## 1.0.1
|
8
40
|
|
9
41
|
### Added
|
10
42
|
- Optimize object shapes for the Ruby interpreter by declaring instance variables in constructors.
|
11
43
|
|
12
|
-
##
|
44
|
+
## 1.0.0
|
13
45
|
|
14
46
|
### Added
|
15
47
|
- Everything!
|
data/README.md
CHANGED
@@ -1,28 +1,28 @@
|
|
1
1
|
# SuperSettings
|
2
2
|
|
3
3
|
[![Continuous Integration](https://github.com/bdurand/super_settings/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/super_settings/actions/workflows/continuous_integration.yml)
|
4
|
-
[![Regression Test](https://github.com/bdurand/super_settings/actions/workflows/regression_test.yml/badge.svg)](https://github.com/bdurand/super_settings/actions/workflows/regression_test.yml)
|
5
4
|
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/super_settings.svg)](https://badge.fury.io/rb/super_settings)
|
6
6
|
|
7
|
-
|
7
|
+
SuperSettings is a Ruby gem that provides a scalable framework for maintaining runtime application settings. Settings are persisted in a database but cached in memory for quick, efficient access. This design ensures settings can be updated dynamically without requiring code deployment or process restarts, making it ideal for high-throughput environments.
|
8
8
|
|
9
|
-
As applications grow, they
|
9
|
+
As applications grow, they often accumulate a large number of configuration options. Common approaches such as environment variables, YAML files, or additional database columns all have their place:
|
10
10
|
|
11
|
-
|
11
|
+
- **Environment variables** are suitable for environment-specific configuration, but require restarts for changes to take effect and can only store string values.
|
12
|
+
- **YAML files** offer support for complex data structures and can be bundled with application code but typically require a new release to update.
|
13
|
+
- **Database columns** - These are great for settings tied to data models; however, they don’t apply well outside of the data model, and you need to build custom tools to manage them in your application.
|
12
14
|
|
13
|
-
|
15
|
+
Key features include:
|
14
16
|
|
15
|
-
- **
|
17
|
+
- **Caching:** A thread-safe, in-memory caching mechanism ensures high-performance access to settings, while minimizing database load. Cache refreshes are highly efficient and configurable.
|
18
|
+
- **Dynamic Updates:** Settings can be changed on the fly, with automatic logging of changes to maintain an audit trail for compliance.
|
19
|
+
- **Data Types and Validation:** Settings can be defined with specific data types (string, integer, float, boolean, datetime, or array) to ensure validity.
|
20
|
+
- **Documentation:** Each setting can include a description to explain its purpose and usage.
|
21
|
+
- **Web UI and REST API:** Manage settings via a built-in web interface or REST API.
|
22
|
+
- **Custom Callbacks:** Execute custom logic whenever a setting changes, enabling more sophisticated behavior for logging and compliance.
|
23
|
+
- **Pluggable Data Storage:** SuperSettings supports multiple storage engines, including ActiveRecord, Redis, and S3. You can also chain it to another application running SuperSettings via the HTTP storage engine to allow microservices to share settings.
|
16
24
|
|
17
|
-
|
18
|
-
|
19
|
-
SuperSettings provides a simple interface for accessing settings backed by a thread-safe caching mechanism, which provides in-memory performance while significantly limiting any load on the database. You can tune how frequently the cache is refreshed and each refresh call is tuned to be highly efficient.
|
20
|
-
|
21
|
-
There is also an out of the box Web UI and REST API for managing settings. You can specify data types for your settings (string, integer, float, boolean, datetime, or array) to ensure that values will be valid. You can also supply documentation for each setting so that it's clear what each one does and how it is used.
|
22
|
-
|
23
|
-
Changes to settings are stored whenever a setting is changed to give you an audit trail if you need it for compliance reasons. In addition, you can provide your own callbacks to execute whenever a setting is changed.
|
24
|
-
|
25
|
-
There is a companion gem [ultra_settings](https://github.com/bdurand/ultra_settings) that can be used to integrate SuperSettings into a combined configuration system alongside YAML files and environment variables.
|
25
|
+
For projects that require a combination of runtime settings, environment variables, and YAML files, SuperSettings integrates seamlessly with [ultra_settings](https://github.com/bdurand/ultra_settings), creating a flexible and powerful configuration system.
|
26
26
|
|
27
27
|
## Usage
|
28
28
|
|
@@ -102,6 +102,16 @@ SuperSettings.context do
|
|
102
102
|
end
|
103
103
|
```
|
104
104
|
|
105
|
+
You can also use the `SuperSettings.rand` method inside a context block to return a consistent random number. This can be useful for things like feature flags that you want to turn on for only a percentage of requests:
|
106
|
+
|
107
|
+
```ruby
|
108
|
+
def enabled?
|
109
|
+
SuperSettings.float("feature_rollout_percent") <= SuperSettings.rand
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
Now the value of `enabled?` will always return the same value inside of a context block. It will still be random if it is enabled for each context block.
|
114
|
+
|
105
115
|
It's a good idea to add a `context` block around your main unit of work:
|
106
116
|
|
107
117
|
- Rack application: add `SuperSettings::Context::RackMiddleware` to your middleware stack
|
@@ -137,10 +147,11 @@ This gem abstracts out the storage engine and can support multiple storage mecha
|
|
137
147
|
* `SuperSettings::Storage::ActiveRecordStorage` - Stores the settings in a relational database using ActiveRecord. This is the default storage engine for Rails applications.
|
138
148
|
* `SuperSettings::Storage::RedisStorage` - Stores the settings in a Redis database using the [redis](https://github.com/redis/redis-rb) gem.
|
139
149
|
* `SuperSettings::Storage::HttpStorage` - Uses the SuperSettings REST API running on another server. This is useful in a microservices architecture so you can have a central settings server used by all the services.
|
150
|
+
* `SuperSettings::Storage::S3Storage` - Stores the settings in JSON in an S3 object. This is useful for applications running on AWS that want to store settings in a central location since it does not require a dedicated database. It is possible to read the settings directly from S3 from another application.
|
140
151
|
|
141
152
|
Additional storage engines can be built by creating a class that includes `SuperSettings::Storage` and implements the unimplemented methods in that module.
|
142
153
|
|
143
|
-
The storage engine is defined by setting `SuperSettings::Setting.storage` to a storage class. Note that each storage class may also require additional configuration. For instance, the Redis storage class requires you to provide a connection to a Redis database. If you are running a Rails application, then the storage engine will be set to ActiveRecord by default. Otherwise, you will need to define the storage class somewhere in your application's initialization.
|
154
|
+
The storage engine is defined by setting `SuperSettings::Setting.storage` to a storage class. Note that each storage class may also require additional configuration. For instance, the Redis storage class requires you to provide a connection to a Redis database. If you are running a Rails application, then the storage engine will be set to ActiveRecord by default. Otherwise, you will need to define the storage class somewhere in your application's initialization. See the storage class documentation for more information.
|
144
155
|
|
145
156
|
### Web UI
|
146
157
|
|
@@ -231,6 +242,9 @@ SuperSettings.configure do |config|
|
|
231
242
|
# Set the superclass to use for the controller. Defaults to using `ApplicationController`.
|
232
243
|
config.controller.superclass = Admin::BaseController
|
233
244
|
|
245
|
+
# Set the color scheme to use for the Web UI. Options are :light (default), :dark, or :system.
|
246
|
+
config.controller.color_scheme = :dark
|
247
|
+
|
234
248
|
# Add additional code to the controller. In this case we are adding code to ensure only
|
235
249
|
# admins can access the functionality and changing the layout to use one defined by the application.
|
236
250
|
config.controller.enhance do
|
@@ -278,6 +292,54 @@ SuperSettings.configure do |config|
|
|
278
292
|
end
|
279
293
|
```
|
280
294
|
|
295
|
+
#### Using your own controller
|
296
|
+
|
297
|
+
You can embed the SuperSettings web UI into your own templates. This gives you the option to more tightly integrate it with your application's navigation and look and feel.
|
298
|
+
|
299
|
+
First disable the web UI in the configuration since you won't need it.
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
# config/initializers/super_settings.rb
|
303
|
+
|
304
|
+
SuperSettings.configure do |config|
|
305
|
+
# Disable the built in web UI since you won't be using it.
|
306
|
+
config.controller.web_ui_enabled = false
|
307
|
+
end
|
308
|
+
```
|
309
|
+
|
310
|
+
Create your controller that will render the SuperSettings web UI.
|
311
|
+
|
312
|
+
```ruby
|
313
|
+
class AppSettingsController < ApplicationController
|
314
|
+
def index
|
315
|
+
end
|
316
|
+
end
|
317
|
+
```
|
318
|
+
|
319
|
+
Mount the engine routes in your `config/routes.rb` file.
|
320
|
+
|
321
|
+
```ruby
|
322
|
+
# config/routes.rb
|
323
|
+
|
324
|
+
Rails.application.routes.draw do
|
325
|
+
mount SuperSettings::Engine => "/settings"
|
326
|
+
|
327
|
+
controller :app_settings do
|
328
|
+
get "/app_settings", action: :index
|
329
|
+
end
|
330
|
+
end
|
331
|
+
```
|
332
|
+
|
333
|
+
Create a view that embeds the SuperSettings web UI using the `SuperSettings::Application` class. You need to specify the path you mounted the engine at as the base URL for the REST API.
|
334
|
+
|
335
|
+
```erb
|
336
|
+
# app/views/app_settings/index.html.erb
|
337
|
+
|
338
|
+
<h1>Application Settings</h1>
|
339
|
+
|
340
|
+
<%= SuperSettings::Application.new(api_base_url: "/settings").render %>
|
341
|
+
```
|
342
|
+
|
281
343
|
## Installation
|
282
344
|
|
283
345
|
Add this line to your application's Gemfile:
|
@@ -302,6 +364,49 @@ Open a pull request on GitHub.
|
|
302
364
|
|
303
365
|
Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
|
304
366
|
|
367
|
+
You can run a local Rails development using ActiveRecord storage with
|
368
|
+
|
369
|
+
```bash
|
370
|
+
# Initialize the database (one time only)
|
371
|
+
bin/rails bin/rails super_settings:install:migrations
|
372
|
+
bin/rails db:migrate
|
373
|
+
|
374
|
+
# Start the server
|
375
|
+
bin/rails s
|
376
|
+
```
|
377
|
+
|
378
|
+
You can also bring up a local rack server with
|
379
|
+
|
380
|
+
```bash
|
381
|
+
bundle exec rackup
|
382
|
+
```
|
383
|
+
|
384
|
+
By default this will use Redis for storage using the default Redis URL. You can change the storage engine with the `STORAGE` environment variable.
|
385
|
+
|
386
|
+
- `redis` - Use the Redis storage engine. The Redis URL can be set with the `REDIS_URL` environment variable.
|
387
|
+
- `http` - Use the HTTP storage engine. The URL for the REST API can be set with the `REST_API_URL` environment variable.
|
388
|
+
- `s3` - Use the S3 storage engine. The S3 URL can be set with the `S3_URL` environment variable.
|
389
|
+
- `mongodb` - Use the MongoDB storage engine. The MongoDB URL can be set with the `MONGODB_URL` environment variable.
|
390
|
+
|
391
|
+
You can bring up all of these storage engines locally with the included docker-compose configuration.
|
392
|
+
|
393
|
+
```bash
|
394
|
+
docker-compose up
|
395
|
+
```
|
396
|
+
|
397
|
+
This will work out of the box with the defaults for the storage engines when running the rack server.:
|
398
|
+
|
399
|
+
- `REDIS_URL` - `redis://localhost:6379/0`
|
400
|
+
- `REST_API_URL` - `http://localhost:3000/settings` (this is the default URL for the Rails application)
|
401
|
+
- `S3_URL` - `s3://accesskey:secretkey@region-1/settings/settings.json` (the S3 endpoint will be set to `http://localhost:9000`)
|
402
|
+
- `MONGODB_URL` - `mongodb://localhost:27017/super_settings`
|
403
|
+
|
404
|
+
Finally, you can run the application in dark mode by setting the `COLOR_SCHEME` environment variable.
|
405
|
+
|
406
|
+
```bash
|
407
|
+
COLOR_SCHEME=dark bundle exec rackup
|
408
|
+
```
|
409
|
+
|
305
410
|
## License
|
306
411
|
|
307
412
|
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
|
-
|
1
|
+
2.0.0
|
@@ -5,19 +5,20 @@ module SuperSettings
|
|
5
5
|
# Render the styles.css as an inline <style> tag.
|
6
6
|
def super_settings_layout_style_tag
|
7
7
|
application_dir = File.expand_path(File.join("..", "..", "..", "lib", "super_settings", "application"), __dir__)
|
8
|
+
css = render(file: File.join(application_dir, "layout_styles.css"))
|
8
9
|
content_tag(:style, type: "text/css") do
|
9
|
-
|
10
|
+
(layout_css_vars + css).html_safe
|
10
11
|
end
|
11
12
|
end
|
12
13
|
|
13
14
|
# Return the application name set by the configuration or a default value.
|
14
15
|
def super_settings_application_name
|
15
|
-
|
16
|
+
SuperSettings.configuration.controller.application_name || "Application"
|
16
17
|
end
|
17
18
|
|
18
19
|
# Render the header for the web pages using values set in the configuration.
|
19
20
|
def super_settings_application_header
|
20
|
-
config =
|
21
|
+
config = SuperSettings.configuration.controller
|
21
22
|
content = "#{super_settings_application_name} Settings"
|
22
23
|
if Coerce.present?(config.application_logo)
|
23
24
|
content = image_tag(config.application_logo, alt: "").concat(content)
|
@@ -28,5 +29,14 @@ module SuperSettings
|
|
28
29
|
content
|
29
30
|
end
|
30
31
|
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def layout_css_vars
|
36
|
+
application_dir = File.expand_path(File.join("..", "..", "..", "lib", "super_settings", "application"), __dir__)
|
37
|
+
erb = ERB.new(File.read(File.join(application_dir, "layout_vars.css.erb")))
|
38
|
+
color_scheme = SuperSettings.configuration.controller.color_scheme
|
39
|
+
erb.result(binding).html_safe
|
40
|
+
end
|
31
41
|
end
|
32
42
|
end
|
data/config/routes.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
SuperSettings::Engine.routes.draw do
|
4
4
|
controller :settings do
|
5
|
-
if SuperSettings
|
5
|
+
if SuperSettings.configuration.controller.web_ui_enabled?
|
6
6
|
get "/", action: :root, as: :root
|
7
7
|
end
|
8
8
|
get "/settings", action: :index
|
@@ -1,12 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
migration_class = ActiveRecord::Migration
|
5
|
-
if migration_class.respond_to?(:[])
|
6
|
-
migration_class = migration_class[4.2]
|
7
|
-
end
|
8
|
-
|
9
|
-
class CreateSuperSettings < migration_class
|
3
|
+
class CreateSuperSettings < ActiveRecord::Migration[5.0]
|
10
4
|
def up
|
11
5
|
create_table :super_settings do |t|
|
12
6
|
t.string :key, null: false, limit: 190, index: {unique: true}
|
@@ -9,7 +9,10 @@
|
|
9
9
|
(function() {
|
10
10
|
// Get the URL for making an API call to the specified action and id.
|
11
11
|
function apiURL(action, params) {
|
12
|
-
let url =
|
12
|
+
let url = document.querySelector(".super-settings[data-api-base-url]")?.dataset?.apiBaseUrl;
|
13
|
+
if (!url) {
|
14
|
+
url = window.location.pathname;
|
15
|
+
}
|
13
16
|
if (url.endsWith("/")) {
|
14
17
|
url = url.substring(0, url.length - 1);
|
15
18
|
}
|
@@ -5,13 +5,14 @@ module SuperSettings
|
|
5
5
|
# are mixed in to the Application class so they are accessible from the ERB templates.
|
6
6
|
module Helper
|
7
7
|
ICON_SVG = Dir.glob(File.join(__dir__, "images", "*.svg")).each_with_object({}) do |file, cache|
|
8
|
-
|
8
|
+
svg = File.read(file).chomp
|
9
|
+
cache[File.basename(file, ".svg")] = svg
|
9
10
|
end.freeze
|
10
11
|
|
11
12
|
ICON_BUTTON_STYLE = {
|
12
13
|
cursor: "pointer",
|
13
|
-
width: "1.
|
14
|
-
height: "1.
|
14
|
+
width: "1.35rem",
|
15
|
+
height: "1.35rem",
|
15
16
|
"min-width": "20px",
|
16
17
|
"min-height": "20px",
|
17
18
|
"margin-top": "0.25rem",
|
@@ -21,8 +22,7 @@ module SuperSettings
|
|
21
22
|
DEFAULT_ICON_STYLE = {
|
22
23
|
width: "1rem",
|
23
24
|
height: "1rem",
|
24
|
-
display: "inline-block"
|
25
|
-
"vertical-align": "middle"
|
25
|
+
display: "inline-block"
|
26
26
|
}.freeze
|
27
27
|
|
28
28
|
# Render the scripts.js file as an inline <script> tag.
|
@@ -41,6 +41,7 @@ module SuperSettings
|
|
41
41
|
def style_tag
|
42
42
|
<<~HTML
|
43
43
|
<style type="text/css">
|
44
|
+
#{render_partial("style_vars.css.erb")}
|
44
45
|
#{File.read(File.join(__dir__, "styles.css"))}
|
45
46
|
</style>
|
46
47
|
HTML
|
@@ -50,39 +51,61 @@ module SuperSettings
|
|
50
51
|
def layout_style_tag
|
51
52
|
<<~HTML
|
52
53
|
<style type="text/css">
|
54
|
+
#{render_partial("layout_vars.css.erb")}
|
53
55
|
#{File.read(File.join(__dir__, "layout_styles.css"))}
|
54
56
|
</style>
|
55
57
|
HTML
|
56
58
|
end
|
57
59
|
|
60
|
+
# Render an ERB template.
|
61
|
+
#
|
62
|
+
# @param erb_file [String] the path to the ERB file to render
|
63
|
+
# @return [String] the rendered HTML
|
64
|
+
def render_partial(erb_file)
|
65
|
+
template = ERB.new(File.read(File.expand_path(erb_file, __dir__)))
|
66
|
+
template.result(binding)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Escape text for use in HTML.
|
70
|
+
#
|
71
|
+
# @param text [String] the text to escape
|
72
|
+
# @return [String] the escaped text
|
73
|
+
def html_escape(text)
|
74
|
+
ERB::Util.html_escape(text)
|
75
|
+
end
|
76
|
+
|
58
77
|
# Render an image tag for one of the SVG images in the images directory. If the :color option
|
59
78
|
# is specified, it will be applied to the SVG image.
|
60
79
|
def icon_image(name, options = {})
|
61
80
|
svg = ICON_SVG[name.to_s]
|
62
|
-
|
63
|
-
|
81
|
+
style = (options[:style] || {})
|
82
|
+
css = DEFAULT_ICON_STYLE.merge(style).map { |name, value| "#{name}: #{value}" }.join("; ")
|
83
|
+
options = options.merge(style: css, class: "super-settings-icon")
|
84
|
+
if options[:data].is_a?(Hash)
|
85
|
+
options[:data].each do |key, value|
|
86
|
+
options["data-#{key}"] = value
|
87
|
+
end
|
88
|
+
options.delete(:data)
|
64
89
|
end
|
65
|
-
|
66
|
-
options = {alt: ""}.merge(options).merge(src: "data:image/svg+xml;utf8,#{svg}", style: css)
|
67
|
-
tag(:img, options)
|
90
|
+
content_tag(:span, svg, options)
|
68
91
|
end
|
69
92
|
|
70
93
|
# Render an icon image as a link tag.
|
71
94
|
def icon_button(icon, title:, color:, js_class:, url: nil, disabled: false, style: {}, link_style: nil)
|
72
95
|
url = "#" if Coerce.blank?(url)
|
73
|
-
image = icon_image(icon, alt: title,
|
74
|
-
content_tag(:a, image, href: url, class: js_class, disabled: disabled, style: link_style)
|
96
|
+
image = icon_image(icon, alt: title, style: ICON_BUTTON_STYLE.merge(style).merge(color: color))
|
97
|
+
content_tag(:a, image, href: url, class: js_class, disabled: disabled, style: link_style, title: title)
|
75
98
|
end
|
76
99
|
|
77
100
|
# Return the application name set by the configuration or a default value.
|
78
101
|
def application_name
|
79
|
-
|
102
|
+
html_escape(SuperSettings.configuration.controller.application_name || "Application")
|
80
103
|
end
|
81
104
|
|
82
105
|
# Render the header for the web pages using values set in the configuration.
|
83
106
|
def application_header
|
84
|
-
config =
|
85
|
-
content =
|
107
|
+
config = SuperSettings.configuration.controller
|
108
|
+
content = html_escape("#{application_name} Settings")
|
86
109
|
if Coerce.present?(config.application_logo)
|
87
110
|
content = tag(:img, src: config.application_logo, alt: "") + content
|
88
111
|
end
|
@@ -107,14 +130,30 @@ module SuperSettings
|
|
107
130
|
def html_attributes(options)
|
108
131
|
html_options = []
|
109
132
|
options.each do |name, value|
|
110
|
-
html_options << "#{name}=\"#{
|
133
|
+
html_options << "#{name}=\"#{html_escape(value.to_s)}\""
|
111
134
|
end
|
112
135
|
html_options.join(" ")
|
113
136
|
end
|
114
137
|
|
115
|
-
#
|
138
|
+
# Additional HTML code that should go into the <head> element on the page.
|
139
|
+
#
|
140
|
+
# @return [String]
|
116
141
|
def add_to_head
|
117
142
|
@add_to_head if defined?(@add_to_head)
|
118
143
|
end
|
144
|
+
|
145
|
+
# The base URL for the REST API.
|
146
|
+
#
|
147
|
+
# @return [String]
|
148
|
+
def api_base_url
|
149
|
+
@api_base_url if defined?(@api_base_url)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Whether to use dark mode for the application UI.
|
153
|
+
#
|
154
|
+
# @return [Boolean, nil]
|
155
|
+
def color_scheme
|
156
|
+
@color_scheme if defined?(@color_scheme)
|
157
|
+
end
|
119
158
|
end
|
120
159
|
end
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-down-short" viewBox="0 0 16 16">
|
2
|
+
<path fill-rule="evenodd" d="M8 4a.5.5 0 0 1 .5.5v5.793l2.146-2.147a.5.5 0 0 1 .708.708l-3 3a.5.5 0 0 1-.708 0l-3-3a.5.5 0 1 1 .708-.708L7.5 10.293V4.5A.5.5 0 0 1 8 4"/>
|
3
|
+
</svg>
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-up-short" viewBox="0 0 16 16">
|
2
|
+
<path fill-rule="evenodd" d="M8 12a.5.5 0 0 0 .5-.5V5.707l2.146 2.147a.5.5 0 0 0 .708-.708l-3-3a.5.5 0 0 0-.708 0l-3 3a.5.5 0 1 0 .708.708L7.5 5.707V11.5a.5.5 0 0 0 .5.5"/>
|
3
|
+
</svg>
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-info-circle" viewBox="0 0 16 16">
|
2
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/>
|
3
|
+
<path d="m8.93 6.588-2.29.287-.082.38.45.083c.294.07.352.176.288.469l-.738 3.468c-.194.897.105 1.319.808 1.319.545 0 1.178-.252 1.465-.598l.088-.416c-.2.176-.492.246-.686.246-.275 0-.375-.193-.304-.533zM9 4.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0"/>
|
4
|
+
</svg>
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-pencil-square" viewBox="0 0 16 16">
|
2
|
+
<path d="M15.502 1.94a.5.5 0 0 1 0 .706L14.459 3.69l-2-2L13.502.646a.5.5 0 0 1 .707 0l1.293 1.293zm-1.75 2.456-2-2L4.939 9.21a.5.5 0 0 0-.121.196l-.805 2.414a.25.25 0 0 0 .316.316l2.414-.805a.5.5 0 0 0 .196-.12l6.813-6.814z"/>
|
3
|
+
<path fill-rule="evenodd" d="M1 13.5A1.5 1.5 0 0 0 2.5 15h11a1.5 1.5 0 0 0 1.5-1.5v-6a.5.5 0 0 0-1 0v6a.5.5 0 0 1-.5.5h-11a.5.5 0 0 1-.5-.5v-11a.5.5 0 0 1 .5-.5H9a.5.5 0 0 0 0-1H2.5A1.5 1.5 0 0 0 1 2.5z"/>
|
4
|
+
</svg>
|
@@ -1 +1,3 @@
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus" viewBox="0 0 16 16">
|
2
|
+
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4"/>
|
3
|
+
</svg>
|
@@ -0,0 +1,3 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash3" viewBox="0 0 16 16">
|
2
|
+
<path d="M6.5 1h3a.5.5 0 0 1 .5.5v1H6v-1a.5.5 0 0 1 .5-.5M11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3A1.5 1.5 0 0 0 5 1.5v1H1.5a.5.5 0 0 0 0 1h.538l.853 10.66A2 2 0 0 0 4.885 16h6.23a2 2 0 0 0 1.994-1.84l.853-10.66h.538a.5.5 0 0 0 0-1zm1.958 1-.846 10.58a1 1 0 0 1-.997.92h-6.23a1 1 0 0 1-.997-.92L3.042 3.5zm-7.487 1a.5.5 0 0 1 .528.47l.5 8.5a.5.5 0 0 1-.998.06L5 5.03a.5.5 0 0 1 .47-.53Zm5.058 0a.5.5 0 0 1 .47.53l-.5 8.5a.5.5 0 1 1-.998-.06l.5-8.5a.5.5 0 0 1 .528-.47M8 4.5a.5.5 0 0 1 .5.5v8.5a.5.5 0 0 1-1 0V5a.5.5 0 0 1 .5-.5"/>
|
3
|
+
</svg>
|
@@ -0,0 +1,4 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-x-circle" viewBox="0 0 16 16">
|
2
|
+
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16"/>
|
3
|
+
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708"/>
|
4
|
+
</svg>
|