super_settings 2.0.3 → 2.1.1
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 +20 -0
- data/README.md +4 -0
- data/VERSION +1 -1
- data/lib/super_settings/application/scripts.js +1 -1
- data/lib/super_settings/coerce.rb +2 -0
- data/lib/super_settings/configuration.rb +5 -0
- data/lib/super_settings/history_item.rb +9 -0
- data/lib/super_settings/rest_api.rb +2 -2
- data/lib/super_settings/setting.rb +13 -6
- data/lib/super_settings/storage/active_record_storage/models.rb +7 -0
- data/lib/super_settings/storage/active_record_storage.rb +9 -14
- data/lib/super_settings/storage/http_storage.rb +4 -4
- data/lib/super_settings/storage/json_storage.rb +54 -26
- data/lib/super_settings/storage/mongodb_storage.rb +34 -24
- data/lib/super_settings/storage/null_storage.rb +72 -0
- data/lib/super_settings/storage/redis_storage.rb +4 -4
- data/lib/super_settings/storage/test_storage.rb +20 -7
- data/lib/super_settings/storage/transaction.rb +3 -2
- data/lib/super_settings/storage.rb +11 -3
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c245924ff453f5cd0bde098003e6dc05d54b21aba4a42ddaf7c614c63233a542
|
4
|
+
data.tar.gz: fb8672624d5c688794f8492be263534ed4e46ac49684f76d9bbf3bd120b62579
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 605db9f00bbd1865203caaf7515c3d5f1ad83911653dd9410c69344dda0b21b8e58167806186969a6d2b27d4867a61d97b29333715c3f139741f16a3d3b0a635
|
7
|
+
data.tar.gz: b1565e3ace970a26a8d126ec27e3bee6786b5da85e33e25bbde5a947af12e8067a23d143870b7012dd449b86ab5658fbeb99fb365bc8f738276cf8672ffc318d
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,26 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## 2.1.1
|
8
|
+
|
9
|
+
### Fixed
|
10
|
+
|
11
|
+
- Added check to ensure that ActiveRecord has a connection to the database to avoid error when settings are checked before the database is connected or when the database doesn't yet exist.
|
12
|
+
|
13
|
+
### Added
|
14
|
+
|
15
|
+
- Added `:null` storage engine that doesn't store settings at all. This is useful for testing or when the storage engine is no available in your continuous integration environment.
|
16
|
+
|
17
|
+
## 2.1.0
|
18
|
+
|
19
|
+
## Fixed
|
20
|
+
|
21
|
+
- More robust handling of history tracking when keys are deleted and then reused. Previously, the history was not fully recorded when a key was reused. Now the history on the old key is recorded as a delete and the history on the new key is recorded as being an update.
|
22
|
+
|
23
|
+
## Changed
|
24
|
+
|
25
|
+
- Times are now consistently encoded in UTC in ISO-8601 format with microseconds whenever they are serialized to JSON.
|
26
|
+
|
7
27
|
## 2.0.3
|
8
28
|
|
9
29
|
### Fixed
|
data/README.md
CHANGED
@@ -154,6 +154,8 @@ This gem abstracts out the storage engine and can support multiple storage mecha
|
|
154
154
|
* `SuperSettings::Storage::RedisStorage` - Stores the settings in a Redis database using the [redis](https://github.com/redis/redis-rb) gem.
|
155
155
|
* `SuperSettings::Storage::HttpStorage` - Uses the SuperSettings REST API running on another server. This is useful in a microservices architecture so you can have a central settings server used by all the services.
|
156
156
|
* `SuperSettings::Storage::S3Storage` - Stores the settings in JSON in an S3 object. This is useful for applications running on AWS that want to store settings in a central location since it does not require a dedicated database. It is possible to read the settings directly from S3 from another application.
|
157
|
+
* `SuperSettings::Storage::MongoDBStorage` - Stores the settings in a MongoDB database using the [mongo](https://github.com/mongodb/mongo-ruby-driver) gem.
|
158
|
+
* `SuperSettings::Storage::NullStorage` - Does not store settings at all. This is useful for testing or when the storage engine is not available in your continuous integration environment.
|
157
159
|
|
158
160
|
Additional storage engines can be built by creating a class that includes `SuperSettings::Storage` and implements the unimplemented methods in that module.
|
159
161
|
|
@@ -391,10 +393,12 @@ bundle exec rackup
|
|
391
393
|
|
392
394
|
By default this will use Redis for storage using the default Redis URL. You can change the storage engine with the `STORAGE` environment variable.
|
393
395
|
|
396
|
+
- `active_record` - Use the ActiveRecord storage engine.
|
394
397
|
- `redis` - Use the Redis storage engine. The Redis URL can be set with the `REDIS_URL` environment variable.
|
395
398
|
- `http` - Use the HTTP storage engine. The URL for the REST API can be set with the `REST_API_URL` environment variable.
|
396
399
|
- `s3` - Use the S3 storage engine. The S3 URL can be set with the `S3_URL` environment variable.
|
397
400
|
- `mongodb` - Use the MongoDB storage engine. The MongoDB URL can be set with the `MONGODB_URL` environment variable.
|
401
|
+
- `null` - Use the null storage engine. This storage engine does not store settings at all.
|
398
402
|
|
399
403
|
You can bring up all of these storage engines locally with the included docker-compose configuration.
|
400
404
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.1.1
|
@@ -384,7 +384,7 @@
|
|
384
384
|
payload.histories.forEach(function(history) {
|
385
385
|
const date = new Date(Date.parse(history.created_at));
|
386
386
|
const dateString = dateFormatter().format(date);
|
387
|
-
const value = (
|
387
|
+
const value = (history.deleted ? '<em class="super-settings-text-danger">deleted</em>' : escapeHTML(history.value));
|
388
388
|
rowsHTML += `<tr><td class="super-settings-text-nowrap">${escapeHTML(dateString)}</td><td>${escapeHTML(history.changed_by)}</td><td>${value}</td></tr>`;
|
389
389
|
});
|
390
390
|
tbody.insertAdjacentHTML("beforeend", rowsHTML);
|
@@ -36,6 +36,7 @@ module SuperSettings
|
|
36
36
|
def time(value)
|
37
37
|
value = nil if value.nil? || value.to_s.empty?
|
38
38
|
return nil if value.nil?
|
39
|
+
|
39
40
|
time = if value.is_a?(Numeric)
|
40
41
|
Time.at(value)
|
41
42
|
elsif value.respond_to?(:to_time)
|
@@ -52,6 +53,7 @@ module SuperSettings
|
|
52
53
|
# @return [Boolean] true if the value is nil or empty.
|
53
54
|
def blank?(value)
|
54
55
|
return true if value.nil?
|
56
|
+
|
55
57
|
if value.respond_to?(:empty?)
|
56
58
|
value.empty?
|
57
59
|
else
|
@@ -27,6 +27,10 @@ module SuperSettings
|
|
27
27
|
@web_ui_enabled = true
|
28
28
|
@color_scheme = false
|
29
29
|
@changed_by_block = nil
|
30
|
+
@enhancement = nil
|
31
|
+
@application_name = nil
|
32
|
+
@application_logo = nil
|
33
|
+
@application_link = nil
|
30
34
|
end
|
31
35
|
|
32
36
|
def superclass
|
@@ -119,6 +123,7 @@ module SuperSettings
|
|
119
123
|
@storage = :active_record
|
120
124
|
@after_save_blocks = []
|
121
125
|
@changed_by_display = nil
|
126
|
+
@cache = nil
|
122
127
|
end
|
123
128
|
|
124
129
|
# Specify the storage engine to use for persisting settings. The value can either be specified
|
@@ -145,7 +145,7 @@ module SuperSettings
|
|
145
145
|
end
|
146
146
|
|
147
147
|
payload[:histories] = histories.collect do |history|
|
148
|
-
history_values = {value: history.value, changed_by: history.changed_by_display, created_at: history.created_at}
|
148
|
+
history_values = {value: history.value, changed_by: history.changed_by_display, created_at: history.created_at.utc.iso8601(6)}
|
149
149
|
history_values[:deleted] = true if history.deleted?
|
150
150
|
history_values
|
151
151
|
end
|
@@ -163,7 +163,7 @@ module SuperSettings
|
|
163
163
|
# last_updated_at: iso8601 string
|
164
164
|
# }
|
165
165
|
def last_updated_at
|
166
|
-
{last_updated_at: Setting.last_updated_at.utc.iso8601}
|
166
|
+
{last_updated_at: Setting.last_updated_at.utc.iso8601(6)}
|
167
167
|
end
|
168
168
|
|
169
169
|
# Return settings that have been updated since a specified timestamp.
|
@@ -446,18 +446,17 @@ module SuperSettings
|
|
446
446
|
#
|
447
447
|
# @return [void]
|
448
448
|
def save!
|
449
|
-
record_value_change
|
450
|
-
|
451
449
|
unless valid?
|
452
450
|
raise InvalidRecordError.new(errors.values.join("; "))
|
453
451
|
end
|
454
452
|
|
455
453
|
timestamp = Time.now
|
456
454
|
self.created_at ||= timestamp
|
457
|
-
self.updated_at = timestamp
|
455
|
+
self.updated_at = timestamp if updated_at.nil? || !changed?(:updated_at)
|
458
456
|
|
459
457
|
self.class.storage.with_connection do
|
460
458
|
self.class.storage.transaction do
|
459
|
+
record_value_change
|
461
460
|
@record.save!
|
462
461
|
end
|
463
462
|
|
@@ -525,8 +524,8 @@ module SuperSettings
|
|
525
524
|
value: value,
|
526
525
|
value_type: value_type,
|
527
526
|
description: description,
|
528
|
-
created_at: created_at,
|
529
|
-
updated_at: updated_at
|
527
|
+
created_at: created_at&.utc&.iso8601(6),
|
528
|
+
updated_at: updated_at&.utc&.iso8601(6)
|
530
529
|
}
|
531
530
|
attributes[:deleted] = true if deleted?
|
532
531
|
attributes
|
@@ -592,8 +591,16 @@ module SuperSettings
|
|
592
591
|
# Update the histories association whenever the value or key is changed.
|
593
592
|
def record_value_change
|
594
593
|
return unless changed?(:raw_value) || changed?(:deleted) || changed?(:key)
|
594
|
+
|
595
595
|
recorded_value = (deleted? ? nil : raw_value)
|
596
|
-
@record.create_history(value: recorded_value, deleted: deleted?, changed_by: changed_by, created_at:
|
596
|
+
@record.class.create_history(key: key, value: recorded_value, deleted: deleted?, changed_by: changed_by, created_at: updated_at)
|
597
|
+
|
598
|
+
if changed?(:key)
|
599
|
+
key_was = @changes["key"][0]
|
600
|
+
if key_was
|
601
|
+
@record.class.create_history(key: key_was, changed_by: changed_by, created_at: updated_at, deleted: true)
|
602
|
+
end
|
603
|
+
end
|
597
604
|
end
|
598
605
|
|
599
606
|
def clear_changes
|
@@ -12,6 +12,13 @@ module SuperSettings
|
|
12
12
|
self.table_name = "super_settings"
|
13
13
|
|
14
14
|
has_many :history_items, class_name: "SuperSettings::Storage::ActiveRecordStorage::HistoryModel", foreign_key: :key, primary_key: :key
|
15
|
+
|
16
|
+
class << self
|
17
|
+
# ActiveRecord storage is only available if the connection pool is connected and the table exists.
|
18
|
+
def available?
|
19
|
+
connection_pool&.connected? && table_exists?
|
20
|
+
end
|
21
|
+
end
|
15
22
|
end
|
16
23
|
|
17
24
|
class HistoryModel < ApplicationRecord
|
@@ -18,7 +18,7 @@ module SuperSettings
|
|
18
18
|
|
19
19
|
class << self
|
20
20
|
def all
|
21
|
-
if Model.
|
21
|
+
if Model.available?
|
22
22
|
Model.all.collect { |model| new(model) }
|
23
23
|
else
|
24
24
|
[]
|
@@ -26,7 +26,7 @@ module SuperSettings
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def active
|
29
|
-
if Model.
|
29
|
+
if Model.available?
|
30
30
|
Model.where(deleted: false).collect { |model| new(model) }
|
31
31
|
else
|
32
32
|
[]
|
@@ -34,7 +34,7 @@ module SuperSettings
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def updated_since(time)
|
37
|
-
if Model.
|
37
|
+
if Model.available?
|
38
38
|
Model.where("updated_at > ?", time).collect { |model| new(model) }
|
39
39
|
else
|
40
40
|
[]
|
@@ -42,16 +42,20 @@ module SuperSettings
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def find_by_key(key)
|
45
|
-
model = Model.where(deleted: false).find_by(key: key) if Model.
|
45
|
+
model = Model.where(deleted: false).find_by(key: key) if Model.available?
|
46
46
|
new(model) if model
|
47
47
|
end
|
48
48
|
|
49
49
|
def last_updated_at
|
50
|
-
if Model.
|
50
|
+
if Model.available?
|
51
51
|
Model.maximum(:updated_at)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
def create_history(key:, changed_by:, created_at:, value: nil, deleted: false)
|
56
|
+
HistoryModel.create!(key: key, value: value, deleted: deleted, changed_by: changed_by, created_at: created_at)
|
57
|
+
end
|
58
|
+
|
55
59
|
def with_connection(&block)
|
56
60
|
Model.connection_pool.with_connection(&block)
|
57
61
|
end
|
@@ -117,15 +121,6 @@ module SuperSettings
|
|
117
121
|
HistoryItem.new(key: key, value: record.value, changed_by: record.changed_by, created_at: record.created_at, deleted: record.deleted?)
|
118
122
|
end
|
119
123
|
end
|
120
|
-
|
121
|
-
def create_history(changed_by:, created_at:, value: nil, deleted: false)
|
122
|
-
history_attributes = {value: value, deleted: deleted, changed_by: changed_by, created_at: created_at}
|
123
|
-
if @model.persisted?
|
124
|
-
@model.history_items.create!(history_attributes)
|
125
|
-
else
|
126
|
-
@model.history_items.build(history_attributes)
|
127
|
-
end
|
128
|
-
end
|
129
124
|
end
|
130
125
|
end
|
131
126
|
end
|
@@ -71,6 +71,10 @@ module SuperSettings
|
|
71
71
|
SuperSettings::Coerce.time(value)
|
72
72
|
end
|
73
73
|
|
74
|
+
def create_history(key:, changed_by:, created_at:, value: nil, deleted: false)
|
75
|
+
# No-op since history is maintained by the source system.
|
76
|
+
end
|
77
|
+
|
74
78
|
def save_all(changes)
|
75
79
|
payload = []
|
76
80
|
changes.each do |setting|
|
@@ -132,10 +136,6 @@ module SuperSettings
|
|
132
136
|
end
|
133
137
|
end
|
134
138
|
|
135
|
-
def create_history(changed_by:, created_at:, value: nil, deleted: false)
|
136
|
-
# No-op since history is maintained by the source system.
|
137
|
-
end
|
138
|
-
|
139
139
|
def reload
|
140
140
|
self.class.find_by_key(key)
|
141
141
|
self.attributes = self.class.find_by_key(key).attributes
|
@@ -19,9 +19,32 @@ module SuperSettings
|
|
19
19
|
include Transaction
|
20
20
|
|
21
21
|
class HistoryStorage < HistoryAttributes
|
22
|
+
class << self
|
23
|
+
def create!(attributes)
|
24
|
+
record = new(attributes)
|
25
|
+
record.save!
|
26
|
+
record
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(*)
|
31
|
+
@storage = nil
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_writer :storage
|
36
|
+
|
22
37
|
def created_at=(val)
|
23
38
|
super(TimePrecision.new(val).time)
|
24
39
|
end
|
40
|
+
|
41
|
+
def save!
|
42
|
+
raise ArgumentError.new("Missing key") if Coerce.blank?(key)
|
43
|
+
|
44
|
+
@storage.transaction do |changes|
|
45
|
+
changes << self
|
46
|
+
end
|
47
|
+
end
|
25
48
|
end
|
26
49
|
|
27
50
|
class << self
|
@@ -37,36 +60,54 @@ module SuperSettings
|
|
37
60
|
active.detect { |setting| setting.key == key }
|
38
61
|
end
|
39
62
|
|
63
|
+
def create_history(key:, changed_by:, created_at:, value: nil, deleted: false)
|
64
|
+
HistoryStorage.create!(key: key, value: value, changed_by: changed_by, created_at: created_at, deleted: deleted, storage: self)
|
65
|
+
end
|
66
|
+
|
40
67
|
def save_all(changes)
|
41
68
|
existing = {}
|
42
69
|
parse_settings(settings_json_payload).each do |setting|
|
43
70
|
existing[setting.key] = setting
|
44
71
|
end
|
45
72
|
|
46
|
-
|
47
|
-
|
73
|
+
history_items = []
|
74
|
+
changes.each do |record|
|
75
|
+
if record.is_a?(HistoryStorage)
|
76
|
+
history_items << record
|
77
|
+
else
|
78
|
+
existing[record.key] = record
|
79
|
+
end
|
48
80
|
end
|
49
81
|
|
50
82
|
settings = existing.values.sort_by(&:key)
|
83
|
+
|
51
84
|
changed_histories = {}
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
}
|
85
|
+
history_items.each do |history_item|
|
86
|
+
setting = existing[history_item.key]
|
87
|
+
next unless setting
|
88
|
+
|
89
|
+
history = changed_histories[history_item.key]
|
90
|
+
unless history
|
91
|
+
history = setting.history.dup
|
92
|
+
changed_histories[history_item.key] = history
|
61
93
|
end
|
62
|
-
|
94
|
+
history.unshift(history_item)
|
63
95
|
end
|
64
96
|
|
65
97
|
settings_json = JSON.dump(settings.collect(&:as_json))
|
66
98
|
save_settings_json(settings_json)
|
67
99
|
|
68
100
|
changed_histories.each do |setting_key, setting_history|
|
69
|
-
|
101
|
+
ordered_history = setting_history.sort_by { |history_item| history_item.created_at }.reverse
|
102
|
+
payload = ordered_history.collect do |history_item|
|
103
|
+
{
|
104
|
+
value: history_item.value,
|
105
|
+
changed_by: history_item.changed_by,
|
106
|
+
created_at: history_item.created_at.iso8601(6),
|
107
|
+
deleted: history_item.deleted?
|
108
|
+
}
|
109
|
+
end
|
110
|
+
history_json = JSON.dump(payload)
|
70
111
|
save_history_json(setting_key, history_json)
|
71
112
|
end
|
72
113
|
end
|
@@ -128,11 +169,6 @@ module SuperSettings
|
|
128
169
|
end
|
129
170
|
end
|
130
171
|
|
131
|
-
def initialize(*)
|
132
|
-
@new_history = []
|
133
|
-
super
|
134
|
-
end
|
135
|
-
|
136
172
|
def created_at=(val)
|
137
173
|
super(TimePrecision.new(val).time)
|
138
174
|
end
|
@@ -149,14 +185,6 @@ module SuperSettings
|
|
149
185
|
end
|
150
186
|
end
|
151
187
|
|
152
|
-
def create_history(changed_by:, created_at:, value: nil, deleted: false)
|
153
|
-
history = HistoryStorage.new(key: key, value: value, changed_by: changed_by, created_at: created_at, deleted: deleted)
|
154
|
-
@new_history.unshift(history)
|
155
|
-
history
|
156
|
-
end
|
157
|
-
|
158
|
-
attr_reader :new_history
|
159
|
-
|
160
188
|
def as_json
|
161
189
|
{
|
162
190
|
key: key,
|
@@ -26,6 +26,26 @@ module SuperSettings
|
|
26
26
|
@mutex = Mutex.new
|
27
27
|
|
28
28
|
class HistoryStorage < HistoryAttributes
|
29
|
+
class << self
|
30
|
+
def create!(attributes)
|
31
|
+
record = new(attributes)
|
32
|
+
record.save!
|
33
|
+
record
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def created_at=(value)
|
38
|
+
super(TimePrecision.new(value, :millisecond).time)
|
39
|
+
end
|
40
|
+
|
41
|
+
def save!
|
42
|
+
raise ArgumentError.new("Missing key") if Coerce.blank?(key)
|
43
|
+
|
44
|
+
MongoDBStorage.transaction do |changes|
|
45
|
+
changes << self
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
29
49
|
def as_bson
|
30
50
|
attributes = {
|
31
51
|
value: value,
|
@@ -89,13 +109,16 @@ module SuperSettings
|
|
89
109
|
last_updated_setting["updated_at"] if last_updated_setting
|
90
110
|
end
|
91
111
|
|
112
|
+
def create_history(key:, changed_by:, created_at:, value: nil, deleted: false)
|
113
|
+
HistoryStorage.create!(key: key, value: value, changed_by: changed_by, created_at: created_at, deleted: deleted)
|
114
|
+
end
|
115
|
+
|
92
116
|
def destroy_all
|
93
117
|
settings_collection.delete_many({})
|
94
118
|
end
|
95
119
|
|
96
120
|
def save_all(changes)
|
97
121
|
upserts = changes.collect { |setting| upsert(setting) }
|
98
|
-
changes.each { |setting| setting.new_history.clear }
|
99
122
|
settings_collection.bulk_write(upserts)
|
100
123
|
true
|
101
124
|
end
|
@@ -108,17 +131,18 @@ module SuperSettings
|
|
108
131
|
|
109
132
|
private
|
110
133
|
|
111
|
-
def upsert(
|
112
|
-
|
113
|
-
|
134
|
+
def upsert(record)
|
135
|
+
update = {"$setOnInsert": {key: record.key}}
|
136
|
+
if record.is_a?(MongoDBStorage::HistoryStorage)
|
137
|
+
update["$push"] = {history: record.as_bson}
|
138
|
+
else
|
139
|
+
update["$set"] = record.as_bson.except(:key)
|
140
|
+
end
|
141
|
+
|
114
142
|
{
|
115
143
|
update_one: {
|
116
|
-
filter: {key:
|
117
|
-
update:
|
118
|
-
"$set": doc.except(:key, :history),
|
119
|
-
"$setOnInsert": {key: setting.key},
|
120
|
-
"$push": {history: {"$each": history}}
|
121
|
-
},
|
144
|
+
filter: {key: record.key},
|
145
|
+
update: update,
|
122
146
|
upsert: true
|
123
147
|
}
|
124
148
|
}
|
@@ -146,13 +170,6 @@ module SuperSettings
|
|
146
170
|
end
|
147
171
|
end
|
148
172
|
|
149
|
-
attr_reader :new_history
|
150
|
-
|
151
|
-
def initialize(*)
|
152
|
-
@new_history = []
|
153
|
-
super
|
154
|
-
end
|
155
|
-
|
156
173
|
def history(limit: nil, offset: 0)
|
157
174
|
pipeline = [
|
158
175
|
{
|
@@ -195,13 +212,6 @@ module SuperSettings
|
|
195
212
|
end
|
196
213
|
end
|
197
214
|
|
198
|
-
def create_history(changed_by:, created_at:, value: nil, deleted: false)
|
199
|
-
created_at = TimePrecision.new(created_at, :millisecond).time
|
200
|
-
history = HistoryStorage.new(key: key, value: value, changed_by: changed_by, created_at: created_at, deleted: deleted)
|
201
|
-
@new_history.unshift(history)
|
202
|
-
history
|
203
|
-
end
|
204
|
-
|
205
215
|
def created_at=(val)
|
206
216
|
super(TimePrecision.new(val, :millisecond).time)
|
207
217
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SuperSettings
|
4
|
+
module Storage
|
5
|
+
# No-op implementation of the SuperSettings::Storage model. You can use this model if there
|
6
|
+
# is no other storage engine available. It can be useful for situations where the normal
|
7
|
+
# storage engine is not available, such as in a continious integration environment.
|
8
|
+
class NullStorage
|
9
|
+
# :nocov:
|
10
|
+
|
11
|
+
include Storage
|
12
|
+
|
13
|
+
attr_accessor :key, :raw_value, :description, :value_type, :updated_at, :created_at, :changed_by
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_reader :settings
|
17
|
+
|
18
|
+
def history(key)
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
|
22
|
+
def destroy_all
|
23
|
+
end
|
24
|
+
|
25
|
+
def all
|
26
|
+
[]
|
27
|
+
end
|
28
|
+
|
29
|
+
def updated_since(time)
|
30
|
+
[]
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_by_key(key)
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def last_updated_at
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_history(key:, changed_by:, created_at:, value: nil, deleted: false)
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_asynchronous?
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def history(limit: nil, offset: 0)
|
51
|
+
[]
|
52
|
+
end
|
53
|
+
|
54
|
+
def save!
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def deleted=(value)
|
59
|
+
end
|
60
|
+
|
61
|
+
def deleted?
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def persisted?
|
66
|
+
false
|
67
|
+
end
|
68
|
+
|
69
|
+
# :nocov:
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -116,6 +116,10 @@ module SuperSettings
|
|
116
116
|
record unless record.deleted?
|
117
117
|
end
|
118
118
|
|
119
|
+
def create_history(key:, changed_by:, created_at:, value: nil, deleted: false)
|
120
|
+
HistoryStorage.create!(key: key, value: value, deleted: deleted, changed_by: changed_by, created_at: created_at)
|
121
|
+
end
|
122
|
+
|
119
123
|
def last_updated_at
|
120
124
|
result = with_redis { |redis| redis.zrevrange(UPDATED_KEY, 0, 1, withscores: true).first }
|
121
125
|
return nil unless result
|
@@ -184,10 +188,6 @@ module SuperSettings
|
|
184
188
|
end
|
185
189
|
end
|
186
190
|
|
187
|
-
def create_history(changed_by:, created_at:, value: nil, deleted: false)
|
188
|
-
HistoryStorage.create!(key: key, value: value, deleted: deleted, changed_by: changed_by, created_at: created_at)
|
189
|
-
end
|
190
|
-
|
191
191
|
def save_to_redis(redis)
|
192
192
|
redis.hset(SETTINGS_KEY, key, payload_json)
|
193
193
|
redis.zadd(UPDATED_KEY, self.class.microseconds(updated_at), key)
|
@@ -59,6 +59,20 @@ module SuperSettings
|
|
59
59
|
settings.values.collect { |attributes| attributes[:updated_at] }.max
|
60
60
|
end
|
61
61
|
|
62
|
+
def create_history(key:, changed_by:, created_at:, value: nil, deleted: false)
|
63
|
+
history = @history[key]
|
64
|
+
unless history
|
65
|
+
history = []
|
66
|
+
@history[key] = history
|
67
|
+
end
|
68
|
+
|
69
|
+
created_at = SuperSettings::TimePrecision.new(created_at).time if created_at
|
70
|
+
item = {key: key, value: value, deleted: deleted, changed_by: changed_by, created_at: created_at}
|
71
|
+
history.unshift(item)
|
72
|
+
|
73
|
+
item
|
74
|
+
end
|
75
|
+
|
62
76
|
protected
|
63
77
|
|
64
78
|
def default_load_asynchronous?
|
@@ -85,11 +99,6 @@ module SuperSettings
|
|
85
99
|
end
|
86
100
|
end
|
87
101
|
|
88
|
-
def create_history(changed_by:, created_at:, value: nil, deleted: false)
|
89
|
-
item = {key: key, value: value, deleted: deleted, changed_by: changed_by, created_at: created_at}
|
90
|
-
self.class.history(key).unshift(item)
|
91
|
-
end
|
92
|
-
|
93
102
|
def save!
|
94
103
|
self.updated_at ||= Time.now
|
95
104
|
self.created_at ||= updated_at
|
@@ -123,11 +132,15 @@ module SuperSettings
|
|
123
132
|
end
|
124
133
|
|
125
134
|
def created_at=(value)
|
126
|
-
|
135
|
+
time = SuperSettings::Coerce.time(value)
|
136
|
+
time = SuperSettings::TimePrecision.new(time).time if time
|
137
|
+
@created_at = time
|
127
138
|
end
|
128
139
|
|
129
140
|
def updated_at=(value)
|
130
|
-
|
141
|
+
time = SuperSettings::Coerce.time(value)
|
142
|
+
time = SuperSettings::TimePrecision.new(time).time if time
|
143
|
+
@updated_at = time
|
131
144
|
end
|
132
145
|
|
133
146
|
def deleted?
|
@@ -53,8 +53,9 @@ module SuperSettings
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def save!
|
56
|
-
|
57
|
-
self.
|
56
|
+
timestamp = Time.now
|
57
|
+
self.updated_at ||= timestamp if respond_to?(:updated_at)
|
58
|
+
self.created_at ||= (respond_to?(:updated_at) ? updated_at : timestamp)
|
58
59
|
|
59
60
|
self.class.transaction do |changes|
|
60
61
|
changes << self
|
@@ -13,6 +13,7 @@ module SuperSettings
|
|
13
13
|
autoload :JSONStorage, File.join(__dir__, "storage/json_storage")
|
14
14
|
autoload :S3Storage, File.join(__dir__, "storage/s3_storage")
|
15
15
|
autoload :MongoDBStorage, File.join(__dir__, "storage/mongodb_storage")
|
16
|
+
autoload :NullStorage, File.join(__dir__, "storage/null_storage")
|
16
17
|
|
17
18
|
def self.included(base)
|
18
19
|
base.extend(ClassMethods)
|
@@ -67,6 +68,15 @@ module SuperSettings
|
|
67
68
|
# :nocov:
|
68
69
|
end
|
69
70
|
|
71
|
+
# Create a history item for the setting.
|
72
|
+
#
|
73
|
+
# @return [void]
|
74
|
+
def create_history(key:, changed_by:, created_at:, value: nil, deleted: false)
|
75
|
+
# :nocov:
|
76
|
+
raise NotImplementedError
|
77
|
+
# :nocov:
|
78
|
+
end
|
79
|
+
|
70
80
|
# Implementing classes can override this method to setup a thread safe connection within a block.
|
71
81
|
#
|
72
82
|
# @return [void]
|
@@ -253,9 +263,7 @@ module SuperSettings
|
|
253
263
|
#
|
254
264
|
# @return [void]
|
255
265
|
def create_history(changed_by:, created_at:, value: nil, deleted: false)
|
256
|
-
|
257
|
-
raise NotImplementedError
|
258
|
-
# :nocov:
|
266
|
+
self.class.create_history(key: key, changed_by: changed_by, created_at: created_at, value: value, deleted: deleted)
|
259
267
|
end
|
260
268
|
|
261
269
|
# Persist the record to storage.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: super_settings
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Durand
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -79,6 +79,7 @@ files:
|
|
79
79
|
- lib/super_settings/storage/http_storage.rb
|
80
80
|
- lib/super_settings/storage/json_storage.rb
|
81
81
|
- lib/super_settings/storage/mongodb_storage.rb
|
82
|
+
- lib/super_settings/storage/null_storage.rb
|
82
83
|
- lib/super_settings/storage/redis_storage.rb
|
83
84
|
- lib/super_settings/storage/s3_storage.rb
|
84
85
|
- lib/super_settings/storage/storage_attributes.rb
|