super_settings 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -2
- data/README.md +11 -0
- data/VERSION +1 -1
- data/lib/super_settings/attributes.rb +2 -2
- data/lib/super_settings/context/current.rb +33 -0
- data/lib/super_settings/context.rb +3 -0
- data/lib/super_settings/engine.rb +0 -2
- data/lib/super_settings/local_cache.rb +0 -15
- data/lib/super_settings/setting.rb +1 -1
- data/lib/super_settings/storage/active_record_storage/models.rb +28 -0
- data/lib/super_settings/storage/active_record_storage.rb +3 -20
- data/lib/super_settings/storage/redis_storage.rb +1 -0
- data/lib/super_settings/storage.rb +5 -3
- data/lib/super_settings.rb +37 -13
- data/super_settings.gemspec +8 -1
- metadata +12 -7
- /data/{MIT-LICENSE → MIT-LICENSE.txt} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56ed02dddf10d95bcb20863a4c5918c0396ac510de6815d5ebfdc8d4f7de2847
|
4
|
+
data.tar.gz: 0f2f7d2ae0cbf5948b2d1683b374fbc865e7aaa6a87a1f7e30d73870dc2eac42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6972f850704fd153fec84125c801e3c10627a0d4cc2cde1c3f15274d2cef7867239999aba2f95e83bbf452abc5dd6db9a264b322e46d3fb17673d34cbf362db1
|
7
|
+
data.tar.gz: 3889cb5652b9fd01b6e31a512fa9919bab40abad9d4e5367058c045eb406425d988aab2617a2d148a8bf669340d2839c07225a9d3c5e768a02807f191c24ec26
|
data/CHANGELOG.md
CHANGED
@@ -4,12 +4,22 @@ 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
|
-
##
|
7
|
+
## 1.0.2
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Added SuperSetting.rand method that can return a consistent random number inside of a context block.
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
|
15
|
+
- Lazy load non-required classes.
|
16
|
+
|
17
|
+
## 1.0.1
|
8
18
|
|
9
19
|
### Added
|
10
20
|
- Optimize object shapes for the Ruby interpreter by declaring instance variables in constructors.
|
11
21
|
|
12
|
-
##
|
22
|
+
## 1.0.0
|
13
23
|
|
14
24
|
### Added
|
15
25
|
- Everything!
|
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
[![Continuous Integration](https://github.com/bdurand/super_settings/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/super_settings/actions/workflows/continuous_integration.yml)
|
4
4
|
[![Regression Test](https://github.com/bdurand/super_settings/actions/workflows/regression_test.yml/badge.svg)](https://github.com/bdurand/super_settings/actions/workflows/regression_test.yml)
|
5
5
|
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
|
6
|
+
[![Gem Version](https://badge.fury.io/rb/super_settings.svg)](https://badge.fury.io/rb/super_settings)
|
6
7
|
|
7
8
|
This gem provides a framework for maintaining runtime application settings. Settings are persisted in a database but cached in memory for quick, efficient access. The settings are designed so they can be updated dynamically without requiring code deployment or restarting processes. The code scales very well and can easily handle very high throughput environments.
|
8
9
|
|
@@ -102,6 +103,16 @@ SuperSettings.context do
|
|
102
103
|
end
|
103
104
|
```
|
104
105
|
|
106
|
+
You can also use the `SuperSettings.rand` method inside a context block to return a consistent random number. This can be useful for things like feature flags that you want to turn on for only a percentage of requests:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
def enabled?
|
110
|
+
SuperSettings.float("feature_rollout_percent") <= SuperSettings.rand
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
Now the value of `enabled?` will always return the same value inside of a context block. It will still be random if it is enabled for each context block.
|
115
|
+
|
105
116
|
It's a good idea to add a `context` block around your main unit of work:
|
106
117
|
|
107
118
|
- Rack application: add `SuperSettings::Context::RackMiddleware` to your middleware stack
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.
|
1
|
+
1.0.2
|
@@ -13,8 +13,8 @@ module SuperSettings
|
|
13
13
|
|
14
14
|
def attributes=(values)
|
15
15
|
values.each do |name, value|
|
16
|
-
if respond_to?("#{name}=", true)
|
17
|
-
send("#{name}=", value)
|
16
|
+
if respond_to?(:"#{name}=", true)
|
17
|
+
send(:"#{name}=", value)
|
18
18
|
else
|
19
19
|
raise UnknownAttributeError.new("unknown attribute #{name.to_s.inspect} for #{self.class}")
|
20
20
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SuperSettings
|
4
|
+
module Context
|
5
|
+
class Current
|
6
|
+
def initialize
|
7
|
+
@context = {}
|
8
|
+
@seed = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def include?(key)
|
12
|
+
@context.include?(key)
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
@context[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
def []=(key, value)
|
20
|
+
@context[key] = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def delete(key)
|
24
|
+
@context.delete(key)
|
25
|
+
end
|
26
|
+
|
27
|
+
def rand(max = nil)
|
28
|
+
@seed ||= Random.new_seed
|
29
|
+
Random.new(@seed).rand(max || 1.0)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -17,8 +17,6 @@ module SuperSettings
|
|
17
17
|
end
|
18
18
|
|
19
19
|
if defined?(Sidekiq.server?) && Sidekiq.server?
|
20
|
-
require_relative "context/sidekiq_middleware"
|
21
|
-
|
22
20
|
Sidekiq.configure_server do |sidekiq_config|
|
23
21
|
sidekiq_config.server_middleware do |chain|
|
24
22
|
chain.prepend(SuperSettings::Context::SidekiqMiddleware)
|
@@ -240,21 +240,6 @@ module SuperSettings
|
|
240
240
|
end
|
241
241
|
end
|
242
242
|
|
243
|
-
# Recusive method for creating a nested hash from delimited keys.
|
244
|
-
def set_nested_hash_value(hash, key, value, current_depth, delimiter:, max_depth:)
|
245
|
-
key, sub_key = ((max_depth && current_depth < max_depth) ? [key, nil] : key.split(delimiter, 2))
|
246
|
-
if sub_key
|
247
|
-
sub_hash = hash[key]
|
248
|
-
unless sub_hash.is_a?(Hash)
|
249
|
-
sub_hash = {}
|
250
|
-
hash[key] = sub_hash
|
251
|
-
end
|
252
|
-
set_nested_hash_value(sub_hash, sub_key, value, current_depth + 1, delimiter: delimiter, max_depth: max_depth)
|
253
|
-
else
|
254
|
-
hash[key] = value
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
243
|
# Recursively freeze a hash.
|
259
244
|
def deep_freeze_hash(hash)
|
260
245
|
hash.each_value do |value|
|
@@ -54,7 +54,7 @@ module SuperSettings
|
|
54
54
|
# @api private
|
55
55
|
def storage
|
56
56
|
if @storage == NOT_SET
|
57
|
-
if defined?(::SuperSettings::Storage::ActiveRecordStorage)
|
57
|
+
if defined?(ActiveRecord) && defined?(::SuperSettings::Storage::ActiveRecordStorage)
|
58
58
|
::SuperSettings::Storage::ActiveRecordStorage
|
59
59
|
else
|
60
60
|
raise ArgumentError.new("No storage class defined for #{name}")
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SuperSettings
|
4
|
+
module Storage
|
5
|
+
class ActiveRecordStorage
|
6
|
+
# Base class that the models extend from.
|
7
|
+
class ApplicationRecord < ActiveRecord::Base
|
8
|
+
self.abstract_class = true
|
9
|
+
end
|
10
|
+
|
11
|
+
class Model < ApplicationRecord
|
12
|
+
self.table_name = "super_settings"
|
13
|
+
|
14
|
+
has_many :history_items, class_name: "SuperSettings::Storage::ActiveRecordStorage::HistoryModel", foreign_key: :key, primary_key: :key
|
15
|
+
end
|
16
|
+
|
17
|
+
class HistoryModel < ApplicationRecord
|
18
|
+
self.table_name = "super_settings_histories"
|
19
|
+
|
20
|
+
# Since these models are created automatically on a callback, ensure that the data will
|
21
|
+
# fit into the database columns since we can't handle any validation errors.
|
22
|
+
before_validation do
|
23
|
+
self.changed_by = changed_by.to_s[0, 150] if changed_by.present?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -10,26 +10,9 @@ module SuperSettings
|
|
10
10
|
# @example
|
11
11
|
# rake app:super_settings:install:migrations
|
12
12
|
class ActiveRecordStorage
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
class Model < ApplicationRecord
|
19
|
-
self.table_name = "super_settings"
|
20
|
-
|
21
|
-
has_many :history_items, class_name: "SuperSettings::Storage::ActiveRecordStorage::HistoryModel", foreign_key: :key, primary_key: :key
|
22
|
-
end
|
23
|
-
|
24
|
-
class HistoryModel < ApplicationRecord
|
25
|
-
self.table_name = "super_settings_histories"
|
26
|
-
|
27
|
-
# Since these models are created automatically on a callback, ensure that the data will
|
28
|
-
# fit into the database columns since we can't handle any validation errors.
|
29
|
-
before_validation do
|
30
|
-
self.changed_by = changed_by.to_s[0, 150] if changed_by.present?
|
31
|
-
end
|
32
|
-
end
|
13
|
+
autoload :ApplicationRecord, File.join(__dir__, "active_record_storage/models")
|
14
|
+
autoload :Model, File.join(__dir__, "active_record_storage/models")
|
15
|
+
autoload :HistoryModel, File.join(__dir__, "active_record_storage/models")
|
33
16
|
|
34
17
|
include Storage
|
35
18
|
|
@@ -4,6 +4,10 @@ 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 :ActiveRecordStorage, File.join(__dir__, "storage/active_record_storage")
|
8
|
+
autoload :HttpStorage, File.join(__dir__, "storage/http_storage")
|
9
|
+
autoload :RedisStorage, File.join(__dir__, "storage/redis_storage")
|
10
|
+
|
7
11
|
class RecordInvalid < StandardError
|
8
12
|
end
|
9
13
|
|
@@ -272,10 +276,8 @@ end
|
|
272
276
|
require_relative "storage/http_storage"
|
273
277
|
require_relative "storage/redis_storage"
|
274
278
|
if defined?(ActiveSupport) && ActiveSupport.respond_to?(:on_load)
|
275
|
-
ActiveSupport.on_load(:
|
279
|
+
ActiveSupport.on_load(:active_record_base) do
|
276
280
|
require_relative "storage/active_record_storage"
|
277
281
|
end
|
278
|
-
elsif defined?(ActiveRecord::Base)
|
279
|
-
require_relative "storage/active_record_storage"
|
280
282
|
end
|
281
283
|
# :nocov:
|
data/lib/super_settings.rb
CHANGED
@@ -1,26 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
# These classes are required for the gem to function.
|
4
|
+
require_relative "super_settings/attributes"
|
4
5
|
require_relative "super_settings/coerce"
|
5
6
|
require_relative "super_settings/configuration"
|
6
7
|
require_relative "super_settings/context"
|
7
|
-
require_relative "super_settings/context/rack_middleware"
|
8
8
|
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
9
|
require_relative "super_settings/setting"
|
14
|
-
require_relative "super_settings/history_item"
|
15
10
|
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
|
21
11
|
|
22
12
|
# This is the main interface to the access settings.
|
23
13
|
module SuperSettings
|
14
|
+
# These classes are autoloaded when they are needed.
|
15
|
+
autoload :Application, "super_settings/application"
|
16
|
+
autoload :RestAPI, "super_settings/rest_api"
|
17
|
+
autoload :RackApplication, "super_settings/rack_application"
|
18
|
+
autoload :ControllerActions, "super_settings/controller_actions"
|
19
|
+
autoload :HistoryItem, "super_settings/history_item"
|
20
|
+
autoload :VERSION, "super_settings/version"
|
21
|
+
|
24
22
|
DEFAULT_REFRESH_INTERVAL = 5.0
|
25
23
|
|
26
24
|
@local_cache = LocalCache.new(refresh_interval: DEFAULT_REFRESH_INTERVAL)
|
@@ -105,6 +103,28 @@ module SuperSettings
|
|
105
103
|
Array(val).collect { |v| v&.to_s }
|
106
104
|
end
|
107
105
|
|
106
|
+
# Get a pseudo random number. This method works the same as Kernel.rand. However, if you are
|
107
|
+
# inside a context block, then the random number will be the same each time you call this method.
|
108
|
+
# This is useful when you need to generate a random number for a setting that you want to remain
|
109
|
+
# constant for the duration of the block.
|
110
|
+
#
|
111
|
+
# So, for instance, if you are generating a random number to determine if a feature is enabled,
|
112
|
+
# you can use this method to ensure that the feature is either always enabled or always disabled
|
113
|
+
# for the duration of the block.
|
114
|
+
#
|
115
|
+
# @param max [Integer, Float, Range] the maximum value or range to use for the random number
|
116
|
+
# @return [Integer, Float] the random number. It will be an integer if max is an integer, otherwise
|
117
|
+
# it will be a float.
|
118
|
+
def rand(max = nil)
|
119
|
+
max ||= 1.0
|
120
|
+
context = current_context
|
121
|
+
if context
|
122
|
+
context.rand(max)
|
123
|
+
else
|
124
|
+
Random.rand(max)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
108
128
|
# Create settings and update the local cache with the values. If a block is given, then the
|
109
129
|
# value will be reverted at the end of the block. This method can be used in tests when you
|
110
130
|
# need to inject a specific value into your settings.
|
@@ -148,7 +168,7 @@ module SuperSettings
|
|
148
168
|
def context(&block)
|
149
169
|
reset_context = Thread.current[:super_settings_context].nil?
|
150
170
|
begin
|
151
|
-
Thread.current[:super_settings_context] ||=
|
171
|
+
Thread.current[:super_settings_context] ||= Context::Current.new
|
152
172
|
yield
|
153
173
|
ensure
|
154
174
|
Thread.current[:super_settings_context] = nil if reset_context
|
@@ -241,3 +261,7 @@ module SuperSettings
|
|
241
261
|
end
|
242
262
|
end
|
243
263
|
end
|
264
|
+
|
265
|
+
if defined?(Rails::Engine)
|
266
|
+
require_relative "super_settings/engine"
|
267
|
+
end
|
data/super_settings.gemspec
CHANGED
@@ -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
|
7
|
+
spec.summary = "Fast access runtime settings for applications with an included UI and API for administration."
|
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[
|
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.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Durand
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-09-18 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
|
@@ -57,6 +57,7 @@ files:
|
|
57
57
|
- lib/super_settings/coerce.rb
|
58
58
|
- lib/super_settings/configuration.rb
|
59
59
|
- lib/super_settings/context.rb
|
60
|
+
- lib/super_settings/context/current.rb
|
60
61
|
- lib/super_settings/context/rack_middleware.rb
|
61
62
|
- lib/super_settings/context/sidekiq_middleware.rb
|
62
63
|
- lib/super_settings/controller_actions.rb
|
@@ -68,6 +69,7 @@ files:
|
|
68
69
|
- lib/super_settings/setting.rb
|
69
70
|
- lib/super_settings/storage.rb
|
70
71
|
- lib/super_settings/storage/active_record_storage.rb
|
72
|
+
- lib/super_settings/storage/active_record_storage/models.rb
|
71
73
|
- lib/super_settings/storage/http_storage.rb
|
72
74
|
- lib/super_settings/storage/redis_storage.rb
|
73
75
|
- lib/super_settings/storage/test_storage.rb
|
@@ -76,7 +78,10 @@ files:
|
|
76
78
|
homepage: https://github.com/bdurand/super_settings
|
77
79
|
licenses:
|
78
80
|
- MIT
|
79
|
-
metadata:
|
81
|
+
metadata:
|
82
|
+
homepage_uri: https://github.com/bdurand/super_settings
|
83
|
+
source_code_uri: https://github.com/bdurand/super_settings
|
84
|
+
changelog_uri: https://github.com/bdurand/super_settings/blob/main/CHANGELOG.md
|
80
85
|
post_install_message:
|
81
86
|
rdoc_options: []
|
82
87
|
require_paths:
|
@@ -92,9 +97,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
97
|
- !ruby/object:Gem::Version
|
93
98
|
version: '0'
|
94
99
|
requirements: []
|
95
|
-
rubygems_version: 3.4.
|
100
|
+
rubygems_version: 3.4.10
|
96
101
|
signing_key:
|
97
102
|
specification_version: 4
|
98
|
-
summary: Fast access runtime settings for
|
99
|
-
|
103
|
+
summary: Fast access runtime settings for applications with an included UI and API
|
104
|
+
for administration.
|
100
105
|
test_files: []
|
File without changes
|