super_settings 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -2
  3. data/README.md +121 -16
  4. data/VERSION +1 -1
  5. data/app/helpers/super_settings/settings_helper.rb +13 -3
  6. data/app/views/layouts/super_settings/settings.html.erb +1 -1
  7. data/config/routes.rb +1 -1
  8. data/db/migrate/20210414004553_create_super_settings.rb +1 -7
  9. data/lib/super_settings/application/api.js +4 -1
  10. data/lib/super_settings/application/helper.rb +56 -17
  11. data/lib/super_settings/application/images/arrow-down-short.svg +3 -0
  12. data/lib/super_settings/application/images/arrow-up-short.svg +3 -0
  13. data/lib/super_settings/application/images/info-circle.svg +4 -0
  14. data/lib/super_settings/application/images/pencil-square.svg +4 -0
  15. data/lib/super_settings/application/images/plus.svg +3 -1
  16. data/lib/super_settings/application/images/trash3.svg +3 -0
  17. data/lib/super_settings/application/images/x-circle.svg +4 -0
  18. data/lib/super_settings/application/index.html.erb +54 -37
  19. data/lib/super_settings/application/layout.html.erb +5 -2
  20. data/lib/super_settings/application/layout_styles.css +7 -151
  21. data/lib/super_settings/application/layout_vars.css.erb +21 -0
  22. data/lib/super_settings/application/scripts.js +100 -21
  23. data/lib/super_settings/application/style_vars.css.erb +62 -0
  24. data/lib/super_settings/application/styles.css +183 -14
  25. data/lib/super_settings/application.rb +18 -11
  26. data/lib/super_settings/attributes.rb +1 -8
  27. data/lib/super_settings/configuration.rb +9 -0
  28. data/lib/super_settings/context/current.rb +33 -0
  29. data/lib/super_settings/context.rb +3 -0
  30. data/lib/super_settings/controller_actions.rb +2 -2
  31. data/lib/super_settings/engine.rb +1 -3
  32. data/lib/super_settings/history_item.rb +1 -1
  33. data/lib/super_settings/http_client.rb +165 -0
  34. data/lib/super_settings/local_cache.rb +0 -15
  35. data/lib/super_settings/rack_application.rb +3 -3
  36. data/lib/super_settings/rest_api.rb +5 -4
  37. data/lib/super_settings/setting.rb +14 -3
  38. data/lib/super_settings/storage/active_record_storage/models.rb +28 -0
  39. data/lib/super_settings/storage/active_record_storage.rb +10 -20
  40. data/lib/super_settings/storage/history_attributes.rb +31 -0
  41. data/lib/super_settings/storage/http_storage.rb +60 -184
  42. data/lib/super_settings/storage/json_storage.rb +201 -0
  43. data/lib/super_settings/storage/mongodb_storage.rb +238 -0
  44. data/lib/super_settings/storage/redis_storage.rb +50 -111
  45. data/lib/super_settings/storage/s3_storage.rb +165 -0
  46. data/lib/super_settings/storage/storage_attributes.rb +64 -0
  47. data/lib/super_settings/storage/test_storage.rb +3 -5
  48. data/lib/super_settings/storage/transaction.rb +67 -0
  49. data/lib/super_settings/storage.rb +17 -8
  50. data/lib/super_settings/time_precision.rb +36 -0
  51. data/lib/super_settings.rb +48 -13
  52. data/super_settings.gemspec +11 -2
  53. metadata +30 -12
  54. data/lib/super_settings/application/images/edit.svg +0 -1
  55. data/lib/super_settings/application/images/info.svg +0 -1
  56. data/lib/super_settings/application/images/slash.svg +0 -1
  57. data/lib/super_settings/application/images/trash.svg +0 -1
  58. /data/{MIT-LICENSE → MIT-LICENSE.txt} +0 -0
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SuperSettings
4
+ module Storage
5
+ # This is an abstract storage class that provides support for storing settings in a JSON file.
6
+ # The settings are stored in JSON as an array of hashes with each hash representing a setting.
7
+ #
8
+ # Setting history should be stored in separate JSON files per key and are loaded separately
9
+ # from the main settings file.
10
+ #
11
+ # This class can be used as the base for any storage class where the settings are all stored
12
+ # together in a single JSON payload.
13
+ #
14
+ # Subclasses must implement the following methods:
15
+ # - self.all
16
+ # - self.last_updated_at
17
+ # - save!
18
+ class JSONStorage < StorageAttributes
19
+ include Transaction
20
+
21
+ class HistoryStorage < HistoryAttributes
22
+ def created_at=(val)
23
+ super(TimePrecision.new(val).time)
24
+ end
25
+ end
26
+
27
+ class << self
28
+ def all
29
+ parse_settings(settings_json_payload)
30
+ end
31
+
32
+ def updated_since(timestamp)
33
+ all.select { |setting| setting.updated_at > timestamp }
34
+ end
35
+
36
+ def find_by_key(key)
37
+ active.detect { |setting| setting.key == key }
38
+ end
39
+
40
+ def save_all(changes)
41
+ existing = {}
42
+ parse_settings(settings_json_payload).each do |setting|
43
+ existing[setting.key] = setting
44
+ end
45
+
46
+ changes.each do |setting|
47
+ existing[setting.key] = setting
48
+ end
49
+
50
+ settings = existing.values.sort_by(&:key)
51
+ changed_histories = {}
52
+ changes.collect do |setting|
53
+ history = (setting.new_history + setting.history).sort_by(&:created_at).reverse
54
+ changed_histories[setting.key] = history.collect do |history_item|
55
+ {
56
+ value: history_item.value,
57
+ changed_by: history_item.changed_by,
58
+ created_at: history_item.created_at&.iso8601(6),
59
+ deleted: history_item.deleted?
60
+ }
61
+ end
62
+ setting.new_history.clear
63
+ end
64
+
65
+ settings_json = JSON.dump(settings.collect(&:as_json))
66
+ save_settings_json(settings_json)
67
+
68
+ changed_histories.each do |setting_key, setting_history|
69
+ history_json = JSON.dump(setting_history)
70
+ save_history_json(setting_key, history_json)
71
+ end
72
+ end
73
+
74
+ # Heper method to load settings from a JSON string.
75
+ #
76
+ # @param json [String] JSON string to parse.
77
+ # @return [Array<SuperSettings::Storage::JSONStorage>] Array of settings.
78
+ def parse_settings(json)
79
+ return [] if Coerce.blank?(json)
80
+
81
+ JSON.parse(json).collect do |attributes|
82
+ setting = new(
83
+ key: attributes["key"],
84
+ raw_value: attributes["value"],
85
+ description: attributes["description"],
86
+ value_type: attributes["value_type"],
87
+ updated_at: Time.parse(attributes["updated_at"]),
88
+ created_at: Time.parse(attributes["created_at"]),
89
+ deleted: attributes["deleted"]
90
+ )
91
+ setting.persisted = true
92
+ setting
93
+ end
94
+ end
95
+
96
+ protected
97
+
98
+ # Subclasses must implement this method to return the JSON payload containing all of the
99
+ # settings as a string.
100
+ #
101
+ # @return [String] JSON string.
102
+ def settings_json_payload
103
+ # :nocov:
104
+ raise NotImplementedError
105
+ # :nocov:
106
+ end
107
+
108
+ # Subclasses must implement this method to persist the JSON payload containing all of the
109
+ # settings.
110
+ #
111
+ # @param json [String] JSON string to save.
112
+ def save_settings_json(json)
113
+ # :nocov:
114
+ raise NotImplementedError
115
+ # :nocov:
116
+ end
117
+
118
+ # Subclasses must implement this method to persist the JSON payload containing the history
119
+ # records for a setting key.
120
+ #
121
+ # @param key [String] Setting key.
122
+ # @param json [String] JSON string to save.
123
+ # @return [void]
124
+ def save_history_json(key, json)
125
+ # :nocov:
126
+ raise NotImplementedError
127
+ # :nocov:
128
+ end
129
+ end
130
+
131
+ def initialize(*)
132
+ @new_history = []
133
+ super
134
+ end
135
+
136
+ def created_at=(val)
137
+ super(TimePrecision.new(val).time)
138
+ end
139
+
140
+ def updated_at=(val)
141
+ super(TimePrecision.new(val).time)
142
+ end
143
+
144
+ def history(limit: nil, offset: 0)
145
+ history = fetch_history
146
+ limit ||= history.length
147
+ history[offset, limit].collect do |record|
148
+ HistoryItem.new(key: key, value: record.value, changed_by: record.changed_by, created_at: record.created_at, deleted: record.deleted?)
149
+ end
150
+ end
151
+
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
+ def as_json
161
+ {
162
+ key: key,
163
+ value: raw_value,
164
+ value_type: value_type,
165
+ description: description,
166
+ created_at: created_at&.iso8601(6),
167
+ updated_at: updated_at&.iso8601(6),
168
+ deleted: deleted?
169
+ }
170
+ end
171
+
172
+ protected
173
+
174
+ # Subclasses must implement this method to return the JSON payload containing all of the
175
+ # history records for the setting key. The payload must be an array that contains hashes
176
+ # with the keys "value", "changed_by", "deleted", and "created_at".
177
+ def fetch_history_json
178
+ raise NotImplementedError
179
+ end
180
+
181
+ private
182
+
183
+ def fetch_history
184
+ json = fetch_history_json
185
+ history_payload = Coerce.blank?(json) ? [] : JSON.parse(json)
186
+
187
+ history_items = history_payload.collect do |attributes|
188
+ HistoryStorage.new(
189
+ key: key,
190
+ value: attributes["value"],
191
+ changed_by: attributes["changed_by"],
192
+ created_at: Time.parse(attributes["created_at"]),
193
+ deleted: attributes["deleted"]
194
+ )
195
+ end
196
+
197
+ history_items.sort_by(&:created_at).reverse
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mongo"
4
+
5
+ module SuperSettings
6
+ module Storage
7
+ # MongoDB implementation of the SuperSettings::Storage model.
8
+ #
9
+ # You must define the connection URL to use by setting the `url` or `mongodb` attribute on the class.
10
+ #
11
+ # @example
12
+ # SuperSettings::Storage::MongoDBStorage.url = "mongodb://user:password@localhost:27017/super_settings"
13
+ #
14
+ # @example
15
+ # SuperSettings::Storage::MongoDBStorage.mongodb = Mongo::Client.new("mongodb://user:password@localhost:27017/super_settings")
16
+ class MongoDBStorage < StorageAttributes
17
+ include Storage
18
+ include Transaction
19
+
20
+ DEFAULT_COLLECTION_NAME = "super_settings"
21
+
22
+ @mongodb = nil
23
+ @url = nil
24
+ @url_hash = @url.hash
25
+ @collection_name = DEFAULT_COLLECTION_NAME
26
+ @mutex = Mutex.new
27
+
28
+ class HistoryStorage < HistoryAttributes
29
+ def as_bson
30
+ attributes = {
31
+ value: value,
32
+ changed_by: changed_by,
33
+ created_at: created_at
34
+ }
35
+ attributes[:deleted] = true if deleted?
36
+ attributes
37
+ end
38
+ end
39
+
40
+ class << self
41
+ attr_writer :url, :mongodb
42
+ attr_accessor :collection_name
43
+
44
+ def mongodb
45
+ if @mongodb.nil? || @url_hash != @url.hash
46
+ @mutex.synchronize do
47
+ unless @url_hash == @url.hash
48
+ @url_hash = @url.hash
49
+ @mongodb = Mongo::Client.new(@url)
50
+ create_indexes!(@mongodb)
51
+ end
52
+ end
53
+ end
54
+ @mongodb
55
+ end
56
+
57
+ def settings_collection
58
+ mongodb[collection_name]
59
+ end
60
+
61
+ def updated_since(time)
62
+ time = TimePrecision.new(time, :millisecond).time
63
+ settings_collection.find(updated_at: {"$gt": time}).projection(history: 0).sort({updated_at: -1}).collect do |attributes|
64
+ record = new(attributes)
65
+ record.persisted = true
66
+ record
67
+ end
68
+ end
69
+
70
+ def all
71
+ settings_collection.find.projection(history: 0).collect do |attributes|
72
+ record = new(attributes)
73
+ record.persisted = true
74
+ record
75
+ end
76
+ end
77
+
78
+ def find_by_key(key)
79
+ query = {
80
+ key: key,
81
+ deleted: false
82
+ }
83
+ record = settings_collection.find(query).projection(history: 0).first
84
+ new(record) if record
85
+ end
86
+
87
+ def last_updated_at
88
+ last_updated_setting = settings_collection.find.projection(updated_at: 1).sort(updated_at: -1).limit(1).first
89
+ last_updated_setting["updated_at"] if last_updated_setting
90
+ end
91
+
92
+ def destroy_all
93
+ settings_collection.delete_many({})
94
+ end
95
+
96
+ def save_all(changes)
97
+ upserts = changes.collect { |setting| upsert(setting) }
98
+ changes.each { |setting| setting.new_history.clear }
99
+ settings_collection.bulk_write(upserts)
100
+ true
101
+ end
102
+
103
+ protected
104
+
105
+ def default_load_asynchronous?
106
+ true
107
+ end
108
+
109
+ private
110
+
111
+ def upsert(setting)
112
+ doc = setting.as_bson
113
+ history = setting.new_history.collect(&:as_bson)
114
+ {
115
+ update_one: {
116
+ filter: {key: setting.key},
117
+ update: {
118
+ "$set": doc.except(:key, :history),
119
+ "$setOnInsert": {key: setting.key},
120
+ "$push": {history: {"$each": history}}
121
+ },
122
+ upsert: true
123
+ }
124
+ }
125
+ end
126
+
127
+ def create_indexes!(client)
128
+ collection = client[collection_name]
129
+ collection_exists = client.database.collection_names.include?(collection.name)
130
+ existing_indexes = (collection_exists ? collection.indexes.to_a : [])
131
+
132
+ unique_key_index = {key: 1}
133
+ unless existing_indexes.any? { |index| index["key"] == unique_key_index }
134
+ collection.indexes.create_one(unique_key_index, unique: true)
135
+ end
136
+
137
+ updated_at_index = {updated_at: -1}
138
+ unless existing_indexes.any? { |index| index["key"] == updated_at_index }
139
+ collection.indexes.create_one(updated_at_index)
140
+ end
141
+
142
+ history_created_at_desc_index = {key: 1, "history.created_at": -1}
143
+ unless existing_indexes.any? { |index| index["key"] == history_created_at_desc_index }
144
+ collection.indexes.create_one(history_created_at_desc_index)
145
+ end
146
+ end
147
+ end
148
+
149
+ attr_reader :new_history
150
+
151
+ def initialize(*)
152
+ @new_history = []
153
+ super
154
+ end
155
+
156
+ def history(limit: nil, offset: 0)
157
+ pipeline = [
158
+ {
159
+ "$match": {key: key}
160
+ },
161
+ {
162
+ "$addFields": {
163
+ history: {
164
+ "$sortArray": {
165
+ input: "$history",
166
+ sortBy: {created_at: -1}
167
+ }
168
+ }
169
+ }
170
+ }
171
+ ]
172
+
173
+ if limit || offset > 0
174
+ pipeline << {
175
+ "$addFields": {
176
+ history: {
177
+ "$slice": ["$history", offset, (limit || {"$size": "$history"})]
178
+ }
179
+ }
180
+ }
181
+ end
182
+
183
+ pipeline << {
184
+ "$project": {
185
+ _id: 0,
186
+ history: 1
187
+ }
188
+ }
189
+
190
+ record = self.class.settings_collection.aggregate(pipeline).to_a.first
191
+ return [] unless record && record["history"].is_a?(Array)
192
+
193
+ record["history"].collect do |record|
194
+ HistoryItem.new(key: key, value: record["value"], changed_by: record["changed_by"], created_at: record["created_at"], deleted: record["deleted"])
195
+ end
196
+ end
197
+
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
+ def created_at=(val)
206
+ super(TimePrecision.new(val, :millisecond).time)
207
+ end
208
+
209
+ def updated_at=(val)
210
+ super(TimePrecision.new(val, :millisecond).time)
211
+ end
212
+
213
+ def destroy
214
+ settings_collection.delete_one(key: key)
215
+ end
216
+
217
+ def as_bson
218
+ {
219
+ key: key,
220
+ raw_value: raw_value,
221
+ value_type: value_type,
222
+ description: description,
223
+ created_at: created_at,
224
+ updated_at: updated_at,
225
+ deleted: deleted?
226
+ }
227
+ end
228
+ end
229
+
230
+ def created_at=(val)
231
+ super(TimePrecision.new(val, :millisecond).time)
232
+ end
233
+
234
+ def updated_at=(val)
235
+ super(TimePrecision.new(val, :millisecond).time)
236
+ end
237
+ end
238
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
3
+ require "redis"
4
4
 
