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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/README.md +110 -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/controller_actions.rb +2 -2
  29. data/lib/super_settings/engine.rb +1 -1
  30. data/lib/super_settings/history_item.rb +1 -1
  31. data/lib/super_settings/http_client.rb +165 -0
  32. data/lib/super_settings/rack_application.rb +3 -3
  33. data/lib/super_settings/rest_api.rb +5 -4
  34. data/lib/super_settings/setting.rb +13 -2
  35. data/lib/super_settings/storage/active_record_storage.rb +7 -0
  36. data/lib/super_settings/storage/history_attributes.rb +31 -0
  37. data/lib/super_settings/storage/http_storage.rb +60 -184
  38. data/lib/super_settings/storage/json_storage.rb +201 -0
  39. data/lib/super_settings/storage/mongodb_storage.rb +238 -0
  40. data/lib/super_settings/storage/redis_storage.rb +49 -111
  41. data/lib/super_settings/storage/s3_storage.rb +165 -0
  42. data/lib/super_settings/storage/storage_attributes.rb +64 -0
  43. data/lib/super_settings/storage/test_storage.rb +3 -5
  44. data/lib/super_settings/storage/transaction.rb +67 -0
  45. data/lib/super_settings/storage.rb +13 -6
  46. data/lib/super_settings/time_precision.rb +36 -0
  47. data/lib/super_settings.rb +11 -0
  48. data/super_settings.gemspec +4 -2
  49. metadata +22 -9
  50. data/lib/super_settings/application/images/edit.svg +0 -1
  51. data/lib/super_settings/application/images/info.svg +0 -1
  52. data/lib/super_settings/application/images/slash.svg +0 -1
  53. data/lib/super_settings/application/images/trash.svg +0 -1
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SuperSettings
4
+ module Storage
5
+ # Generic class that can be extended to represent a setting in memory.
6
+ class StorageAttributes
7
+ include SuperSettings::Storage
8
+
9
+ attr_reader :key, :raw_value, :description, :value_type, :updated_at, :created_at
10
+
11
+ def initialize(attributes = nil)
12
+ @key = nil
13
+ @raw_value = nil
14
+ @description = nil
15
+ @value_type = nil
16
+ @deleted = false
17
+ @updated_at = nil
18
+ @created_at = nil
19
+ @persisted = false
20
+ super
21
+ end
22
+
23
+ def key=(value)
24
+ @key = (Coerce.blank?(value) ? nil : value.to_s)
25
+ end
26
+
27
+ def raw_value=(value)
28
+ @raw_value = (Coerce.blank?(value) ? nil : value.to_s)
29
+ end
30
+
31
+ def value_type=(value)
32
+ @value_type = (Coerce.blank?(value) ? nil : value.to_s)
33
+ end
34
+
35
+ def description=(value)
36
+ @description = (Coerce.blank?(value) ? nil : value.to_s)
37
+ end
38
+
39
+ def updated_at=(value)
40
+ @updated_at = SuperSettings::Coerce.time(value)
41
+ end
42
+
43
+ def created_at=(value)
44
+ @created_at = SuperSettings::Coerce.time(value)
45
+ end
46
+
47
+ def deleted?
48
+ !!@deleted
49
+ end
50
+
51
+ def deleted=(val)
52
+ @deleted = !!val
53
+ end
54
+
55
+ def persisted?
56
+ !!@persisted
57
+ end
58
+
59
+ def persisted=(val)
60
+ @persisted = !!val
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
-
5
3
  module SuperSettings
6
4
  module Storage
7
5
  # Implementation of the SuperSettings::Storage model for running unit tests.
@@ -28,7 +26,7 @@ module SuperSettings
28
26
  items
29
27
  end
30
28
 
31
- def clear
29
+ def destroy_all
32
30
  @settings = {}
33
31
  @history = {}
34
32
  end
@@ -42,7 +40,7 @@ module SuperSettings
42
40
  end
43
41
 
44
42
  def updated_since(time)
45
- settings.values.select { |attributes| attributes[:updated_at].to_f >= time.to_f }.collect do |attributes|
43
+ settings.values.select { |attributes| attributes[:updated_at].to_f > time.to_f }.collect do |attributes|
46
44
  setting = new(attributes)
