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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f58b7aa08a4a6082168ff9fa1487f79ff84bf9374b9463d9ba3ebe4e188b6a47
4
- data.tar.gz: b4bf73f87885acacc091e3f455a8a33a8b0baa770dd00db42a1fac6b468e5427
3
+ metadata.gz: 56ed02dddf10d95bcb20863a4c5918c0396ac510de6815d5ebfdc8d4f7de2847
4
+ data.tar.gz: 0f2f7d2ae0cbf5948b2d1683b374fbc865e7aaa6a87a1f7e30d73870dc2eac42
5
5
  SHA512:
6
- metadata.gz: 973a1ac38488a2cdc4d4cc639f9057d8a1cfd24e216cd7b8cfe4b034af5085b3e0245999b918f3aac7a95416d47fcba46f099d8312afaa2406f63bd5a17f1d25
7
- data.tar.gz: e1922327a1c8965dd72d8b11826f398b7dd0cd4f3a3f27be7cc3aa5a81f44da925a159ae2321a122267a9605dd7fbb5fa6af909d4daeeae3cf371087b4df7a7f
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
- ## [1.0.1]
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
- ## [1.0.0]
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
+ 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
@@ -2,5 +2,8 @@
2
2
 
3
3
  module SuperSettings
4
4
  module Context
5
+ autoload :Current, File.join(__dir__, "context/current")
6
+ autoload :RackMiddleware, File.join(__dir__, "context/rack_middleware")
7
+ autoload :SidekiqMiddleware, File.join(__dir__, "context/sidekiq_middleware")
5
8
  end
6
9
  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
- # Base class that the models extend from.
14
- class ApplicationRecord < ActiveRecord::Base
15
- self.abstract_class = true
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
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "redis"
4
5
 
5
6
  module SuperSettings
6
7
  module Storage
@@ -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(:active_record) do
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:
@@ -1,26 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "super_settings/application"
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
@@ -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 = "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.1
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: 2023-11-11 00:00:00.000000000 Z
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.12
100
+ rubygems_version: 3.4.10
96
101
  signing_key:
97
102
  specification_version: 4
98
- summary: Fast access runtime settings for a Rails application with an included UI
99
- and API for administration.
103
+ summary: Fast access runtime settings for applications with an included UI and API
104
+ for administration.
100
105
  test_files: []
File without changes