system_settings 0.6.1 → 0.9.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -13
  3. data/app/assets/config/manifest.js +1 -0
  4. data/app/assets/stylesheets/system_settings/application.css +227 -0
  5. data/app/controllers/system_settings/application_controller.rb +2 -6
  6. data/app/controllers/system_settings/settings_controller.rb +21 -16
  7. data/app/helpers/system_settings/application_helper.rb +34 -0
  8. data/app/models/system_settings/application_record.rb +2 -0
  9. data/app/models/system_settings/boolean_setting.rb +2 -0
  10. data/app/models/system_settings/configurator.rb +38 -10
  11. data/app/models/system_settings/decimal_list_setting.rb +8 -0
  12. data/app/models/system_settings/decimal_setting.rb +8 -0
  13. data/app/models/system_settings/errors/error.rb +2 -0
  14. data/app/models/system_settings/errors/not_found_error.rb +3 -1
  15. data/app/models/system_settings/errors/not_loaded_error.rb +8 -0
  16. data/app/models/system_settings/errors/settings_read_error.rb +2 -0
  17. data/app/models/system_settings/integer_list_setting.rb +3 -1
  18. data/app/models/system_settings/integer_setting.rb +3 -1
  19. data/app/models/system_settings/list_of_decimals_validator.rb +37 -0
  20. data/app/models/system_settings/list_of_integers_validator.rb +2 -0
  21. data/app/models/system_settings/list_of_strings_validator.rb +2 -0
  22. data/app/models/system_settings/setting.rb +2 -0
  23. data/app/models/system_settings/string_list_setting.rb +2 -0
  24. data/app/models/system_settings/string_setting.rb +7 -0
  25. data/app/models/system_settings/type/decimal_list.rb +42 -0
  26. data/app/models/system_settings/type/integer_list.rb +10 -15
  27. data/app/models/system_settings/type/string_list.rb +2 -0
  28. data/app/views/layouts/system_settings/application.html.erb +21 -0
  29. data/app/views/system_settings/settings/_common_attributes.html.erb +14 -0
  30. data/app/views/system_settings/settings/_form.html.erb +18 -0
  31. data/app/views/system_settings/settings/edit.html.erb +2 -0
  32. data/app/views/system_settings/settings/index.html.erb +29 -0
  33. data/app/views/system_settings/settings/show.html.erb +9 -0
  34. data/config/locales/system_settings.en.yml +27 -1
  35. data/config/routes.rb +7 -4
  36. data/lib/system_settings.rb +13 -7
  37. data/lib/system_settings/engine.rb +6 -13
  38. data/lib/system_settings/version.rb +3 -1
  39. metadata +17 -14
  40. data/app/controllers/system_settings/root_controller.rb +0 -11
  41. data/app/models/system_settings/pagination.rb +0 -18
  42. data/frontend/build/asset-manifest.json +0 -11
  43. data/frontend/build/favicon.ico +0 -0
  44. data/frontend/build/index.html +0 -1
  45. data/frontend/build/precache-manifest.cace9a6c696c96461d63a2e3a1cca55e.js +0 -22
  46. data/frontend/build/service-worker.js +0 -39
  47. data/frontend/build/static/css/main.003e6dc8.chunk.css +0 -1
  48. data/frontend/build/static/js/2.42b63415.chunk.js +0 -1
  49. data/frontend/build/static/js/main.9315e265.chunk.js +0 -1
  50. data/frontend/build/static/js/runtime~main.a09e9b82.js +0 -1
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SystemSettings
4
+ class DecimalListSetting < SystemSettings::Setting
5
+ attribute :value, SystemSettings::Type::DecimalList.new(scale: 6)
6
+ validates :value, "system_settings/list_of_decimals": true
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SystemSettings
4
+ class DecimalSetting < SystemSettings::Setting
5
+ attribute :value, :decimal, scale: 6
6
+ validates :value, numericality: true
7
+ end
8
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  module Errors
3
5
  class Error < StandardError
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  module Errors
3
5
  class NotFoundError < SystemSettings::Errors::Error
4
6
  end
5
7
  end
6
- end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SystemSettings
4
+ module Errors
5
+ class NotLoadedError < SystemSettings::Errors::Error
6
+ end
7
+ end
8
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  module Errors
3
5
  class SettingsReadError < SystemSettings::Errors::Error
@@ -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
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  class IntegerSetting < SystemSettings::Setting
3
- attribute :value, :integer
5
+ attribute :value, :integer, limit: 8
4
6
  validates :value, numericality: { only_integer: 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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  class ListOfIntegersValidator < ActiveModel::EachValidator
3
5
  LIST_REGEXP = /\A[+-]?\d+(?:; *[+-]?\d+)*\z/.freeze
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  class ListOfStringsValidator < ActiveModel::EachValidator
3
5
  NON_WHITESPACE_REGEXP = /[^[:space:]]/.freeze
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  class Setting < SystemSettings::ApplicationRecord
3
5
  validates :type, presence: true
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  class StringListSetting < SystemSettings::Setting
3
5
  attribute :value, SystemSettings::Type::StringList.new
@@ -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 = ";".freeze
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 do |v|
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 do |v|
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  module Type
3
5
  class StringList < ActiveModel::Type::Value
@@ -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,2 @@
1
+ <%= render "common_attributes", setting: @setting %>
2
+ <%= render "form", setting: @setting %>
@@ -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."
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  SystemSettings::Engine.routes.draw do
2
- scope "api" do
3
- resources "settings"
4
+ resources :settings, only: [:show, :edit] do
5
+ member do
6
+ post "/", to: "settings#update"
7
+ end
4
8
  end
5
- root to: "root#index"
6
- get "*rest", to: "root#index", as: :catch_all
9
+ root to: "settings#index"
7
10
  end
@@ -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 = "Couldn't find system setting #{name}\n\n"
13
- message << "It might not be loaded from settings file(#{settings_file_path}).\n"
14
- message << "To load missing settings with their initial values you "
15
- message << "can call SystemSettings.load from your Rails environment or run Rails task:\n\n"
16
- message << " bin/rails system_settings:load RAILS_ENV=#{::Rails.env}"
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