47
45
  setting.send(:set_persisted!)
48
46
  setting
@@ -54,7 +52,7 @@ module SuperSettings
54
52
  return nil unless attributes
55
53
  setting = new(attributes)
56
54
  setting.send(:set_persisted!)
57
- setting
55
+ setting unless setting.deleted?
58
56
  end
59
57
 
60
58
  def last_updated_at
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SuperSettings
4
+ module Storage
5
+ # This module provides support for transactions in storage models that don't natively
6
+ # support transactions.
7
+ module Transaction
8
+ def self.included(base)
9
+ base.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ def transaction(&block)
14
+ if Thread.current[transaction_key]
15
+ yield Thread.current[transaction_key]
16
+ else
17
+ begin
18
+ changes = []
19
+ Thread.current[transaction_key] = changes
20
+
21
+ yield(changes)
22
+
23
+ if save_all(changes) != false
24
+ changes.each do |object|
25
+ object.persisted = true if object.respond_to?(:persisted=)
26
+ end
27
+ end
28
+ ensure
29
+ Thread.current[transaction_key] = nil
30
+ end
31
+ end
32
+ end
33
+
34
+ def save_all(changes)
35
+ # :nocov:
36
+ raise NotImplementedError
37
+ # :nocov:
38
+ end
39
+
40
+ private
41
+
42
+ def transaction_key
43
+ "#{name}.transaction"
44
+ end
45
+ end
46
+
47
+ def persisted=(value)
48
+ @persisted = Coerce.boolean(value)
49
+ end
50
+
51
+ def persisted?
52
+ !!@persisted
53
+ end
54
+
55
+ def save!
56
+ self.updated_at ||= Time.now
57
+ self.created_at ||= updated_at
58
+
59
+ self.class.transaction do |changes|
60
+ changes << self
61
+ end
62
+
63
+ true
64
+ end
65
+ end
66
+ end
67
+ end
@@ -4,12 +4,15 @@ module SuperSettings
4
4
  # Abstraction over how a setting is stored and retrieved from the storage engine. Models
5
5
  # must implement the methods module in this module that raise NotImplementedError.
6
6
  module Storage
7
+ autoload :StorageAttributes, File.join(__dir__, "storage/storage_attributes")
8
+ autoload :HistoryAttributes, File.join(__dir__, "storage/history_attributes")
9
+ autoload :Transaction, File.join(__dir__, "storage/transaction")
7
10
  autoload :ActiveRecordStorage, File.join(__dir__, "storage/active_record_storage")
8
11
  autoload :HttpStorage, File.join(__dir__, "storage/http_storage")
9
12
  autoload :RedisStorage, File.join(__dir__, "storage/redis_storage")
10
-
11
- class RecordInvalid < StandardError
12
- end
13
+ autoload :JSONStorage, File.join(__dir__, "storage/json_storage")
14
+ autoload :S3Storage, File.join(__dir__, "storage/s3_storage")
15
+ autoload :MongoDBStorage, File.join(__dir__, "storage/mongodb_storage")
13
16
 
14
17
  def self.included(base)
15
18
  base.extend(ClassMethods)
@@ -88,6 +91,11 @@ module SuperSettings
88
91
  # Set to true to force loading setting asynchronously in a background thread.
89
92
  attr_writer :load_asynchronous
90
93
 
94
+ # Implementing classes can override this method to delete all settings. This is used
95
+ # for testing purposes.
96
+ def destroy_all
97
+ end
98
+
91
99
  protected
92
100
 
93
101
  # Implementing classes can override this method to indicate if it is safe to load the
@@ -155,6 +163,7 @@ module SuperSettings
155
163
  end
156
164
 
157
165
  # The description for the setting.
166
+ #
158
167
  # @return [String]
159
168
  def description
160
169
  # :nocov:
@@ -163,7 +172,7 @@ module SuperSettings
163
172
  end
164
173
 
165
174
  # Set the description for the setting.
166
-
175
+ #
167
176
  # @param val [String]
168
177
  # @return [void]
169
178
  def description=(val)
@@ -273,8 +282,6 @@ module SuperSettings
273
282
  end
