system_settings 0.6.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -13
- data/app/assets/config/manifest.js +1 -0
- data/app/assets/stylesheets/system_settings/application.css +227 -0
- data/app/controllers/system_settings/application_controller.rb +2 -6
- data/app/controllers/system_settings/settings_controller.rb +21 -16
- data/app/helpers/system_settings/application_helper.rb +34 -0
- data/app/models/system_settings/application_record.rb +2 -0
- data/app/models/system_settings/boolean_setting.rb +2 -0
- data/app/models/system_settings/configurator.rb +38 -10
- data/app/models/system_settings/decimal_list_setting.rb +8 -0
- data/app/models/system_settings/decimal_setting.rb +8 -0
- data/app/models/system_settings/errors/error.rb +2 -0
- data/app/models/system_settings/errors/not_found_error.rb +3 -1
- data/app/models/system_settings/errors/not_loaded_error.rb +8 -0
- data/app/models/system_settings/errors/settings_read_error.rb +2 -0
- data/app/models/system_settings/integer_list_setting.rb +3 -1
- data/app/models/system_settings/integer_setting.rb +3 -1
- data/app/models/system_settings/list_of_decimals_validator.rb +37 -0
- data/app/models/system_settings/list_of_integers_validator.rb +2 -0
- data/app/models/system_settings/list_of_strings_validator.rb +2 -0
- data/app/models/system_settings/setting.rb +2 -0
- data/app/models/system_settings/string_list_setting.rb +2 -0
- data/app/models/system_settings/string_setting.rb +7 -0
- data/app/models/system_settings/type/decimal_list.rb +42 -0
- data/app/models/system_settings/type/integer_list.rb +10 -15
- data/app/models/system_settings/type/string_list.rb +2 -0
- data/app/views/layouts/system_settings/application.html.erb +21 -0
- data/app/views/system_settings/settings/_common_attributes.html.erb +14 -0
- data/app/views/system_settings/settings/_form.html.erb +18 -0
- data/app/views/system_settings/settings/edit.html.erb +2 -0
- data/app/views/system_settings/settings/index.html.erb +29 -0
- data/app/views/system_settings/settings/show.html.erb +9 -0
- data/config/locales/system_settings.en.yml +27 -1
- data/config/routes.rb +7 -4
- data/lib/system_settings.rb +13 -7
- data/lib/system_settings/engine.rb +6 -13
- data/lib/system_settings/version.rb +3 -1
- metadata +17 -14
- data/app/controllers/system_settings/root_controller.rb +0 -11
- data/app/models/system_settings/pagination.rb +0 -18
- data/frontend/build/asset-manifest.json +0 -11
- data/frontend/build/favicon.ico +0 -0
- data/frontend/build/index.html +0 -1
- data/frontend/build/precache-manifest.cace9a6c696c96461d63a2e3a1cca55e.js +0 -22
- data/frontend/build/service-worker.js +0 -39
- data/frontend/build/static/css/main.003e6dc8.chunk.css +0 -1
- data/frontend/build/static/js/2.42b63415.chunk.js +0 -1
- data/frontend/build/static/js/main.9315e265.chunk.js +0 -1
- data/frontend/build/static/js/runtime~main.a09e9b82.js +0 -1
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SystemSettings
|
2
4
|
class IntegerListSetting < SystemSettings::Setting
|
3
|
-
attribute :value, SystemSettings::Type::IntegerList.new
|
5
|
+
attribute :value, SystemSettings::Type::IntegerList.new(limit: 8)
|
4
6
|
validates :value, "system_settings/list_of_integers": true
|
5
7
|
end
|
6
8
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SystemSettings
|
4
|
+
class ListOfDecimalsValidator < ActiveModel::EachValidator
|
5
|
+
LIST_REGEXP = /\A[+-]?\d+(?:\.\d+){0,1}(?:; *[+-]?\d+(?:\.\d+){0,1})*\z/.freeze
|
6
|
+
SINGLE_REGEXP = /\A[+-]?\d+(?:\.\d+){0,1}\z/.freeze
|
7
|
+
|
8
|
+
def validate_each(record, attr_name, value)
|
9
|
+
came_from_user = :"#{attr_name}_came_from_user?"
|
10
|
+
|
11
|
+
raw_value = record.read_attribute_before_type_cast(attr_name) if record.respond_to?(came_from_user) && record.public_send(came_from_user)
|
12
|
+
raw_value ||= value
|
13
|
+
|
14
|
+
raw_value = value if record_attribute_changed_in_place?(record, attr_name)
|
15
|
+
|
16
|
+
record.errors.add(attr_name, :not_a_list_of_decimals) unless matches_list_of_decimals_regexp?(raw_value)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def record_attribute_changed_in_place?(record, attr_name)
|
22
|
+
record.respond_to?(:attribute_changed_in_place?) &&
|
23
|
+
record.attribute_changed_in_place?(attr_name.to_s)
|
24
|
+
end
|
25
|
+
|
26
|
+
def matches_list_of_decimals_regexp?(raw_value)
|
27
|
+
case raw_value
|
28
|
+
when String
|
29
|
+
LIST_REGEXP.match?(raw_value)
|
30
|
+
when Array
|
31
|
+
raw_value.all? { |v| SINGLE_REGEXP.match?(v.to_s) }
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -1,6 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SystemSettings
|
2
4
|
class StringSetting < SystemSettings::Setting
|
3
5
|
attribute :value, :string
|
4
6
|
validates :value, presence: true
|
7
|
+
|
8
|
+
def value=(original_value)
|
9
|
+
next_value = original_value.to_s.blank? ? nil : original_value.to_s.strip
|
10
|
+
super(next_value)
|
11
|
+
end
|
5
12
|
end
|
6
13
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SystemSettings
|
4
|
+
module Type
|
5
|
+
class DecimalList < ActiveModel::Type::Value
|
6
|
+
SEPARATOR = ";"
|
7
|
+
|
8
|
+
def initialize(precision: nil, limit: nil, scale: nil)
|
9
|
+
super
|
10
|
+
@single_type = ActiveModel::Type::Decimal.new(precision: precision, limit: limit, scale: scale)
|
11
|
+
end
|
12
|
+
|
13
|
+
def type
|
14
|
+
:decimal_list
|
15
|
+
end
|
16
|
+
|
17
|
+
def deserialize(value)
|
18
|
+
result = value.presence && JSON.parse(value)
|
19
|
+
if result.is_a?(Array)
|
20
|
+
result.map { |v| @single_type.cast(v) }
|
21
|
+
else
|
22
|
+
result
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def serialize(value)
|
27
|
+
JSON.dump(value) unless value.nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def cast_value(value)
|
33
|
+
case value
|
34
|
+
when Array
|
35
|
+
value.map { |v| @single_type.cast(v) }
|
36
|
+
when String
|
37
|
+
value.split(SEPARATOR).map { |v| @single_type.cast(v) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,7 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SystemSettings
|
2
4
|
module Type
|
3
5
|
class IntegerList < ActiveModel::Type::Value
|
4
|
-
SEPARATOR = ";"
|
6
|
+
SEPARATOR = ";"
|
7
|
+
|
8
|
+
def initialize(precision: nil, limit: nil, scale: nil)
|
9
|
+
super
|
10
|
+
@single_type = ActiveModel::Type::Integer.new(precision: precision, limit: limit, scale: scale)
|
11
|
+
end
|
5
12
|
|
6
13
|
def type
|
7
14
|
:integer_list
|
@@ -20,21 +27,9 @@ module SystemSettings
|
|
20
27
|
def cast_value(value)
|
21
28
|
case value
|
22
29
|
when Array
|
23
|
-
value.map
|
24
|
-
begin
|
25
|
-
v.to_i
|
26
|
-
rescue StandardError
|
27
|
-
nil
|
28
|
-
end
|
29
|
-
end
|
30
|
+
value.map { |v| @single_type.cast(v) }
|
30
31
|
when String
|
31
|
-
value.split(SEPARATOR).map
|
32
|
-
begin
|
33
|
-
v.to_i
|
34
|
-
rescue StandardError
|
35
|
-
nil
|
36
|
-
end
|
37
|
-
end
|
32
|
+
value.split(SEPARATOR).map { |v| @single_type.cast(v) }
|
38
33
|
end
|
39
34
|
end
|
40
35
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
<title>System Settings - <%= SystemSettings::VERSION %></title>
|
6
|
+
<%= csrf_meta_tags %>
|
7
|
+
<%= stylesheet_link_tag 'system_settings/application', media: 'all' %>
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<header>
|
11
|
+
<div class="container">
|
12
|
+
<%= link_to "System Settings", root_path, class: "app-name" %>
|
13
|
+
</div>
|
14
|
+
</header>
|
15
|
+
<main>
|
16
|
+
<div class="container">
|
17
|
+
<%= yield %>
|
18
|
+
</div>
|
19
|
+
</main>
|
20
|
+
</body>
|
21
|
+
</html>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div class="attribute">
|
2
|
+
<div class="name"><%= setting.class.human_attribute_name(:name) %></div>
|
3
|
+
<div class="value">
|
4
|
+
<span class="sys-name"><%= setting.name %></span>
|
5
|
+
</div>
|
6
|
+
</div>
|
7
|
+
<div class="attribute">
|
8
|
+
<div class="name"><%= setting.class.human_attribute_name(:type) %></div>
|
9
|
+
<div class="value"><%= setting.model_name.human %></div>
|
10
|
+
</div>
|
11
|
+
<div class="attribute">
|
12
|
+
<div class="name"><%= setting.class.human_attribute_name(:description) %></div>
|
13
|
+
<div class="value"><%= setting.description %></div>
|
14
|
+
</div>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%= form_for setting, as: :setting, url: setting_path(setting), method: "post" do |f| %>
|
2
|
+
<div class="attribute">
|
3
|
+
<%= f.label :value, class: "name" %>
|
4
|
+
<% if f.object.is_a?(SystemSettings::BooleanSetting) %>
|
5
|
+
<%= f.check_box :value, value: f.object.value %>
|
6
|
+
<% else %>
|
7
|
+
<%= f.text_field :value, value: format_value_for_form(f.object), class: "value" %>
|
8
|
+
<% end %>
|
9
|
+
<% if f.object.errors.messages[:value].any? %>
|
10
|
+
<div class="errors"><%= f.object.errors.messages[:value].join("; ") %></div>
|
11
|
+
<% end %>
|
12
|
+
<div class="hint"><%= t(f.object.type, scope: "system_settings.settings.form.hints_by_type", default: "").html_safe %></div>
|
13
|
+
</div>
|
14
|
+
<div class="buttons">
|
15
|
+
<%= f.submit t(".save"), class: "primary" %>
|
16
|
+
<%= link_to t(".back"), setting_path(setting), class: "button" %>
|
17
|
+
</div>
|
18
|
+
<% end %>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<table class="settings-table">
|
2
|
+
<thead>
|
3
|
+
<tr>
|
4
|
+
<th class="name"><%= SystemSettings::Setting.human_attribute_name(:name) %></th>
|
5
|
+
<th class="description"><%= SystemSettings::Setting.human_attribute_name(:description) %></th>
|
6
|
+
<th class="value"><%= SystemSettings::Setting.human_attribute_name(:value) %></th>
|
7
|
+
</tr>
|
8
|
+
</thead>
|
9
|
+
<tbody>
|
10
|
+
<% if @settings.any? %>
|
11
|
+
<% @settings.each do |setting| %>
|
12
|
+
<tr>
|
13
|
+
<td class="name"><%= link_to setting.name, setting_path(setting), class: "sys-name" %></td>
|
14
|
+
<td class="description"><%= setting.description %></td>
|
15
|
+
<td class="value"><%= format_value setting.value %></td>
|
16
|
+
</tr>
|
17
|
+
<% end %>
|
18
|
+
<% else %>
|
19
|
+
<tr>
|
20
|
+
<td colspan="3" class="no-entries"><%= t(".no_settings").html_safe %></td>
|
21
|
+
</tr>
|
22
|
+
<% end %>
|
23
|
+
</tbody>
|
24
|
+
<tfoot>
|
25
|
+
<tr>
|
26
|
+
<td colspan="3"><%= t(".add_new", path: display_settings_file_path).html_safe %></td>
|
27
|
+
</tr>
|
28
|
+
</tfoot>
|
29
|
+
</table>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<%= render "common_attributes", setting: @setting %>
|
2
|
+
<div class="attribute">
|
3
|
+
<div class="name"><%= @setting.class.human_attribute_name(:value) %></div>
|
4
|
+
<div class="value"><%= format_value @setting.value %></div>
|
5
|
+
</div>
|
6
|
+
<div class="buttons">
|
7
|
+
<%= link_to t(".edit"), edit_setting_path(@setting), class: "button primary" %>
|
8
|
+
<%= link_to t(".back"), root_path, class: "button" %>
|
9
|
+
</div>
|
@@ -16,11 +16,21 @@ en:
|
|
16
16
|
system_settings/string_setting:
|
17
17
|
one: "System Setting (String)"
|
18
18
|
other: "System Settings (String)"
|
19
|
+
system_settings/decimal_setting:
|
20
|
+
one: "System Setting (Decimal)"
|
21
|
+
other: "System Settings (Decimal)"
|
22
|
+
system_settings/decimal_list_setting:
|
23
|
+
one: "System Setting (Decimal list)"
|
24
|
+
other: "System Settings (Decimal list)"
|
25
|
+
system_settings/boolean_setting:
|
26
|
+
one: "System Setting (Boolean)"
|
27
|
+
other: "System Settings (Boolean)"
|
19
28
|
attributes:
|
20
29
|
system_settings/setting:
|
21
30
|
id: "ID"
|
22
31
|
name: "Name"
|
23
32
|
type: "Type"
|
33
|
+
value: "Value"
|
24
34
|
description: "Description"
|
25
35
|
created_at: "Created at"
|
26
36
|
uploaded_at: "Updated at"
|
@@ -31,7 +41,23 @@ en:
|
|
31
41
|
value:
|
32
42
|
not_a_list_of_integers: "not a list of integers"
|
33
43
|
not_a_list_of_strings: "not a list of strings"
|
44
|
+
not_a_list_of_decimals: "not a list of decimals"
|
34
45
|
system_settings/boolean_setting:
|
35
46
|
attributes:
|
36
47
|
value:
|
37
|
-
inclusion: "must be set to true or false"
|
48
|
+
inclusion: "must be set to true or false"
|
49
|
+
system_settings:
|
50
|
+
settings:
|
51
|
+
index:
|
52
|
+
no_settings: "No settings"
|
53
|
+
add_new: "Add new entries in <code>%{path}</code>"
|
54
|
+
show:
|
55
|
+
edit: Edit
|
56
|
+
back: Back
|
57
|
+
form:
|
58
|
+
save: Save
|
59
|
+
back: Back
|
60
|
+
hints_by_type:
|
61
|
+
"SystemSettings::StringListSetting": "Separate values by <code>;</code> character."
|
62
|
+
"SystemSettings::IntegerListSetting": "Separate values by <code>;</code> character."
|
63
|
+
"SystemSettings::DecimalListSetting": "Decimal separator is the <code>.</code> character. Separate multiple values by <code>;</code> character."
|
data/config/routes.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
SystemSettings::Engine.routes.draw do
|
2
|
-
|
3
|
-
|
4
|
+
resources :settings, only: [:show, :edit] do
|
5
|
+
member do
|
6
|
+
post "/", to: "settings#update"
|
7
|
+
end
|
4
8
|
end
|
5
|
-
root to: "
|
6
|
-
get "*rest", to: "root#index", as: :catch_all
|
9
|
+
root to: "settings#index"
|
7
10
|
end
|
data/lib/system_settings.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "system_settings/version"
|
1
4
|
require "system_settings/engine"
|
2
5
|
|
3
6
|
module SystemSettings
|
@@ -9,21 +12,24 @@ module SystemSettings
|
|
9
12
|
instrument("system_settings.find", name: name) do |payload|
|
10
13
|
record = Setting.find_by(name: name)
|
11
14
|
unless record
|
12
|
-
message =
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
message = <<~MESSAGE.strip
|
16
|
+
Couldn't find system setting #{name}
|
17
|
+
|
18
|
+
It might not be loaded from settings file(#{settings_file_path}).
|
19
|
+
To load missing settings with their initial values you can call SystemSettings.load from your Rails environment or run Rails task:
|
20
|
+
|
21
|
+
bin/rails system_settings:load RAILS_ENV=#{::Rails.env}
|
22
|
+
MESSAGE
|
17
23
|
raise(Errors::NotFoundError, message)
|
18
24
|
end
|
19
25
|
payload[:value] = record.value
|
20
26
|
end
|
21
27
|
end
|
22
28
|
|
23
|
-
def self.load
|
29
|
+
def self.load(*names)
|
24
30
|
instrument("system_settings.load", path: settings_file_path) do |payload|
|
25
31
|
configurator = Configurator.from_file(settings_file_path)
|
26
|
-
payload[:success] = configurator.persist
|
32
|
+
payload[:success] = configurator.persist(only: names)
|
27
33
|
end
|
28
34
|
end
|
29
35
|
|