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,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aws-sdk-s3"
4
+
5
+ module SuperSettings
6
+ module Storage
7
+ # Storage backend for storing the settings in an S3 object. This should work with any S3-compatible
8
+ # storage service.
9
+ class S3Storage < JSONStorage
10
+ SETTINGS_FILE = "settings.json"
11
+ HISTORY_FILE_SUFFIX = ".history.json"
12
+ DEFAULT_PATH = "super_settings"
13
+
14
+ # Configuration for the S3 storage backend.
15
+ #
16
+ # * access_key_id - The AWS access key ID. Defaults to the SUPER_SETTINGS_AWS_ACCESS_KEY_ID
17
+ # or AWS_ACCESS_KEY_ID environment variable or whatever is set in the `Aws.config` object.
18
+ # * secret_access_key - The AWS secret access key. Defaults to the SUPER_SETTINGS_AWS_SECRET_ACCESS_KEY
19
+ # or AWS_SECRET_ACCESS_KEY environment variable or whatever is set in the `Aws.config` object.
20
+ # * region - The AWS region. Defaults to the SUPER_SETTINGS_AWS_REGION or AWS_REGION environment variable.
21
+ # This is required for AWS S3 but may be optional for S3-compatible services.
22
+ # * endpoint - The S3 endpoint URL. This is optional and should only be used for S3-compatible services.
23
+ # Defaults to the SUPER_SETTINGS_AWS_ENDPOINT or AWS_ENDPOINT environment variable.
24
+ # * bucket - The S3 bucket name. Defaults to the SUPER_SETTINGS_S3_BUCKET or AWS_S3_BUCKET
25
+ # environment variable.
26
+ # * object - The S3 object key. Defaults to "super_settings.json.gz" or the value set in the
27
+ # SUPER_SETTINGS_S3_OBJECT environment variable.
28
+ #
29
+ # You can also specify the configuration using a URL in the format using the SUPER_SETTINGS_S3_URL
30
+ # environment variable. The URL should be in the format:
31
+ #
32
+ # ```
33
+ # s3://access_key_id:secret_access_key@region/bucket/object
34
+ # ```
35
+ class Configuration
36
+ attr_accessor :access_key_id, :secret_access_key, :region, :endpoint, :bucket
37
+ attr_reader :path
38
+
39
+ def initialize
40
+ @access_key_id ||= ENV.fetch("SUPER_SETTINGS_AWS_ACCESS_KEY_ID", ENV["AWS_ACCESS_KEY_ID"])
41
+ @secret_access_key ||= ENV.fetch("SUPER_SETTINGS_AWS_SECRET_ACCESS_KEY", ENV["AWS_SECRET_ACCESS_KEY"])
42
+ @region ||= ENV.fetch("SUPER_SETTINGS_AWS_REGION", ENV["AWS_REGION"])
43
+ @endpoint ||= ENV.fetch("SUPER_SETTINGS_AWS_ENDPOINT", ENV["AWS_ENDPOINT"])
44
+ @bucket ||= ENV.fetch("SUPER_SETTINGS_S3_BUCKET", ENV["AWS_S3_BUCKET"])
45
+ @path ||= ENV.fetch("SUPER_SETTINGS_S3_OBJECT", DEFAULT_PATH)
46
+ self.url = ENV["SUPER_SETTINGS_S3_URL"] unless ENV["SUPER_SETTINGS_S3_URL"].to_s.empty?
47
+ end
48
+
49
+ def url=(url)
50
+ return if url.to_s.empty?
51
+
52
+ uri = URI.parse(url)
53
+ raise ArgumentError, "Invalid S3 URL" unless uri.scheme == "s3"
54
+
55
+ self.access_key_id = uri.user if uri.user
56
+ self.secret_access_key = uri.password if uri.password
57
+ self.region = uri.host if uri.host
58
+ _, bucket, path = uri.path.split("/", 3) if uri.path
59
+ self.bucket = bucket if bucket
60
+ self.path = path if path
61
+ end
62
+
63
+ def path=(value)
64
+ @path = "#{value}.chomp('/')/"
65
+ end
66
+
67
+ def hash
68
+ [self.class, access_key_id, secret_access_key, region, endpoint, bucket, path].hash
69
+ end
70
+ end
71
+
72
+ @bucket = nil
73
+ @bucket_hash = nil
74
+
75
+ class << self
76
+ def last_updated_at
77
+ all.collect(&:updated_at).compact.max
78
+ end
79
+
80
+ def configuration
81
+ @config ||= Configuration.new
82
+ end
83
+
84
+ def destroy_all
85
+ s3_bucket.objects(prefix: configuration.path).each do |object|
86
+ if object.key == file_path(SETTINGS_FILE) || object.key.end_with?(HISTORY_FILE_SUFFIX)
87
+ object.delete
88
+ end
89
+ end
90
+ end
91
+
92
+ protected
93
+
94
+ def default_load_asynchronous?
95
+ true
96
+ end
97
+
98
+ def settings_json_payload
99
+ object = settings_object
100
+ return nil unless object.exists?
101
+
102
+ object.get.body.read
103
+ end
104
+
105
+ def save_settings_json(json)
106
+ object = settings_object
107
+ object.put(body: json)
108
+ end
109
+
110
+ def save_history_json(key, json)
111
+ object = history_object(key)
112
+ object.put(body: json)
113
+ end
114
+
115
+ private
116
+
117
+ def s3_bucket
118
+ if configuration.hash != @bucket_hash
119
+ @bucket_hash = configuration.hash
120
+ options = {
121
+ endpoint: configuration.endpoint,
122
+ access_key_id: configuration.access_key_id,
123
+ secret_access_key: configuration.secret_access_key,
124
+ region: configuration.region
125
+ }
126
+ options[:force_path_style] = true if configuration.endpoint
127
+ options.compact!
128
+
129
+ @bucket = Aws::S3::Resource.new(options).bucket(configuration.bucket)
130
+ end
131
+ @bucket
132
+ end
133
+
134
+ def s3_object(filename)
135
+ s3_bucket.object(file_path(filename))
136
+ end
137
+
138
+ def file_path(filename)
139
+ "#{configuration.path}#{filename}"
140
+ end
141
+
142
+ def settings_object
143
+ s3_object(SETTINGS_FILE)
144
+ end
145
+
146
+ def history_object(key)
147
+ s3_object("#{key}#{HISTORY_FILE_SUFFIX}")
148
+ end
149
+
150
+ def history_json(key)
151
+ object = history_object(key)
152
+ return nil unless object.exists?
153
+
154
+ object.get.body.read
155
+ end
156
+ end
157
+
158
+ protected
159
+
160
+ def fetch_history_json
161
+ self.class.send(:history_json, key)
162
+ end
163
+ end
164
+ end
165
+ end
@@ -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,8 +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
- class RecordInvalid < StandardError
8
- end
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")
10
+ autoload :ActiveRecordStorage, File.join(__dir__, "storage/active_record_storage")
11
+ autoload :HttpStorage, File.join(__dir__, "storage/http_storage")
12
+ autoload :RedisStorage, File.join(__dir__, "storage/redis_storage")
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")
9
16
 
