super_settings 1.0.2 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +110 -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/controller_actions.rb +2 -2
- data/lib/super_settings/engine.rb +1 -1
- data/lib/super_settings/history_item.rb +1 -1
- data/lib/super_settings/http_client.rb +165 -0
- data/lib/super_settings/rack_application.rb +3 -3
- data/lib/super_settings/rest_api.rb +5 -4
- data/lib/super_settings/setting.rb +13 -2
- data/lib/super_settings/storage/active_record_storage.rb +7 -0
- 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 +49 -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 +13 -6
- data/lib/super_settings/time_precision.rb +36 -0
- data/lib/super_settings.rb +11 -0
- data/super_settings.gemspec +4 -2
- metadata +22 -9
- 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
@@ -0,0 +1,62 @@
|
|
1
|
+
.super-settings {
|
2
|
+
--primary-color-h: 216;
|
3
|
+
--primary-color-s: 98%;
|
4
|
+
--primary-color-l: 52%;
|
5
|
+
--primary-color-hsl: var(--primary-color-h), var(--primary-color-s), var(--primary-color-l);
|
6
|
+
--primary-color: hsl(var(--primary-color-hsl));
|
7
|
+
--primary-contrast-color: #fff;
|
8
|
+
--primary-hover-color: hsl(var(--primary-color-h), var(--primary-color-s), calc(var(--primary-color-l) - 10%));
|
9
|
+
--primary-border-color: hsl(var(--primary-color-h), var(--primary-color-s), calc(var(--primary-color-l) + 10%));
|
10
|
+
}
|
11
|
+
|
12
|
+
<% unless color_scheme == :dark %>
|
13
|
+
.super-settings {
|
14
|
+
--edit-bg-color: #f2fdf2;
|
15
|
+
--deleted-row-color: darkred;
|
16
|
+
--deleted-row-bg-color: #ffd1d8;
|
17
|
+
--history-key-color: royalblue;
|
18
|
+
--table-header-bg-color: #fff;
|
19
|
+
--table-border-color: #dee2e6;
|
20
|
+
--alt-row-color: rgba(0, 0, 0, .05);
|
21
|
+
--form-control-color: #495057;
|
22
|
+
--form-control-bg-color: #fff;
|
23
|
+
--form-control-border-color: #ced4da;
|
24
|
+
--form-control-placeholder-color: #bbb;
|
25
|
+
--modal-bg-color: #fff;
|
26
|
+
--modal-transparency: 0.4;
|
27
|
+
--modal-control-color: #000;
|
28
|
+
--success-color: green;
|
29
|
+
--danger-color: firebrick;
|
30
|
+
--muted-color: #666;
|
31
|
+
--unselected-color: #666;
|
32
|
+
}
|
33
|
+
<% end %>
|
34
|
+
|
35
|
+
<% if color_scheme == :system %>
|
36
|
+
@media (prefers-color-scheme: dark) {
|
37
|
+
<% end %>
|
38
|
+
<% if color_scheme == :system || color_scheme == :dark %>
|
39
|
+
.super-settings {
|
40
|
+
--edit-bg-color: #8dc875;
|
41
|
+
--deleted-row-color: #e0b1b8;
|
42
|
+
--deleted-row-bg-color: #7a3636;
|
43
|
+
--history-key-color: #a7d6f4;
|
44
|
+
--table-header-bg-color: #333;
|
45
|
+
--table-border-color: #555;
|
46
|
+
--alt-row-color: rgba(0, 0, 0, .30);
|
47
|
+
--form-control-color: #eee;
|
48
|
+
--form-control-bg-color: #666;
|
49
|
+
--form-control-border-color: #555;
|
50
|
+
--form-control-placeholder-color: #aaa;
|
51
|
+
--modal-bg-color: #333;
|
52
|
+
--modal-transparency: 0.75;
|
53
|
+
--modal-control-color: #fff;
|
54
|
+
--success-color: #00ff00;
|
55
|
+
--danger-color: #ff0000;
|
56
|
+
--muted-color: #999;
|
57
|
+
--unselected-color: #ccc;
|
58
|
+
}
|
59
|
+
<% end %>
|
60
|
+
<% if color_scheme == :system %>
|
61
|
+
}
|
62
|
+
<% end %>
|
@@ -1,3 +1,10 @@
|
|
1
|
+
.super-settings-container {
|
2
|
+
padding-left: 15px;
|
3
|
+
padding-right: 15px;
|
4
|
+
margin-left: auto;
|
5
|
+
margin-right: auto;
|
6
|
+
}
|
7
|
+
|
1
8
|
#settings-table td p {
|
2
9
|
margin-top: 0;
|
3
10
|
margin-bottom: 0.5rem;
|
@@ -11,13 +18,9 @@
|
|
11
18
|
width: 100%;
|
12
19
|
}
|
13
20
|
|
14
|
-
.super-settings-edit-row {
|
15
|
-
background-color: #f2fdf2 !important;
|
16
|
-
}
|
17
|
-
|
18
21
|
#settings-table tr[data-deleted] td {
|
19
|
-
background-color:
|
20
|
-
color:
|
22
|
+
background-color: var(--deleted-row-bg-color) !important;
|
23
|
+
color: var(--deleted-row-color);
|
21
24
|
text-decoration: line-through;
|
22
25
|
}
|
23
26
|
|
@@ -25,16 +28,49 @@
|
|
25
28
|
display: none;
|
26
29
|
}
|
27
30
|
|
31
|
+
.super-settings-icon {
|
32
|
+
display: inline-block;
|
33
|
+
}
|
34
|
+
|
35
|
+
.super-settings-icon svg {
|
36
|
+
width: 100%;
|
37
|
+
height: 100%;
|
38
|
+
vertical-align: inherit;
|
39
|
+
}
|
40
|
+
|
41
|
+
.super-settings-btn-no-chrome {
|
42
|
+
display: inline-block;
|
43
|
+
vertical-align: middle;
|
44
|
+
background-color: transparent;
|
45
|
+
border: 0;
|
46
|
+
padding: 0;
|
47
|
+
cursor: pointer;
|
48
|
+
}
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
.super-settings-sort-control {
|
53
|
+
color: var(--unselected-color);
|
54
|
+
}
|
55
|
+
|
56
|
+
.super-settings-sort-control svg {
|
57
|
+
vertical-align: middle;
|
58
|
+
}
|
59
|
+
|
60
|
+
.super-settings-edit-row {
|
61
|
+
background-color: var(--edit-bg-color) !important;
|
62
|
+
}
|
63
|
+
|
28
64
|
.super-settings-key {
|
29
65
|
overflow-wrap: break-word;
|
30
66
|
max-width: 30rem;
|
31
|
-
min-width:
|
67
|
+
min-width: 8rem;
|
32
68
|
}
|
33
69
|
|
34
70
|
.super-settings-value {
|
35
71
|
overflow-wrap: break-word;
|
36
72
|
max-width: 30rem;
|
37
|
-
min-width:
|
73
|
+
min-width: 8rem;
|
38
74
|
}
|
39
75
|
|
40
76
|
.super-settings-value-type {
|
@@ -59,7 +95,34 @@
|
|
59
95
|
|
60
96
|
.super-settings-history-key {
|
61
97
|
font-weight: normal;
|
62
|
-
color:
|
98
|
+
color: var(--history-key-color);
|
99
|
+
}
|
100
|
+
|
101
|
+
.super-settings-table {
|
102
|
+
width: 100%;
|
103
|
+
max-width: 100%;
|
104
|
+
margin-bottom: 1rem;
|
105
|
+
border-collapse: collapse;
|
106
|
+
}
|
107
|
+
|
108
|
+
.super-settings-table thead th {
|
109
|
+
vertical-align: bottom;
|
110
|
+
border-bottom: 2px solid var(--table-border-color);
|
111
|
+
white-space: nowrap;
|
112
|
+
}
|
113
|
+
|
114
|
+
.super-settings-table td, .super-settings-table th {
|
115
|
+
padding: 0.75rem;
|
116
|
+
vertical-align: top;
|
117
|
+
border-top: 1px solid var(--table-border-color);
|
118
|
+
}
|
119
|
+
|
120
|
+
.super-settings-table-striped tbody tr:nth-of-type(odd) {
|
121
|
+
background-color: var(--alt-row-color);
|
122
|
+
}
|
123
|
+
|
124
|
+
.super-settings-align-center {
|
125
|
+
text-align: center;
|
63
126
|
}
|
64
127
|
|
65
128
|
.super-settings-modal {
|
@@ -72,12 +135,12 @@
|
|
72
135
|
width: 100%;
|
73
136
|
height: 100%;
|
74
137
|
overflow: auto;
|
75
|
-
background-color: rgba(0,0,0,
|
138
|
+
background-color: rgba(0, 0, 0, var(--modal-transparency));
|
76
139
|
}
|
77
140
|
|
78
141
|
.super-settings-modal-dialog {
|
79
142
|
margin: auto;
|
80
|
-
background-color:
|
143
|
+
background-color: var(--modal-bg-color);
|
81
144
|
position: relative;
|
82
145
|
outline: 0;
|
83
146
|
padding: 2em;
|
@@ -99,7 +162,7 @@
|
|
99
162
|
right: 0;
|
100
163
|
border: 0;
|
101
164
|
padding: 1ex;
|
102
|
-
|
165
|
+
color: var(--modal-control-color);
|
103
166
|
font-size: 1.5rem;
|
104
167
|
font-weight: 500;
|
105
168
|
}
|
@@ -122,6 +185,112 @@
|
|
122
185
|
.super-settings-sticky-top {
|
123
186
|
position: sticky;
|
124
187
|
top: 0;
|
125
|
-
padding: 1rem
|
126
|
-
background-color:
|
188
|
+
padding: 1rem;
|
189
|
+
background-color: var(--table-header-bg-color);
|
190
|
+
}
|
191
|
+
|
192
|
+
.super-settings-form-control {
|
193
|
+
font-family: inherit;
|
194
|
+
margin: 0;
|
195
|
+
overflow: visible;
|
196
|
+
display: block;
|
197
|
+
width: 100%;
|
198
|
+
padding: .375rem .75rem;
|
199
|
+
font-size: 1rem;
|
200
|
+
line-height: 1.5;
|
201
|
+
color: var(--form-control-color);
|
202
|
+
background-color: var(--form-control-bg-color);
|
203
|
+
background-clip: padding-box;
|
204
|
+
border: 1px solid var(--form-control-border-color);
|
205
|
+
border-radius: .25rem;
|
206
|
+
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
207
|
+
}
|
208
|
+
|
209
|
+
.super-settings-form-control::placeholder {
|
210
|
+
color: var(--form-control-placeholder-color);
|
211
|
+
}
|
212
|
+
|
213
|
+
select.super-settings-form-control:not([size]):not([multiple]) {
|
214
|
+
height: calc(2.25rem + 2px);
|
215
|
+
}
|
216
|
+
|
217
|
+
.super-settings-form-check {
|
218
|
+
margin-top: .5rem;
|
219
|
+
display: inline-block;
|
220
|
+
}
|
221
|
+
|
222
|
+
.super-settings-form-check input[type=checkbox] {
|
223
|
+
vertical-align: middle;
|
224
|
+
}
|
225
|
+
|
226
|
+
.super-settings-form-inline {
|
227
|
+
display: inline-block;
|
228
|
+
}
|
229
|
+
|
230
|
+
.super-settings-form-inline .super-settings-form-control {
|
231
|
+
display: inline-block;
|
232
|
+
width: auto;
|
233
|
+
vertical-align: middle;
|
234
|
+
}
|
235
|
+
|
236
|
+
.super-settings-form-control textarea {
|
237
|
+
font-family: inherit;
|
238
|
+
margin: 0;
|
239
|
+
overflow: auto;
|
240
|
+
resize: vertical;
|
241
|
+
}
|
242
|
+
|
243
|
+
.super-settings-text-success {
|
244
|
+
color: var(--success-color) !important;
|
245
|
+
}
|
246
|
+
|
247
|
+
.super-settings-text-danger {
|
248
|
+
color: var(--danger-color) !important;
|
249
|
+
}
|
250
|
+
|
251
|
+
.super-settings-text-muted {
|
252
|
+
color: var(--muted-color) !important;
|
253
|
+
}
|
254
|
+
|
255
|
+
.super-settings-btn {
|
256
|
+
display: inline-block;
|
257
|
+
vertical-align: middle;
|
258
|
+
padding: 0.375rem 0.75rem;
|
259
|
+
border-radius: 0.375rem;
|
260
|
+
border: 1px solid #dcdcdc;
|
261
|
+
background-color:#f9f9f9;
|
262
|
+
text-decoration: none;
|
263
|
+
text-align: center;
|
264
|
+
font-family: Arial, sans-serif;
|
265
|
+
font-size: 1rem;
|
266
|
+
font-weight: 400;
|
267
|
+
line-height: 1.5;
|
268
|
+
user-select: none;
|
269
|
+
cursor: pointer;
|
270
|
+
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out, box-shadow .15s ease-in-out;
|
271
|
+
}
|
272
|
+
|
273
|
+
.super-settings-btn:hover:not(:disabled) {
|
274
|
+
background-color:#e9e9e9;
|
275
|
+
}
|
276
|
+
.super-settings-btn:active {
|
277
|
+
position:relative;
|
278
|
+
top:1px;
|
279
|
+
}
|
280
|
+
.super-settings-btn:disabled {
|
281
|
+
opacity: 0.8;
|
282
|
+
cursor: not-allowed;
|
283
|
+
}
|
284
|
+
|
285
|
+
.super-settings-btn-primary {
|
286
|
+
background-color: var(--primary-color);
|
287
|
+
border-color: var(--primary-border-color);
|
288
|
+
color: var(--primary-contrast-color);
|
289
|
+
}
|
290
|
+
.super-settings-btn-primary:hover:not(:disabled) {
|
291
|
+
background-color: var(--primary-hover-color);
|
292
|
+
}
|
293
|
+
|
294
|
+
.super-settings-btn-primary:disabled {
|
295
|
+
opacity: 0.5;
|
127
296
|
}
|
@@ -10,31 +10,38 @@ module SuperSettings
|
|
10
10
|
# @param layout [String, Symbol] path to an ERB template to use as the layout around the application UI. You can
|
11
11
|
# pass the symbol +:default+ to use the default layout that ships with the gem.
|
12
12
|
# @param add_to_head [String] HTML code to add to the <head> element on the page.
|
13
|
-
|
13
|
+
# @param api_base_url [String] the base URL for the REST API.
|
14
|
+
# @param color_scheme [Symbol] whether to use dark mode for the application UI. If +nil+, the user's system
|
15
|
+
# preference will be used.
|
16
|
+
def initialize(layout: nil, add_to_head: nil, api_base_url: nil, color_scheme: nil)
|
14
17
|
if layout
|
15
18
|
layout = File.expand_path(File.join("application", "layout.html.erb"), __dir__) if layout == :default
|
16
|
-
@layout = ERB.new(File.read(layout))
|
19
|
+
@layout = ERB.new(File.read(layout)) if layout
|
17
20
|
@add_to_head = add_to_head
|
21
|
+
else
|
22
|
+
@layout = nil
|
23
|
+
@add_to_head = nil
|
18
24
|
end
|
25
|
+
|
26
|
+
@api_base_url = api_base_url
|
27
|
+
@color_scheme = color_scheme&.to_sym
|
19
28
|
end
|
20
29
|
|
21
|
-
# Render the
|
30
|
+
# Render the web UI application HTML.
|
22
31
|
#
|
23
32
|
# @return [void]
|
24
|
-
def render
|
25
|
-
template = ERB.new(File.read(File.expand_path(File.join("application",
|
33
|
+
def render
|
34
|
+
template = ERB.new(File.read(File.expand_path(File.join("application", "index.html.erb"), __dir__)))
|
26
35
|
html = template.result(binding)
|
27
|
-
if @layout
|
28
|
-
|
29
|
-
|
30
|
-
html
|
31
|
-
end
|
36
|
+
html = render_layout { html } if @layout
|
37
|
+
html = html.html_safe if html.respond_to?(:html_safe)
|
38
|
+
html
|
32
39
|
end
|
33
40
|
|
34
41
|
private
|
35
42
|
|
36
43
|
def render_layout
|
37
|
-
@layout
|
44
|
+
@layout&.result(binding)
|
38
45
|
end
|
39
46
|
end
|
40
47
|
end
|
@@ -4,20 +4,13 @@ module SuperSettings
|
|
4
4
|
# Interface to expose mass setting attributes on an object. Setting attributes with a
|
5
5
|
# hash will simply call the attribute writers for each key in the hash.
|
6
6
|
module Attributes
|
7
|
-
class UnknownAttributeError < StandardError
|
8
|
-
end
|
9
|
-
|
10
7
|
def initialize(attributes = nil)
|
11
8
|
self.attributes = attributes if attributes
|
12
9
|
end
|
13
10
|
|
14
11
|
def attributes=(values)
|
15
12
|
values.each do |name, value|
|
16
|
-
if respond_to?(:"#{name}=", true)
|
17
|
-
send(:"#{name}=", value)
|
18
|
-
else
|
19
|
-
raise UnknownAttributeError.new("unknown attribute #{name.to_s.inspect} for #{self.class}")
|
20
|
-
end
|
13
|
+
send(:"#{name}=", value) if respond_to?(:"#{name}=", true)
|
21
14
|
end
|
22
15
|
end
|
23
16
|
end
|
@@ -25,6 +25,7 @@ module SuperSettings
|
|
25
25
|
def initialize
|
26
26
|
@superclass = nil
|
27
27
|
@web_ui_enabled = true
|
28
|
+
@color_scheme = false
|
28
29
|
@changed_by_block = nil
|
29
30
|
end
|
30
31
|
|
@@ -63,6 +64,14 @@ module SuperSettings
|
|
63
64
|
!!@web_ui_enabled
|
64
65
|
end
|
65
66
|
|
67
|
+
# Set dark mode for the web UI. Possible values are :light, :dark, or :system.
|
68
|
+
# The default value is :light.
|
69
|
+
attr_writer :color_scheme
|
70
|
+
|
71
|
+
def color_scheme
|
72
|
+
(@color_scheme ||= :light).to_sym
|
73
|
+
end
|
74
|
+
|
66
75
|
# Enhance the controller. You can define methods or call controller class methods like
|
67
76
|
# +before_action+, etc. in the block. These will be applied to the engine controller.
|
68
77
|
# This is essentially the same a monkeypatching the controller class.
|
@@ -19,7 +19,7 @@ module SuperSettings
|
|
19
19
|
|
20
20
|
# Render the HTML application for managing settings.
|
21
21
|
def root
|
22
|
-
html = SuperSettings::Application.new.render
|
22
|
+
html = SuperSettings::Application.new.render
|
23
23
|
render html: html.html_safe, layout: true
|
24
24
|
end
|
25
25
|
|
@@ -40,7 +40,7 @@ module SuperSettings
|
|
40
40
|
|
41
41
|
# API endpoint for updating settings. See SuperSettings::RestAPI for details.
|
42
42
|
def update
|
43
|
-
changed_by =
|
43
|
+
changed_by = SuperSettings.configuration.controller.changed_by(self)
|
44
44
|
result = SuperSettings::RestAPI.update(params[:settings], changed_by)
|
45
45
|
if result[:success]
|
46
46
|
render json: result
|
@@ -27,7 +27,7 @@ module SuperSettings
|
|
27
27
|
|
28
28
|
config.after_initialize do
|
29
29
|
# Call the deferred initialization block.
|
30
|
-
configuration =
|
30
|
+
configuration = SuperSettings.configuration
|
31
31
|
configuration.call
|
32
32
|
|
33
33
|
SuperSettings.refresh_interval = configuration.refresh_interval unless configuration.refresh_interval.nil?
|
@@ -27,7 +27,7 @@ module SuperSettings
|
|
27
27
|
def changed_by_display
|
28
28
|
return changed_by if changed_by.nil?
|
29
29
|
|
30
|
-
display_proc =
|
30
|
+
display_proc = SuperSettings.configuration.model.changed_by_display
|
31
31
|
if display_proc && !changed_by.nil?
|
32
32
|
display_proc.call(changed_by) || changed_by
|
33
33
|
else
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
require "uri"
|
5
|
+
|
6
|
+
module SuperSettings
|
7
|
+
# This is a simple HTTP client that is used to communicate with the REST API. It
|
8
|
+
# will keep the connection alive and reuse it on subsequent requests.
|
9
|
+
class HttpClient
|
10
|
+
DEFAULT_HEADERS = {"Accept" => "application/json"}.freeze
|
11
|
+
DEFAULT_TIMEOUT = 5.0
|
12
|
+
KEEP_ALIVE_TIMEOUT = 60
|
13
|
+
|
14
|
+
class Error < StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
class NotFoundError < Error
|
18
|
+
end
|
19
|
+
|
20
|
+
class InvalidRecordError < Error
|
21
|
+
attr_reader :errors
|
22
|
+
|
23
|
+
def initialize(message, errors:)
|
24
|
+
super(message)
|
25
|
+
@errors = errors
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(base_url, headers: nil, params: nil, timeout: nil, user: nil, password: nil)
|
30
|
+
base_url = "#{base_url}/" unless base_url.end_with?("/")
|
31
|
+
@base_uri = URI(base_url)
|
32
|
+
@base_uri.query = query_string(params) if params
|
33
|
+
@headers = headers ? DEFAULT_HEADERS.merge(headers) : DEFAULT_HEADERS
|
34
|
+
@timeout = timeout || DEFAULT_TIMEOUT
|
35
|
+
@user = user
|
36
|
+
@password = password
|
37
|
+
@mutex = Mutex.new
|
38
|
+
@connections = []
|
39
|
+
end
|
40
|
+
|
41
|
+
def get(path, params = nil)
|
42
|
+
request = Net::HTTP::Get.new(request_uri(path, params))
|
43
|
+
send_request(request)
|
44
|
+
end
|
45
|
+
|
46
|
+
def post(path, params = nil)
|
47
|
+
request = Net::HTTP::Post.new(request_uri(path))
|
48
|
+
request.body = JSON.dump(params) if params
|
49
|
+
send_request(request)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def send_request(request)
|
55
|
+
set_headers(request)
|
56
|
+
response_payload = nil
|
57
|
+
attempts = 0
|
58
|
+
|
59
|
+
with_connection do |http|
|
60
|
+
http.start unless http.started?
|
61
|
+
response = http.request(request)
|
62
|
+
|
63
|
+
begin
|
64
|
+
response.value # raises exception unless response is a success
|
65
|
+
response_payload = JSON.parse(response.body)
|
66
|
+
rescue Net::ProtocolError
|
67
|
+
if [404, 410].include?(response.code.to_i)
|
68
|
+
raise NotFoundError.new("#{response.code} #{response.message}")
|
69
|
+
elsif response.code.to_i == 422
|
70
|
+
raise InvalidRecordError.new("#{response.code} #{response.message}", errors: JSON.parse(response.body)["errors"])
|
71
|
+
else
|
72
|
+
raise Error.new("#{response.code} #{response.message}")
|
73
|
+
end
|
74
|
+
rescue JSON::JSONError => e
|
75
|
+
raise Error.new(e.message)
|
76
|
+
end
|
77
|
+
rescue IOError, Errno::ECONNRESET => connection_error
|
78
|
+
attempts += 1
|
79
|
+
retry if attempts <= 1
|
80
|
+
raise connection_error
|
81
|
+
end
|
82
|
+
|
83
|
+
response_payload
|
84
|
+
end
|
85
|
+
|
86
|
+
def with_connection(&block)
|
87
|
+
http = pop_connection
|
88
|
+
begin
|
89
|
+
response = yield(http)
|
90
|
+
return_connection(http)
|
91
|
+
response
|
92
|
+
rescue => e
|
93
|
+
begin
|
94
|
+
http.finish if http.started?
|
95
|
+
rescue IOError
|
96
|
+
end
|
97
|
+
raise e
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def pop_connection
|
102
|
+
http = nil
|
103
|
+
@mutex.synchronize do
|
104
|
+
http = @connections.pop
|
105
|
+
end
|
106
|
+
http = nil unless http&.started?
|
107
|
+
http ||= new_connection
|
108
|
+
http
|
109
|
+
end
|
110
|
+
|
111
|
+
def return_connection(http)
|
112
|
+
@mutex.synchronize do
|
113
|
+
if @connections.empty?
|
114
|
+
@connections.push(http)
|
115
|
+
http = nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
if http
|
120
|
+
begin
|
121
|
+
http.finish if http.started?
|
122
|
+
rescue IOError
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def new_connection
|
128
|
+
http = Net::HTTP.new(@base_uri.host, @base_uri.port || @base_uri.inferred_port)
|
129
|
+
http.use_ssl = @base_uri.scheme == "https"
|
130
|
+
http.open_timeout = @timeout
|
131
|
+
http.read_timeout = @timeout
|
132
|
+
http.write_timeout = @timeout
|
133
|
+
http.keep_alive_timeout = KEEP_ALIVE_TIMEOUT
|
134
|
+
http
|
135
|
+
end
|
136
|
+
|
137
|
+
def set_headers(request)
|
138
|
+
@headers.each do |name, value|
|
139
|
+
name = name.to_s
|
140
|
+
values = Array(value)
|
141
|
+
request[name] = values[0].to_s
|
142
|
+
values[1, values.length].each do |val|
|
143
|
+
request.add_field(name, val.to_s)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def request_uri(path, params = nil)
|
149
|
+
uri = URI.join(@base_uri, path.delete_prefix("/"))
|
150
|
+
if (params && !params.empty?) || (@base_uri.query && !@base_uri.query.empty?)
|
151
|
+
uri.query = [uri.query, query_string(params)].join("&")
|
152
|
+
end
|
153
|
+
uri
|
154
|
+
end
|
155
|
+
|
156
|
+
def query_string(params)
|
157
|
+
q = []
|
158
|
+
q << @base_uri.query unless @base_uri.query.to_s.empty?
|
159
|
+
params&.each do |name, value|
|
160
|
+
q << "#{URI.encode_www_form_component(name.to_s)}=#{URI.encode_www_form_component(value.to_s)}"
|
161
|
+
end
|
162
|
+
q.join("&")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -122,7 +122,7 @@ module SuperSettings
|
|
122
122
|
#
|
123
123
|
# @return [Boolean]
|
124
124
|
def web_ui_enabled?
|
125
|
-
|
125
|
+
SuperSettings.configuration.controller.web_ui_enabled?
|
126
126
|
end
|
127
127
|
|
128
128
|
private
|
@@ -159,7 +159,7 @@ module SuperSettings
|
|
159
159
|
|
160
160
|
def handle_root_request(request)
|
161
161
|
response = check_authorization(request, write_required: true) do |user|
|
162
|
-
[200, {"content-type" => "text/html; charset=utf-8", "cache-control" => "no-cache"}, [Application.new(:default, add_to_head(request)
|
162
|
+
[200, {"content-type" => "text/html; charset=utf-8", "cache-control" => "no-cache"}, [Application.new(layout: :default, add_to_head: add_to_head(request), color_scheme: SuperSettings.configuration.controller.color_scheme).render]]
|
163
163
|
end
|
164
164
|
|
165
165
|
if [401, 403].include?(response.first)
|
@@ -236,7 +236,7 @@ module SuperSettings
|
|
236
236
|
|
237
237
|
def post_params(request)
|
238
238
|
if request.content_type.to_s.match?(/\Aapplication\/json/i) && request.body
|
239
|
-
request.params.merge(JSON.parse(request.body.
|
239
|
+
request.params.merge(JSON.parse(request.body.read))
|
240
240
|
else
|
241
241
|
request.params
|
242
242
|
end
|
@@ -23,7 +23,7 @@ module SuperSettings
|
|
23
23
|
# ...
|
24
24
|
# ]
|
25
25
|
def index
|
26
|
-
settings = Setting.active.sort_by(&:key)
|
26
|
+
settings = Setting.active.reject(&:deleted?).sort_by(&:key)
|
27
27
|
{settings: settings.collect(&:as_json)}
|
28
28
|
end
|
29
29
|
|
@@ -46,7 +46,8 @@ module SuperSettings
|
|
46
46
|
# updated_at: iso8601 string
|
47
47
|
# }
|
48
48
|
def show(key)
|
49
|
-
Setting.find_by_key(key)
|
49
|
+
setting = Setting.find_by_key(key)
|
50
|
+
setting.as_json if setting && !setting.deleted?
|
50
51
|
end
|
51
52
|
|
52
53
|
# The update operation uses a transaction to atomically update all settings.
|
@@ -156,7 +157,7 @@ module SuperSettings
|
|
156
157
|
#
|
157
158
|
# @example
|
158
159
|
# GET /last_updated_at
|
159
|
-
#
|
160
|
+
#
|
160
161
|
# The response payload is:
|
161
162
|
# {
|
162
163
|
# last_updated_at: iso8601 string
|
@@ -188,7 +189,7 @@ module SuperSettings
|
|
188
189
|
# ]
|
189
190
|
def updated_since(time)
|
190
191
|
time = Coerce.time(time)
|
191
|
-
settings = Setting.updated_since(time)
|
192
|
+
settings = Setting.updated_since(time).reject(&:deleted?)
|
192
193
|
{settings: settings.collect(&:as_json)}
|
193
194
|
end
|
194
195
|
end
|