5
5
  module SuperSettings
6
6
  module Storage
@@ -22,20 +22,15 @@ module SuperSettings
22
22
  #
23
23
  # @example
24
24
  # SuperSettings::Storage::RedisStorage.redis = ConnectionPool.new(size: 5) { Redis.new(url: ENV["REDIS_URL"]) }
25
- class RedisStorage
26
- include Storage
25
+ class RedisStorage < StorageAttributes
26
+ include Transaction
27
27
 
28
28
  SETTINGS_KEY = "SuperSettings.settings"
29
29
  UPDATED_KEY = "SuperSettings.order_by_updated_at"
30
30
 
31
- class HistoryStorage
31
+ class HistoryStorage < HistoryAttributes
32
32
  HISTORY_KEY_PREFIX = "SuperSettings.history"
33
33
 
34
- include SuperSettings::Attributes
35
-
36
- attr_accessor :key, :value, :changed_by, :deleted
37
- attr_reader :created_at
38
-
39
34
  class << self
40
35
  def find_all_by_key(key:, offset: 0, limit: nil)
41
36
  end_index = (limit.nil? ? -1 : offset + limit - 1)
@@ -54,10 +49,8 @@ module SuperSettings
54
49
  record
55
50
  end
56
51
 
57
- def destroy_all_by_key(key)
58
- RedisStorage.transaction do |redis|
59
- redis.del("#{HISTORY_KEY_PREFIX}.#{key}")
60
- end
52
+ def destroy_all_by_key(key, redis)
53
+ redis.del("#{HISTORY_KEY_PREFIX}.#{key}")
61
54
  end