10
17
  def self.included(base)
11
18
  base.extend(ClassMethods)
@@ -84,6 +91,11 @@ module SuperSettings
84
91
  # Set to true to force loading setting asynchronously in a background thread.
85
92
  attr_writer :load_asynchronous
86
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
+
87
99
  protected
88
100
 
89
101
  # Implementing classes can override this method to indicate if it is safe to load the
@@ -151,6 +163,7 @@ module SuperSettings
151
163
  end
152
164
 
153
165
  # The description for the setting.
166
+ #
154
167
  # @return [String]
155
168
  def description
156
169
  # :nocov:
@@ -159,7 +172,7 @@ module SuperSettings
159
172
  end
160
173
 
161
174
  # Set the description for the setting.
162
-
175
+ #
163
176
  # @param val [String]
164
177
  # @return [void]
165
178
  def description=(val)
@@ -269,13 +282,9 @@ module SuperSettings
269
282
  end
270
283
 
271
284
  # :nocov:
272
- require_relative "storage/http_storage"
273
- require_relative "storage/redis_storage"
274
285
  if defined?(ActiveSupport) && ActiveSupport.respond_to?(:on_load)
275
- ActiveSupport.on_load(:active_record) do
286
+ ActiveSupport.on_load(:active_record_base) do
276
287
  require_relative "storage/active_record_storage"
277
288
  end