274
283
 
275
284
  # :nocov:
276
- require_relative "storage/http_storage"
277
- require_relative "storage/redis_storage"
278
285
  if defined?(ActiveSupport) && ActiveSupport.respond_to?(:on_load)
279
286
  ActiveSupport.on_load(:active_record_base) do
280
287
  require_relative "storage/active_record_storage"
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SuperSettings
4
+ # Helper class for truncating timestamps to a specific precision. This is used by storage engines
5
+ # to ensure that timestamps are stored and compared with the same precision.
6
+ class TimePrecision
7
+ attr_reader :time
8
+
9
+ def initialize(time, precision = :microsecond)
10
+ raise ArgumentError.new("Invalid precision: #{precision}") unless valid_precision?(precision)
11
+
12
+ @time = time_with_precision(time.to_f, precision) if time
13
+ end
14
+
15
+ def to_f
16
+ @time.to_f
17
+ end
18
+
19
+ private
20
+
21
+ def valid_precision?(precision)
22
+ [:microsecond, :millisecond].include?(precision)
23
+ end
24
+
25
+ def time_with_precision(timestamp, precision)
26
+ usec = (timestamp % 1) * 1_000_000.0
27
+ if precision == :millisecond
28
+ milliseconds = (usec / 1000.0).round(3).floor
29
+ Time.at(timestamp.to_i, milliseconds, :millisecond).utc
30
+ else
31
+ microseconds = usec.round
32
+ Time.at(timestamp.to_i, microseconds, :microsecond).utc
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "json"
4
+
3
5
  # These classes are required for the gem to function.
4
6
  require_relative "super_settings/attributes"
5
7
  require_relative "super_settings/coerce"
@@ -8,6 +10,7 @@ require_relative "super_settings/context"
8
10
  require_relative "super_settings/local_cache"
9
11
  require_relative "super_settings/setting"
10
12
  require_relative "super_settings/storage"
13
+ require_relative "super_settings/time_precision"
11
14
 
12
15
  # This is the main interface to the access settings.
13
16
  module SuperSettings
@@ -17,6 +20,7 @@ module SuperSettings
17
20
  autoload :RackApplication, "super_settings/rack_application"
18
21
  autoload :ControllerActions, "super_settings/controller_actions"
19
22
  autoload :HistoryItem, "super_settings/history_item"
23
+ autoload :HttpClient, "super_settings/http_client"
20
24
  autoload :VERSION, "super_settings/version"
21
25
 
22
26
  DEFAULT_REFRESH_INTERVAL = 5.0
@@ -221,6 +225,13 @@ module SuperSettings
221
225
  end
222
226
  end
223
227
 
228
+ # Return the configuration object.
229
+ #
230
+ # @return [SuperSettings::Configuration]
231
+ def configuration
232
+ Configuration.instance
233
+ end
234
+
224
235
  # Set the number of seconds between checks to synchronize the in memory cache from the database.
225
236
  # This setting aids in performance since it throttles the number of times the database is queried
226
237
  # for changes. However, changes made to the settings in the databae will take up to the number of
@@ -4,7 +4,7 @@ Gem::Specification.new do |spec|
4
4
  spec.authors = ["Brian Durand"]
5
5
  spec.email = ["bbdurand@gmail.com"]
6
6
 
7
- spec.summary = "Fast access runtime settings for applications with an included UI and API for administration."
7
+ spec.summary = "SuperSettings provides a scalable framework for managing dynamic runtime application settings with in-memory caching, strong typing, a built-in web UI, and support for multiple storage backends."
8
8
 
9
9
  spec.homepage = "https://github.com/bdurand/super_settings"
10
10
  spec.license = "MIT"
@@ -23,6 +23,8 @@ Gem::Specification.new do |spec|
23
23
  Gemfile
24
24
  Gemfile.lock
25
25
  Rakefile
26
+ docker-compose.yml
27
+ config.ru
26
28
  assets/
27
29
  bin/
28
30
  gemfiles/
@@ -36,5 +38,5 @@ Gem::Specification.new do |spec|
36
38
 
37
39
  spec.add_development_dependency "bundler"
38
40
 