62
55
 
63
56
  def redis_key(key)
@@ -65,24 +58,20 @@ module SuperSettings
65
58
  end
66
59
  end
67
60
 
68
- def initialize(*)
69
- @deleted = false
70
- super
71
- end
72
-
73
- def created_at=(val)
74
- @created_at = SuperSettings::Coerce.time(val)
75
- end
76
-
77
61
  def save!
78
62
  raise ArgumentError.new("Missing key") if Coerce.blank?(key)
79
- RedisStorage.transaction do |redis|
80
- redis.lpush(self.class.redis_key(key), payload_json.to_json)
63
+
64
+ RedisStorage.transaction do |changes|
65
+ changes << self
81
66
  end
82
67
  end
83
68
 
84
- def deleted?
85
- !!@deleted
69
+ def save_to_redis(redis)
70
+ redis.lpush(self.class.redis_key(key), payload_json.to_json)
71
+ end
72
+
73
+ def created_at
74
+ SuperSettings::Storage::RedisStorage.time_at_microseconds(super)
86
75
  end
87
76
 
88
77
  private
@@ -91,16 +80,13 @@ module SuperSettings
91
80
  payload = {
92
81
  value: value,
93
82
  changed_by: changed_by,
94
- created_at: created_at.to_f
83
+ created_at: SuperSettings::Storage::RedisStorage.microseconds(created_at)
95
84
  }