278
- elsif defined?(ActiveRecord::Base)
279
- require_relative "storage/active_record_storage"
280
289
  end
281
290
  # :nocov:
@@ -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,26 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "super_settings/application"
3
+ require "json"
4
+
5
+ # These classes are required for the gem to function.
6
+ require_relative "super_settings/attributes"
4
7
  require_relative "super_settings/coerce"
5
8
  require_relative "super_settings/configuration"
6
9
  require_relative "super_settings/context"
7
- require_relative "super_settings/context/rack_middleware"
8
10
  require_relative "super_settings/local_cache"
9
- require_relative "super_settings/rest_api"
10
- require_relative "super_settings/rack_application"
11
- require_relative "super_settings/controller_actions"
12
- require_relative "super_settings/attributes"
13
11
  require_relative "super_settings/setting"
14
- require_relative "super_settings/history_item"
15
12
  require_relative "super_settings/storage"
16
- require_relative "super_settings/version"
17
-
18
- if defined?(Rails::Engine)
19
- require_relative "super_settings/engine"
20
- end
13
+ require_relative "super_settings/time_precision"
21
14
 
22
15
  # This is the main interface to the access settings.
23
16
  module SuperSettings
17
+ # These classes are autoloaded when they are needed.
18
+ autoload :Application, "super_settings/application"
19
+ autoload :RestAPI, "super_settings/rest_api"
20
+ autoload :RackApplication, "super_settings/rack_application"
21
+ autoload :ControllerActions, "super_settings/controller_actions"
22
+ autoload :HistoryItem, "super_settings/history_item"
23
+ autoload :HttpClient, "super_settings/http_client"
24
+ autoload :VERSION, "super_settings/version"
25
+
24
26
  DEFAULT_REFRESH_INTERVAL = 5.0
25
27
 
26
28
  @local_cache = LocalCache.new(refresh_interval: DEFAULT_REFRESH_INTERVAL)
@@ -105,6 +107,28 @@ module SuperSettings
105
107
  Array(val).collect { |v| v&.to_s }
106
108
  end
107
109
 
110
+ # Get a pseudo random number. This method works the same as Kernel.rand. However, if you are
111
+ # inside a context block, then the random number will be the same each time you call this method.
112
+ # This is useful when you need to generate a random number for a setting that you want to remain
113
+ # constant for the duration of the block.
114
+ #
115
+ # So, for instance, if you are generating a random number to determine if a feature is enabled,
116
+ # you can use this method to ensure that the feature is either always enabled or always disabled
117
+ # for the duration of the block.
118
+ #
119
+ # @param max [Integer, Float, Range] the maximum value or range to use for the random number
120
+ # @return [Integer, Float] the random number. It will be an integer if max is an integer, otherwise
121
+ # it will be a float.
122
+ def rand(max = nil)
123
+ max ||= 1.0
124
+ context = current_context
125
+ if context
126
+ context.rand(max)
127
+ else
128
+ Random.rand(max)
129
+ end
130
+ end
131
+
108
132
  # Create settings and update the local cache with the values. If a block is given, then the
109
133
  # value will be reverted at the end of the block. This method can be used in tests when you
110
134
  # need to inject a specific value into your settings.
@@ -148,7 +172,7 @@ module SuperSettings
148
172
  def context(&block)
149
173
  reset_context = Thread.current[:super_settings_context].nil?
150
174
  begin
151
- Thread.current[:super_settings_context] ||= {}
175
+ Thread.current[:super_settings_context] ||= Context::Current.new
152
176
  yield
153
177
  ensure
154
178
  Thread.current[:super_settings_context] = nil if reset_context
@@ -201,6 +225,13 @@ module SuperSettings
201
225
  end
202
226
  end
203
227
 
228
+ # Return the configuration object.
229
+ #
230
+ # @return [SuperSettings::Configuration]
231
+ def configuration
232
+ Configuration.instance
233
+ end
234
+
204
235
  # Set the number of seconds between checks to synchronize the in memory cache from the database.
205
236
  # This setting aids in performance since it throttles the number of times the database is queried
206
237
  # for changes. However, changes made to the settings in the databae will take up to the number of
@@ -241,3 +272,7 @@ module SuperSettings
241
272
  end
242
273
  end
243
274
  end
