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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2436379d110a7e6416bce537892e89d29c0bf9ae6319abc843766010f9ed5e03
4
- data.tar.gz: ee3c15960c0cde816a6078abd303fb892dddd28609990176ab427d35602e435d
3
+ metadata.gz: 321c73f1b71d88e67c98191025ac69b6fa58ea6dbbaa838cb5a6eaea6622337a
4
+ data.tar.gz: 17bd42a54230f37063f618fe2cfebc3875410d3879757e9ed38138990c6a952d
5
5
  SHA512:
6
- metadata.gz: dc1d77aade5cdebcd53fd487d91a1a24c1cc0b4b444ce5b9ebab3fe0439f89df68e320497453a220c7b4c1b8c648515c56a4a7defb73cd379f71c6b04c702182
7
- data.tar.gz: a85ef1e09a368464d980cda6a5ff180191d9bb5dda3bdb49a80701a1c5c80b72935a5e76b492a21a82654a730956a3618ee428ed1c03872ecc312dc1aebcd2f9
6
+ metadata.gz: 4231c986165f29d944aec79b4cc5239ccc9f7d614ef710ec77e452a238f8579ec3afcf20c09c0701ec49b3a89547ebbfcc3e4f395a71d024b1349fadb8df1cfc
7
+ data.tar.gz: bab2dd0fbf7b7f5f38ea38e4ebdcb1d732eb0254a2e3278925ac16408cca8d3c05c898137c92e7916c2b4f85c6105a9ad1e1ec5be0e51eb9d8a0fa617ff3fd47
data/README.md CHANGED
@@ -42,10 +42,14 @@ string :default_locale, value: "en"
42
42
  integer :default_records_per_page, value: 25
43
43
  integer :remainder_interval_in_hours, value: 48
44
44
 
45
- # Array type strings and integers
45
+ # Decimal values
46
+ decimal :max_temp, value: 95.2
47
+
48
+ # Array type strings, integers and decimals
46
49
  string_list :admin_emails, description: "Will receive alerts"
47
50
  string_list :upload_allowed_extensions, value: ["docx", "pdf", "txt"]
48
51
  integer_list :lucky_numbers, description: "Prime numbers are more effective", value: [2, 3, 5, 11]
52
+ decimal_list :allowed_multipliers, value: [12.3, 99, BigDecimal("-87")]
49
53
  ```
50
54
 
51
55
  Load values from `config/system_settings.rb` into database:
@@ -93,19 +97,16 @@ If you would like to store your settings somewhere else than `config/system_sett
93
97
 
94
98
  ## Using System Settings in tests
95
99
 
96
- Your test suite probably clears database before/after every test example. Fortunately is very easy to load fresh settings from configuration file. It can be done by running `SystemSettings.load`.
100
+ Your test suite probably clears database before/after every test example. Fortunately is very easy to load fresh settings from configuration file.
101
+ It can be done by running `SystemSettings.load`. It will persist all loaded settings. But if you would like to persist only a subset of loaded settings run `SystemSettings.load(:one, :two, :three)`.
102
+
97
103
  And if you modify settings values in test example you can reset to defaults with `SystemSettings.reset_to_defaults`.
98
104
 
99
105
 
100
106
  ## Development
101
107
 