96
85
  payload[:deleted] = true if deleted?
97
86
  payload
98
87
  end
99
88
  end
100
89
 
101
- attr_reader :key, :raw_value, :description, :value_type, :updated_at, :created_at
102
- attr_accessor :changed_by
103
-
104
90
  class << self
105
91
  def all
106
92
  with_redis do |redis|
@@ -109,15 +95,15 @@ module SuperSettings
109
95
  end
110
96
 
111
97
  def updated_since(time)
112
- time = SuperSettings::Coerce.time(time)
98
+ min_score = microseconds(time)
113
99
  with_redis do |redis|
114
- min_score = time.to_f
115
100
  keys = redis.zrangebyscore(UPDATED_KEY, min_score, "+inf")
116
101
  return [] if keys.empty?
117
102
 
118
103
  settings = []
119
104
  redis.hmget(SETTINGS_KEY, *keys).each do |json|
120
- settings << load_from_json(json) if json
105
+ setting = load_from_json(json) if json
106
+ settings << setting if setting && setting.updated_at > time
121
107
  end
122
108
  settings
123
109
  end
@@ -133,7 +119,7 @@ module SuperSettings
133
119
  def last_updated_at
134
120
  result = with_redis { |redis| redis.zrevrange(UPDATED_KEY, 0, 1, withscores: true).first }