275
+
276
+ if defined?(Rails::Engine)
277
+ require_relative "super_settings/engine"
278
+ end
@@ -4,10 +4,17 @@ 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 a Rails application 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
  spec.homepage = "https://github.com/bdurand/super_settings"
9
10
  spec.license = "MIT"
10
11
 
12
+ spec.metadata = {
13
+ "homepage_uri" => spec.homepage,
14
+ "source_code_uri" => spec.homepage,
15
+ "changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md"
16
+ }
17
+
11
18
  # Specify which files should be added to the gem when it is released.
12
19
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
13
20
  ignore_files = %w[
@@ -16,6 +23,8 @@ Gem::Specification.new do |spec|
16
23
  Gemfile
17
24
  Gemfile.lock
18
25
  Rakefile
26
+ docker-compose.yml
27
+ config.ru
19
28
  assets/
20
29
  bin/
21
30
  gemfiles/
@@ -29,5 +38,5 @@ Gem::Specification.new do |spec|
29
38
 
30
39
  spec.add_development_dependency "bundler"
31
40
 
32
- spec.required_ruby_version = ">= 2.5"
41
+ spec.required_ruby_version = ">= 2.6"
33
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.1
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: 2023-11-11 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
@@ -32,7 +32,7 @@ extensions: []
32
32
  extra_rdoc_files: []
33
33
  files:
34
34
  - CHANGELOG.md
35
- - MIT-LICENSE
35
+ - MIT-LICENSE.txt
36
36
  - README.md
37
37
  - VERSION
38
38
  - app/helpers/super_settings/settings_helper.rb
@@ -43,40 +43,57 @@ 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
58
62
  - lib/super_settings/configuration.rb
59
63
  - lib/super_settings/context.rb
64
+ - lib/super_settings/context/current.rb
60
65
  - lib/super_settings/context/rack_middleware.rb
61
66
  - lib/super_settings/context/sidekiq_middleware.rb
62
67
  - lib/super_settings/controller_actions.rb
63
68
  - lib/super_settings/engine.rb
64
69
  - lib/super_settings/history_item.rb
70
+ - lib/super_settings/http_client.rb
65
71
  - lib/super_settings/local_cache.rb
66
72
  - lib/super_settings/rack_application.rb
67
73
  - lib/super_settings/rest_api.rb
68
74
  - lib/super_settings/setting.rb
69
75
  - lib/super_settings/storage.rb
70
76
  - lib/super_settings/storage/active_record_storage.rb
77
+ - lib/super_settings/storage/active_record_storage/models.rb
78
+ - lib/super_settings/storage/history_attributes.rb
71
79
  - lib/super_settings/storage/http_storage.rb
80
+ - lib/super_settings/storage/json_storage.rb
81
+ - lib/super_settings/storage/mongodb_storage.rb
72
82
  - lib/super_settings/storage/redis_storage.rb
83
+ - lib/super_settings/storage/s3_storage.rb
84
+ - lib/super_settings/storage/storage_attributes.rb
73
85
  - lib/super_settings/storage/test_storage.rb
86
+ - lib/super_settings/storage/transaction.rb
87
+ - lib/super_settings/time_precision.rb
74
88
  - lib/super_settings/version.rb
75
89
  - super_settings.gemspec
76
90
  homepage: https://github.com/bdurand/super_settings
77
91
  licenses:
78
92
  - MIT
79
- metadata: {}
93
+ metadata:
94
+ homepage_uri: https://github.com/bdurand/super_settings
95
+ source_code_uri: https://github.com/bdurand/super_settings
96
+ changelog_uri: https://github.com/bdurand/super_settings/blob/main/CHANGELOG.md
80
97
  post_install_message:
81
98
  rdoc_options: []
82
99
  require_paths:
@@ -85,16 +102,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
102
  requirements:
86
103
  - - ">="
87
104
  - !ruby/object:Gem::Version
88
- version: '2.5'
105
+ version: '2.6'
89
106
  required_rubygems_version: !ruby/object:Gem::Requirement
90
107
  requirements:
91
108
  - - ">="
92
109
  - !ruby/object:Gem::Version
93
110
  version: '0'
94
111
  requirements: []
95
- rubygems_version: 3.4.12
112
+ rubygems_version: 3.4.10
96
113
  signing_key:
97
114
  specification_version: 4
98
- summary: Fast access runtime settings for a Rails application with an included UI
99
- and API 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.
100
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>