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.
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