135
121
  return nil unless result
136
- Time.at(result[1])
122
+ time_at_microseconds(result[1])
137
123
  end
138
124
 
139
125
  def destroy_all
@@ -151,26 +137,23 @@ module SuperSettings
151
137
  end
152
138
  end
153
139
 
154
- def transaction(&block)
155
- if Thread.current[:super_settings_transaction_redis]
156
- block.call(Thread.current[:super_settings_transaction_redis])
157
- else
158
- begin
159
- with_redis do |redis|
160
- redis.multi do |multi_redis|
161
- Thread.current[:super_settings_transaction_redis] = multi_redis
162
- Thread.current[:super_settings_transaction_after_commit] = []
163
- block.call(multi_redis)
164
- end
165
- after_commits = Thread.current[:super_settings_transaction_after_commit]
166
- Thread.current[:super_settings_transaction_after_commit] = nil
167
- after_commits.each(&:call)
140
+ def save_all(changes)
141
+ with_redis do |redis|
142
+ redis.multi do |multi_redis|
143
+ changes.each do |object|
144
+ object.save_to_redis(multi_redis)
168
145
  end
169
- ensure
170
- Thread.current[:super_settings_transaction_redis] = nil
171
- Thread.current[:super_settings_transaction_after_commit] = nil
172
146
  end