102
- Required development dependencies:
103
- * [Node.js](https://nodejs.org/) - JavaScript runtime
104
- * [Yarn](https://yarnpkg.com/) - package manager
105
-
106
108
  Optional development tools:
107
- * [overmind](https://github.com/DarthSim/overmind) - Process manager for Procfile-based applications and tmux
108
- * [direnv](https://direnv.net/) - Unclutter your .profile
109
+ * [direnv](https://direnv.net/) - Unclutter your .profile
109
110
 
110
111
  Required environment variables:
111
112
  * `RAILS_VERSION`
@@ -120,16 +121,13 @@ Getting started with development:
120
121
  1) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 bundle`
121
122
  2) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 ./bin/rails db:create db:migrate`
122
123
  3) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 ./bin/rails test`
123
- 4) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 ./bin/rails frontend:install`
124
- 4) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 ./bin/rails app:system_settings:load`
125
- 4) `RAILS_VERSION=5.2.3 SQLITE3_VERSION=1.4.1 overmind start`
126
-
127
124
 
128
125
  ## Build status
129
126
  System Settings is being tested with Rails versions - `5.0`, `5.1`, `5.2`, `6.0`, `rails repo master branch`
130
127
 
131
128
  [![Build Status](https://dev.azure.com/kristsozols/System%20Settings/_apis/build/status/krists.system_settings?branchName=master)](https://dev.azure.com/kristsozols/System%20Settings/_build/latest?definitionId=1&branchName=master)
132
-
129
+ [![Maintainability](https://api.codeclimate.com/v1/badges/3ad889ca36f62bad04dc/maintainability)](https://codeclimate.com/github/krists/system_settings/maintainability)
130
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/3ad889ca36f62bad04dc/test_coverage)](https://codeclimate.com/github/krists/system_settings/test_coverage)
133
131
 
134
132
  ## License
135
133
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1 @@
1
+ //= link_directory ../stylesheets .css
@@ -0,0 +1,227 @@
1
+ html {
2
+ box-sizing: border-box;
3
+ font-size: 14px;
4
+ font-family: Arial, Helvetica, sans-serif;
5
+ overflow-y: scroll;
6
+ }
7
+
8
+ *, *:before, *:after {box-sizing: inherit;}
9
+
10
+ body {
11
+ margin: 0;
12
+ padding: 0;
13
+ font-weight: normal;
14
+ min-height: 100vh;
15
+ position: relative;
16
+ display: flex;
17
+ flex-direction: column;
18
+ color: #233656;
19
+ background: white;
20
+ font-size: 1rem;
21
+ line-height: 1.5rem;
22
+ }
23
+
24
+ header {
25
+ border-bottom: 1px solid #e1e1e1;
26
+ height: 2rem;
27
+ padding: 0.5rem 0;
28
+ box-sizing: content-box;
29
+ background: white;
30
+ position: sticky;
31
+ top: 0;
32
+ width: 100%;
33
+ }
34
+
35
+ main {
36
+ padding: 3rem 0;
37
+ }
38
+
39
+ header .container,
40
+ main .container {
41
+ max-width: 1020px;
42
+ margin: 0 auto;
43
+ }
44
+
45
+ a { color: #233656; text-decoration: none; }
46
+ a:hover { text-decoration: underline; }
47
+
48
+ .app-name, .app-name:hover, .app-name:active {
49
+ font-size: 1.5rem;
50
+ line-height: 2rem;
51
+ font-weight: bold;
52
+ user-select: none;
53
+ text-decoration: none;
54
+ }
55
+
56
+ .sys-name {
57
+ display: inline-block;
58
+ background: #f2f2f2;
59
+ border-radius: 3px;
60
+ padding: 0 0.5em;
61
+ font-family: "Courier New", Courier, monospace;
62
+ font-size: 0.75em;
63
+ max-width: 100%;
64
+ }
65
+
66
+ table th {
67
+ text-align: left;
68
+ }
69
+
70
+ .settings-table {
71
+ width: 100%;
72
+ table-layout: fixed;
73
+ white-space: nowrap;
74
+ user-select: none;
75
+ }
76
+
77
+ .settings-table td:empty:after {
78
+ content: "-";
79
+ }
80
+
81
+ .settings-table td {
82
+ overflow: hidden;
83
+ text-overflow: ellipsis;
84
+ }
85
+
86
+ .settings-table .description { width: 35% }
87
+ .settings-table .value { width: 25% }
88
+
89
+ .settings-table tbody td.no-entries {
90
+ color: #737373;
91
+ padding-top: 1rem;
92
+ margin-bottom: 1rem;
93
+ }
94
+
95
+ .settings-table tfoot td {
96
+ font-size: 0.8em;
97
+ padding-top: 1rem;
98
+ margin-bottom: 1rem;
99
+ }
100
+
101
+ .settings-table .value .value-part, .attribute .value .value-part {
102
+ max-width: 100%;
103
+ margin-right: 0.25em;
104
+ }
105
+
106
+ .settings-table .value .value-part:last-child, .attribute .value .value-part:last-child {
107
+ margin-right: 0;
108
+ }
109
+
110
+ .attribute {
111
+ display: flex;
112
+ flex-direction: column;
113
+ margin-bottom: 1rem;
114
+ }
115
+
116
+ .attribute .name {
117
+ font-size: 0.8em;
118
+ color: #737373;
119
+ user-select: none;
120
+ }
121
+
122
+ .attribute .value {
123
+ align-self: flex-start;
124
+ }
125
+ .attribute .value:empty:after {
126
+ content: "-";
127
+ }
128
+
129
+ .attribute .field_with_errors .name, .attribute .errors {
130
+ color: #d01d1d;
131
+ }
132
+ .attribute .hint {
133
+ font-size: 0.8em;
134
+ display: block;
135
+ }
136
+
137
+ .buttons {
138
+ border-top: 1px dotted #f2f2f2;
139
+ padding-top: 1rem;
140
+ margin-bottom: 1rem;
141
+ }
142
+ .buttons > * {
143
+ margin-right: 0.5rem;
144
+ }
145
+
146
+ button, input[type=submit], input[type=button], a.button {
147
+ display: inline-block;
148
+ cursor: pointer;
149
+ border: none;
150
+ border-radius: 3px;
151
+ padding: 0.375rem 0.75rem;
152
+ font-size: 1rem;
153
+ line-height: 1.5;
154
+ user-select: none;
155
+ white-space: nowrap;
156
+ }
157
+
158
+ button, input[type=submit], input[type=button] {
159
+ color: #4d4d4d;
160
+ background-color: #d9d9d9;
161
+ transition: background-color 0.1s ease-in-out;
162
+ outline: none;
163
+ }
164
+
165
+ button:focus, button:hover, input[type=submit]:focus, input[type=submit]:hover, input[type=button]:focus, input[type=button]:hover {
166
+ background-color: #b8b8b8;
167
+ }
168
+
169
+ button:active, input[type=submit]:active, input[type=button]:active {
170
+ background-color: #828282;
171
+ }
172
+
173
+ a.button {
174
+ color: #4d4d4d;
175
+ background-color: transparent;
176
+ transition: background-color 0.1s ease-in-out;
177
+ outline: none;
178
+ text-decoration: none;
179
+ }
180
+
181
+ a.button:focus, a.button:hover {
182
+ background-color: rgba(0, 0, 0, 0.15);
183
+ }
184
+
185
+ a.button:active {
186
+ background-color: rgba(0, 0, 0, 0.4);
187
+ }
188
+
189
+ button.primary, input[type=submit].primary, input[type=button].primary, a.button.primary {
190
+ color: #4d4d4d;
191
+ background-color: #a6a6a6;
192
+ transition: background-color 0.1s ease-in-out;
193
+ outline: none;
194
+ }
195
+
196
+ button.primary:focus, button.primary:hover, input[type=submit].primary:focus, input[type=submit].primary:hover, input[type=button].primary:focus, input[type=button].primary:hover, a.button.primary:focus, a.button.primary:hover {
197
+ background-color: #8d8d8d;
198
+ }
199
+
200
+ button.primary:active, input[type=submit].primary:active, input[type=button].primary:active, a.button.primary:active {
201
+ background-color: #646464;
202
+ }
203
+
204
+ input[type=text] {
205
+ border: none;
206
+ border-radius: 3px;
207
+ padding: 0.5em;
208
+ font-size: 1rem;
209
+ line-height: normal;
210
+ color: #4d4d4d;
211
+ background-color: #e6e6e6;
212
+ transition: background-color 0.1s ease-in-out;
213
+ outline: none;
214
+ box-sizing: content-box;
215
+ width: auto;
216
+ min-width: 12ch;
217
+ max-width: calc(100% - 2em);
218
+ will-change: width;
219
+ }
220
+
221
+ input[type=text]:focus {
222
+ background-color: #c4c4c4;
223
+ }
224
+
225
+ input[type=text]:active {
226
+ background-color: #bfbfbf;
227
+ }
@@ -1,11 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  class ApplicationController < ActionController::Base
3
5
  protect_from_forgery with: :exception
4
-
5
- private
6
-
7
- def add_authenticity_token
8
- response.headers["Authenticity-Token"] = form_authenticity_token if protect_against_forgery?
9
- end
10
6
  end
11
7
  end
@@ -1,29 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  class SettingsController < SystemSettings::ApplicationController
3
- RETURN_ATTRIBUTES = ["id", "name", "type", "value", "description"].freeze
5
+ RETURN_ATTRIBUTES = %w[id name type value description].freeze
6
+ before_action :set_setting, only: [:edit, :show, :update]
4
7
 
5
8
  def index
6
- @total_count = SystemSettings::Setting.count
7
- @settings = SystemSettings::Setting.order(:name).extending(SystemSettings::Pagination).page(params[:page], per_page: params[:per])
8
- render json: {
9
- items: @settings.map { |s| s.as_json(only: RETURN_ATTRIBUTES) },
10
- total_count: @total_count
11
- }
9
+ @settings = SystemSettings::Setting.order(:name)
12
10
  end
13
11
 
14
- def show
15
- @setting = SystemSettings::Setting.find(params[:id])
16
- add_authenticity_token
17
- render json: @setting.as_json(only: RETURN_ATTRIBUTES)
18
- end
12
+ def edit; end
13
+
14
+ def show; end
19
15
 
20
16
  def update
21
- @setting = SystemSettings::Setting.find(params[:id])
22
- if @setting.update(value: params[:value])
23
- render json: @setting.as_json(only: RETURN_ATTRIBUTES)
17
+ if @setting.update(setting_params)
18
+ redirect_to setting_path(@setting)
24
19
  else
25
- render json: {errors: @setting.errors.as_json}, status: :unprocessable_entity
20
+ render :edit
26
21
  end
27
22
  end
23
+
24
+ private
25
+
26
+ def set_setting
27
+ @setting = SystemSettings::Setting.find(params[:id])
28
+ end
29
+
30
+ def setting_params
31
+ params.require(:setting).permit(:value)
32
+ end
28
33
  end
29
34
  end
@@ -0,0 +1,34 @@
1
+ module SystemSettings::ApplicationHelper
2
+ SEPARATOR = ";".freeze
3
+
4
+ def format_value(value)
5
+ if value.respond_to?(:each)
6
+ capture do
7
+ value.each do |v|
8
+ concat content_tag(:span, v, class: "value-part")
9
+ end
10
+ end
11
+ else
12
+ value
13
+ end
14
+ end
15
+
16
+ def format_value_for_form(record)
17
+ case record
18
+ when SystemSettings::StringListSetting
19
+ record.value.map { |v| v.gsub(SEPARATOR, "\\#{SEPARATOR}") }.join(SEPARATOR)
20
+ when SystemSettings::IntegerListSetting, SystemSettings::DecimalListSetting
21
+ record.value.join(SEPARATOR)
22
+ else
23
+ record.value
24
+ end
25
+ end
26
+
27
+ def display_settings_file_path
28
+ if SystemSettings.settings_file_path.to_s.include?(Rails.root.to_s)
29
+ Pathname.new(SystemSettings.settings_file_path).relative_path_from(Rails.root)
30
+ else
31
+ SystemSettings.settings_file_path
32
+ end
33
+ end
34
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  class ApplicationRecord < ActiveRecord::Base
3
5
  self.abstract_class = true
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  class BooleanSetting < SystemSettings::Setting
3
5
  attribute :value, :boolean
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SystemSettings
2
4
  class Configurator
3
5
  class << self
@@ -53,20 +55,33 @@ module SystemSettings
53
55
  add(name, SystemSettings::BooleanSetting, value: value, description: description, &blk)
54
56
  end
55
57
 
56
- def persist
58
+ def decimal(name, value: nil, description: nil, &blk)
59
+ add(name, SystemSettings::DecimalSetting, value: value, description: description, &blk)
60
+ end
61
+
62
+ def decimal_list(name, value: nil, description: nil, &blk)
63
+ add(name, SystemSettings::DecimalListSetting, value: value || [], description: description, &blk)
64
+ end
65
+
66
+ def persist(only: [])
57
67
  SystemSettings.instrument("system_settings.persist", items: @items) do |payload|
58
68
  if settings_table_exists?
59
69
  SystemSettings::Setting.transaction do
60
- @items.each do |entry|
61
- persisted_record = SystemSettings::Setting.find_by(name: entry[:name])
62
- if persisted_record
63
- if persisted_record.class == entry[:class]
64
- persisted_record.update!(description: entry[:description])
65
- else
66
- warn "SystemSettings: Type mismatch detected! Previously #{entry[:name]} had type #{persisted_record.class.name} but you are loading #{entry[:class].name}"
70
+ if only.empty?
71
+ @items.each { |item| create_or_update_item(item) }
72
+ else
73
+ only.each do |wanted_name|
74
+ item = @items.find { |i| i[:name] == wanted_name } || begin
75
+ loaded_names = @items.empty? ? "(none)" : @items.map{ |i| i[:name] }.join("\n")
76
+ message = <<~MESSAGE.strip
77
+ Couldn't persist system setting #{wanted_name}. There are no items by this name. Could it be a typo?
78
+
79
+ Configurator has loaded following items:
80
+ #{loaded_names}
81
+ MESSAGE
82
+ raise(SystemSettings::Errors::NotLoadedError, message)
67
83
  end
68
- else
69
- entry[:class].create!(name: entry[:name], value: entry[:value], description: entry[:description])
84
+ create_or_update_item(item)
70
85
  end
71
86
  end
72
87
  end
@@ -104,5 +119,18 @@ module SystemSettings
104
119
  value = value.call if value.is_a?(Proc)
105
120
  @items.push(name: name, class: class_const, value: value, description: description)
106
121
  end
122
+
123
+ def create_or_update_item(item)
124
+ persisted_record = SystemSettings::Setting.find_by(name: item[:name])
125
+ if persisted_record
126
+ if persisted_record.class == item[:class]
127
+ persisted_record.update!(description: item[:description])
128
+ else
129
+ warn "SystemSettings: Type mismatch detected! Previously #{item[:name]} had type #{persisted_record.class.name} but you are loading #{item[:class].name}"
130
+ end
131
+ else
132
+ item[:class].create!(name: item[:name], value: item[:value], description: item[:description])
133
+ end
134
+ end
107
135
  end
108
136
  end