39
- spec.required_ruby_version = ">= 2.5"
41
+ spec.required_ruby_version = ">= 2.6"
40
42
  end
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: 1.0.2
4
+ version: 2.0.0
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-09-18 00:00:00.000000000 Z
11
+ date: 2024-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -43,15 +43,19 @@ files:
43
43
  - lib/super_settings/application.rb
44
44
  - lib/super_settings/application/api.js
45
45
  - lib/super_settings/application/helper.rb
46
- - lib/super_settings/application/images/edit.svg
47
- - lib/super_settings/application/images/info.svg
46
+ - lib/super_settings/application/images/arrow-down-short.svg
47
+ - lib/super_settings/application/images/arrow-up-short.svg
48
+ - lib/super_settings/application/images/info-circle.svg
49
+ - lib/super_settings/application/images/pencil-square.svg
48
50
  - lib/super_settings/application/images/plus.svg
49
- - lib/super_settings/application/images/slash.svg
50
- - lib/super_settings/application/images/trash.svg
51
+ - lib/super_settings/application/images/trash3.svg
52
+ - lib/super_settings/application/images/x-circle.svg
51
53
  - lib/super_settings/application/index.html.erb
52
54
  - lib/super_settings/application/layout.html.erb
53
55
  - lib/super_settings/application/layout_styles.css
56
+ - lib/super_settings/application/layout_vars.css.erb
54
57
  - lib/super_settings/application/scripts.js
58
+ - lib/super_settings/application/style_vars.css.erb
55
59
  - lib/super_settings/application/styles.css
56
60
  - lib/super_settings/attributes.rb
57
61
  - lib/super_settings/coerce.rb
@@ -63,6 +67,7 @@ files:
63
67
  - lib/super_settings/controller_actions.rb
64
68
  - lib/super_settings/engine.rb
65
69
  - lib/super_settings/history_item.rb
70
+ - lib/super_settings/http_client.rb
66
71
  - lib/super_settings/local_cache.rb
67
72
  - lib/super_settings/rack_application.rb
68
73
  - lib/super_settings/rest_api.rb
@@ -70,9 +75,16 @@ files:
70
75
  - lib/super_settings/storage.rb
71
76
  - lib/super_settings/storage/active_record_storage.rb
72
77
  - lib/super_settings/storage/active_record_storage/models.rb
78
+ - lib/super_settings/storage/history_attributes.rb
73
79
  - lib/super_settings/storage/http_storage.rb
80
+ - lib/super_settings/storage/json_storage.rb
81
+ - lib/super_settings/storage/mongodb_storage.rb
74
82
  - lib/super_settings/storage/redis_storage.rb
83
+ - lib/super_settings/storage/s3_storage.rb
84
+ - lib/super_settings/storage/storage_attributes.rb
75
85
  - lib/super_settings/storage/test_storage.rb
86
+ - lib/super_settings/storage/transaction.rb
87
+ - lib/super_settings/time_precision.rb
76
88
  - lib/super_settings/version.rb
77
89
  - super_settings.gemspec
78
90
  homepage: https://github.com/bdurand/super_settings
@@ -90,7 +102,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
90
102
  requirements:
91
103
  - - ">="
92
104
  - !ruby/object:Gem::Version
93
- version: '2.5'
105
+ version: '2.6'
94
106
  required_rubygems_version: !ruby/object:Gem::Requirement
95
107
  requirements:
96
108
  - - ">="
@@ -100,6 +112,7 @@ requirements: []
100
112
  rubygems_version: 3.4.10
101
113
  signing_key:
102
114
  specification_version: 4
103
- summary: Fast access runtime settings for applications with an included UI and API
104
- for administration.
115
+ summary: SuperSettings provides a scalable framework for managing dynamic runtime
116
+ application settings with in-memory caching, strong typing, a built-in web UI, and
117
+ support for multiple storage backends.
105
118
  test_files: []
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-edit"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-info"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-slash"><circle cx="12" cy="12" r="10"></circle><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"></line></svg>
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-trash-2"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path><line x1="10" y1="11" x2="10" y2="17"></line><line x1="14" y1="11" x2="14" y2="17"></line></svg>