173
147
  end
148
+ true
149
+ end
150
+
151
+ def time_at_microseconds(time)
152
+ TimePrecision.new(time, :microsecond).time
153
+ end
154
+
155
+ def microseconds(time)
156
+ TimePrecision.new(time, :microsecond).to_f
174
157
  end
175
158
 
176
159
  protected
@@ -184,7 +167,7 @@ module SuperSettings
184
167
  def load_from_json(json)
185
168
  attributes = JSON.parse(json)
186
169
  setting = new(attributes)
187
- setting.send(:set_persisted!)
170
+ setting.persisted = true
188
171
  setting
189
172
  end
190
173
  end
@@ -205,83 +188,39 @@ module SuperSettings
205
188
  HistoryStorage.create!(key: key, value: value, deleted: deleted, changed_by: changed_by, created_at: created_at)
206
189
  end
207
190
 
208
- def save!
209
- self.updated_at ||= Time.now
210
- self.created_at ||= updated_at
211
- self.class.transaction do |redis|
212
- redis.hset(SETTINGS_KEY, key, payload_json)
213
- redis.zadd(UPDATED_KEY, updated_at.to_f, key)
214
- set_persisted!
215
- end
216
- true
191
+ def save_to_redis(redis)
192
+ redis.hset(SETTINGS_KEY, key, payload_json)
193
+ redis.zadd(UPDATED_KEY, self.class.microseconds(updated_at), key)
217
194
  end
218
195
 
219
196
  def destroy
220
- self.class.transaction do |redis|
221
- redis.hdel(SETTINGS_KEY, key)
222
- redis.zrem(UPDATED_KEY, key)
223
- HistoryStorage.destroy_all_by_key(key)
197
+ self.class.with_redis do |redis|
198
+ redis.multi do |multi_redis|
199
+ multi_redis.hdel(SETTINGS_KEY, key)
200
+ multi_redis.zrem(UPDATED_KEY, key)
201
+ HistoryStorage.destroy_all_by_key(key, multi_redis)
202
+ end
224
203
  end
225
204
  end
226
205
 
227
- def key=(value)
228
- @key = (Coerce.blank?(value) ? nil : value.to_s)
206
+ def created_at
207
+ self.class.time_at_microseconds(super)
229
208
  end
230
209
 
231
- def raw_value=(value)
232
- @raw_value = (Coerce.blank?(value) ? nil : value.to_s)
233
- end
234
-
235
- def value_type=(value)
236
- @value_type = (Coerce.blank?(value) ? nil : value.to_s)
237
- end
238
-
239
- def description=(value)
240
- @description = (Coerce.blank?(value) ? nil : value.to_s)
241
- end
242
-
243
- def deleted=(value)
244
- @deleted = Coerce.boolean(value)
245
- end
246
-
247
- def created_at=(value)
248
- @created_at = SuperSettings::Coerce.time(value)
249
- end
250
-
251
- def updated_at=(value)
252
- @updated_at = SuperSettings::Coerce.time(value)
253
- end
254
-
255
- def deleted?
256
- !!@deleted
257
- end
258
-
259
- def persisted?
260
- !!@persisted
210
+ def updated_at
211
+ self.class.time_at_microseconds(super)
261
212
  end
262
213
 
263
214
  private
264
215
 
265
- def after_commit(&block)
266
- if Thread.current[:super_settings_transaction_after_commit]
267
- Thread.current[:super_settings_transaction_after_commit] << block
268
- else
269
- block.call
270
- end
271
- end
272
-
273
- def set_persisted!
274
- @persisted = true
275
- end
276
-
277
216
  def payload_json
278
217
  payload = {
279
218
  key: key,
280
219
  raw_value: raw_value,
281
220
  value_type: value_type,
282
221
  description: description,
283
- created_at: created_at.to_f,
284
- updated_at: updated_at.to_f
222
+ created_at: self.class.microseconds(created_at),
223
+ updated_at: self.class.microseconds(updated_at)
285
224
  }
286
225
  payload[:deleted] = true if deleted?
287
226
  payload.to_json