abstract_feature_branch 1.4.0 → 1.5.1

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: 0241ac7ec58e588b7ad5705d3e2261d38d34b1cb8b7e354f1e356d36fbea4772
4
- data.tar.gz: 25bb7d870cf117cae57df4d9510ba92dd01d6892f9088b33e5d6eb48d52a4eae
3
+ metadata.gz: 9b05f473f9e1490d0e627cd6ee46bc065fd99054eeec4fe4276b6009650a3ad4
4
+ data.tar.gz: 657dbe0b15b9ac0827ef3dcbaa748bc571cb7d1ef95b25e44d3ee8c1029bf4ae
5
5
  SHA512:
6
- metadata.gz: 25e037089e15a21f1620f112c2ce9a5479c10633e2bccdd5760d680c3f490c6a5bdd8a9a981ae8e3f2119623ddc232d8ca67d8efac5260d7e0197bf50458c72d
7
- data.tar.gz: 287cda44bd97f86a0dd031747af5be1af7b5c748cc9794aa3d776607dd3db28d25703c67fb6afba10f7b0b725dcbd7694e9f6d3a5995b433628a9b52d204c6a0
6
+ metadata.gz: cbf7abdfa86da1c21bb55e913809879adc0fb7d049ae047a272dbbdec648f064112036bc1671ed2f66dc0cdec7539d0e6fee0f7d9bc099dcc710229cc8a2d1e4
7
+ data.tar.gz: 8effdc0bba15209eb0d4366dd719b98bd11c3d5e6f772788156d40a625f2539b17a726ea79ceeedc29576dc58b49a14f1b0a26cabe46a8037473e0fe5c3cc4be
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.5.1
4
+
5
+ - `AbstractFeatureBranch.toggled_features_for_scope(scope)` API method that returns toggled features for a scope (String)
6
+ - `AbstractFeatureBranch.scopes_for_feature(feature)` API method that returns scopes for a (scoped) feature (String or Symbol)
7
+
8
+ ## 1.5.0
9
+
10
+ - Generalize "Per-User Feature Enablement" as "Scoped Feature Enablement" (using `scoped` value instead of `per_user` value) to scope features by any scope IDs (e.g. entity IDs or value objects)
11
+ - Rename `toggle_features_for_user` to `toggle_features_for_scope`
12
+
3
13
  ## 1.4.0
4
14
 
5
15
  - Avoid live loading of per-user features when `AbstractFeatureBranch.feature_store_live_fetching` is `false` to ensure better performance through caching.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Abstract Feature Branch 1.4.0
1
+ # Abstract Feature Branch 1.5.1
2
2
  [![Gem Version](https://badge.fury.io/rb/abstract_feature_branch.png)](http://badge.fury.io/rb/abstract_feature_branch)
3
3
  [![Build Status](https://api.travis-ci.org/AndyObtiva/abstract_feature_branch.png?branch=master)](https://travis-ci.org/AndyObtiva/abstract_feature_branch)
4
4
  [![Coverage Status](https://coveralls.io/repos/AndyObtiva/abstract_feature_branch/badge.png?branch=master)](https://coveralls.io/r/AndyObtiva/abstract_feature_branch?branch=master)
@@ -23,8 +23,6 @@ context-specific feature files if needed.
23
23
 
24
24
  [`abstract_feature_branch`](https://rubygems.org/gems/abstract_feature_branch) is one of the simplest and most minimalistic "Feature Flags" Ruby gems out there as it enables you to get started very quickly by simply leveraging YAML files without having to set up a data store if you do not need it (albeit, you also have the option to use [Redis](https://redis.com) as a very fast in-memory data store).
25
25
 
26
-
27
-
28
26
  Requirements
29
27
  ------------
30
28
  - Ruby (between `~> 3.1.0` and `~> 1.8.7`)
@@ -38,17 +36,17 @@ Setup
38
36
  ### Rails Application Use
39
37
 
40
38
  1. Configure Rubygem
41
- - With `rails` between `~> 7.0` and `~> 2.0`: Add the following to Gemfile <pre>gem 'abstract_feature_branch', '~> 1.4.0'</pre>
42
- - With `rails` `~> 2.0` only: Add the following to config/environment.rb <pre>config.gem 'abstract_feature_branch', :version => '1.4.0'</pre>
39
+ - With `rails` between `~> 7.0` and `~> 2.0`: Add the following to Gemfile <pre>gem 'abstract_feature_branch', '~> 1.5.1'</pre>
40
+ - With `rails` `~> 2.0` only: Add the following to config/environment.rb <pre>config.gem 'abstract_feature_branch', :version => '1.5.1'</pre>
43
41
  2. Generate <code>config/initializers/abstract_feature_branch.rb</code>, <code>lib/tasks/abstract_feature_branch.rake</code>, <code>config/features.yml</code> and <code>config/features.local.yml</code> in your Rails app directory by running <pre>rails g abstract_feature_branch:install</pre>
44
42
  3. [Optional] Generate <code>config/features/[context_path].yml</code> in your Rails app directory by running <pre>rails g abstract_feature_branch:context context_path</pre> (more details under [**instructions**](#instructions))
45
- 4. [Optional] Customize configuration in <code>config/initializers/abstract_feature_branch.rb</code> (can be useful for changing location of feature files in Rails application, configuring Redis with a Redis or ConnectionPool instance to use for overrides, and [per-user feature enablement](#per-user-feature-enablement), or troubleshooting a specific Rails environment feature configuration)
43
+ 4. [Optional] Customize configuration in <code>config/initializers/abstract_feature_branch.rb</code> (can be useful for changing location of feature files in Rails application, configuring Redis with a Redis or ConnectionPool instance to use for overrides, and [scoped feature enablement](#scoped-feature-enablement) (e.g. per-user), or troubleshooting a specific Rails environment feature configuration)
46
44
  5. [Optional] Redis Server (between `~> 7.0` and `~> 3.0`): On the Mac, you can install simply via [Homebrew](https://brew.sh/) with `brew install redis`
47
45
  6. [Optional] `redis` client gem (between `~> 5.0` and `~> 3.0`): Add the following to Gemfile above [`abstract_feature_branch`](https://rubygems.org/gems/abstract_feature_branch) <pre>gem 'redis', '~> 5.0.5'</pre>
48
46
 
49
47
  ### Ruby Application General Use
50
48
 
51
- 1. <pre>gem install abstract_feature_branch -v 1.4.0</pre>
49
+ 1. <pre>gem install abstract_feature_branch -v 1.5.1</pre>
52
50
  2. Add code <code>require 'abstract_feature_branch'</code>
53
51
  3. Create <code>config/features.yml</code> under <code>AbstractFeatureBranch.application_root</code> and fill it with content similar to that of the sample <code>config/features.yml</code> mentioned under [**instructions**](#instructions).
54
52
  4. [Optional] Create <code>config/features.local.yml</code> under <code>AbstractFeatureBranch.application_root</code> (more details under [**instructions**](#instructions))
@@ -58,8 +56,8 @@ Setup
58
56
  8. [Optional] Add code <code>AbstractFeatureBranch.logger = "[your_application_logger]"</code> (it defaults to a new instance of Ruby <code>Logger</code>. Must use a logger with <code>info</code> and <code>warn</code> methods).
59
57
  9. [Optional] Add code <code>AbstractFeatureBranch.cacheable = {[environment] => [true/false]}</code> to indicate cacheability of loaded feature files for enhanced performance (it defaults to true for every environment other than development).
60
58
  10. [Optional] Add code <code>AbstractFeatureBranch.load_application_features</code> to pre-load application features for improved first-use performance
61
- 11. [Optional] Add code <code>AbstractFeatureBranch.feature_store = Redis.new(options)</code> to configure Redis for overrides and/or [per-user feature enablement](#per-user-feature-enablement)
62
- 12. [Optional] Set <code>AbstractFeatureBranch.feature_store_live_fetching = true</code> to enable live fetching of features from store (e.g. Redis) to avoid need for app/server restart upon feature changes, with the trade-off of slightly more latency due to making calls to feature store over the network (this affects per-user feature enablement too, pre-caching all user IDs for features on app/server restart when disabled)
59
+ 11. [Optional] Add code <code>AbstractFeatureBranch.feature_store = Redis.new(options)</code> to configure Redis for overrides and/or [scoped feature enablement](#scoped-feature-enablement) (e.g. per-user)
60
+ 12. [Optional] Set <code>AbstractFeatureBranch.feature_store_live_fetching = true</code> to enable live fetching of features from store (e.g. Redis) to avoid need for app/server restart upon feature changes, with the trade-off of slightly more latency due to making calls to feature store over the network (this affects scoped feature enablement too, pre-caching all user IDs for features on app/server restart when disabled)
63
61
 
64
62
  Instructions
65
63
  ------------
@@ -84,7 +82,7 @@ enabled (true) or disabled (false) per environment (e.g. production).
84
82
  > feature1: true
85
83
  > feature2: true
86
84
  > feature3: false
87
- > feature4: per_user
85
+ > feature4: scoped
88
86
  >
89
87
  > development:
90
88
  > <<: *defaults
@@ -131,17 +129,17 @@ Note that <code>feature_enabled?</code> returns false if the feature is disabled
131
129
  > AbstractFeatureBranch.environment_features('development')
132
130
  > # => {"feature1"=>true, "feature2"=>false, "feature3"=>false, "feature4"=>true}
133
131
 
134
- ### Per-User Feature Enablement
132
+ ### Scoped Feature Enablement
135
133
 
136
- It is possible to restrict enablement of features per specific users (or per entities of any kind) by setting a feature value to <code>per_user</code>, and then toggling features for specific users (or other entities).
134
+ It is possible to restrict enablement of features per specific users (or per entities of any kind) by setting a feature value to <code>scoped</code>, and then toggling features for specific users (or other entities).
137
135
 
138
- 1. Use <code>toggle_features_for_user</code> in Ruby code to enable features per user ID (e.g. email address or database ID). This loads Redis client gem into memory and stores per-user feature configuration in Redis.
136
+ 1. Use <code>toggle_features_for_scope</code> in Ruby code to enable features per scope ID (e.g. entity ID, comma-separated compound ID, JSON string, or value object), which must be a String or a value that is safely-convertable to a String like Integer (e.g. email address or database ID). This loads Redis client gem into memory and stores scoped feature configuration in Redis.
139
137
  In the example below, current_user is a method that provides the current signed in user (e.g. using Rails [Devise] (https://github.com/plataformatec/devise) library).
140
138
 
141
- > user_id = current_user.email
142
- > AbstractFeatureBranch.toggle_features_for_user(user_id, :feature1 => true, :feature2 => false, :feature3 => true, :feature5 => true)
139
+ > scope = current_user.email
140
+ > AbstractFeatureBranch.toggle_features_for_scope(scope, :feature1 => true, :feature2 => false, :feature3 => true, :feature5 => true)
143
141
 
144
- Use alternate version of <code>feature_branch</code> and <code>feature_enabled?</code> passing extra <code>user_id</code> argument
142
+ Use alternate version of <code>feature_branch</code> and <code>feature_enabled?</code> passing extra <code>scope</code> argument
145
143
 
146
144
  Examples:
147
145
 
@@ -168,9 +166,17 @@ Examples:
168
166
  Note:
169
167
 
170
168
  If a feature is enabled as <code>true</code> or disabled as <code>false</code> in features.yml (or one of the overrides
171
- like features.local.yml or environment variable overrides), then it overrides toggled per-user restrictions, becoming
169
+ like features.local.yml or environment variable overrides), then it overrides toggled scoped feature restrictions, becoming
172
170
  enabled or disabled globally.
173
171
 
172
+ API
173
+ ---
174
+
175
+ `AbstractFeatureBranch.toggle_features_for_scope(scope, feature1: true, feature2: false, ...)`: API method that toggles features (Strings or Symbols) for a scope
176
+
177
+ `AbstractFeatureBranch.toggled_features_for_scope(scope)`: API method that returns toggled features for a scope (String)
178
+
179
+ `AbstractFeatureBranch.scopes_for_feature(feature)`: API method that returns scopes for a (scoped) feature (String or Symbol)
174
180
 
175
181
  Recommendations
176
182
  ---------------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.0
1
+ 1.5.1
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: abstract_feature_branch 1.4.0 ruby lib
5
+ # stub: abstract_feature_branch 1.5.1 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "abstract_feature_branch".freeze
9
- s.version = "1.4.0"
9
+ s.version = "1.5.1"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["Andy Maleh".freeze]
14
- s.date = "2023-02-05"
14
+ s.date = "2023-02-14"
15
15
  s.description = "abstract_feature_branch is a Ruby gem that provides a unique variation on the Branch by Abstraction Pattern by Paul Hammant and the Feature Toggles Pattern by Martin Fowler to enhance team productivity and improve software fault tolerance.\n\nIt provides the ability to wrap blocks of code with an abstract feature branch name, and then specify in a configuration file which features to be switched on or off.\n\nThe goal is to build out upcoming features in the same source code repository branch (i.e. Continuous Integration and Trunk-Based Development), regardless of whether all are completed by the next release date or not, thus increasing team productivity by preventing integration delays. Developers then disable in-progress features until they are ready to be switched on in production, yet enable them locally and in staging environments for in-progress testing.\n\nThis gives developers the added benefit of being able to switch a feature off after release should big problems arise for a high risk feature.\n\nabstract_feature_branch additionally supports Domain Driven Design's pattern of Bounded Contexts by allowing developers to configure context-specific feature files if needed.\n\nabstract_feature_branch is one of the simplest and most minimalistic \"Feature Flags\" Ruby gems out there as it enables you to get started very quickly by simply leveraging YAML files without having to set up a data store if you do not need it (albeit, you also have the option to use Redis as a very fast in-memory data store).\n".freeze
16
16
  s.extra_rdoc_files = [
17
17
  "CHANGELOG.md",
@@ -39,7 +39,7 @@ Gem::Specification.new do |s|
39
39
  ]
40
40
  s.homepage = "http://github.com/AndyObtiva/abstract_feature_branch".freeze
41
41
  s.licenses = ["MIT".freeze]
42
- s.post_install_message = "\nRails-only post-install instructions:\n\n1) Run the following command to generate the Rails initializer and basic feature files:\n\nrails g abstract_feature_branch:install\n\n2) Optionally, you may run this command to generate feature files per context:\n\nrails g abstract_feature_branch:context context_path\n \n3) Optionally, install Redis server with [Homebrew](https://brew.sh/) by running:\n\nbrew install redis\n\n4) Optionally, install redis client gem (required with Redis server) by adding the following line to Gemfile above abstract_feature_branch:\n\ngem 'redis', '~> 5.0.5'\n\nAfterwards, run:\n\nbundle\n\n5) Optionally, customize configuration in config/initializers/abstract_feature_branch.rb\n\n(can be useful for changing location of feature files in Rails application,\nconfiguring Redis with a Redis or ConnectionPool instance to use for overrides and per-user feature enablement,\nand/or troubleshooting specific Rails environment feature configurations)\n\n".freeze
42
+ s.post_install_message = "\nRails-only post-install instructions:\n\n1) Run the following command to generate the Rails initializer and basic feature files:\n\nrails g abstract_feature_branch:install\n\n2) Optionally, you may run this command to generate feature files per context:\n\nrails g abstract_feature_branch:context context_path\n \n3) Optionally, install Redis server with [Homebrew](https://brew.sh/) by running:\n\nbrew install redis\n\n4) Optionally, install redis client gem (required with Redis server) by adding the following line to Gemfile above abstract_feature_branch:\n\ngem 'redis', '~> 5.0.5'\n\nAfterwards, run:\n\nbundle\n\n5) Optionally, customize configuration in config/initializers/abstract_feature_branch.rb\n\n(can be useful for changing location of feature files in Rails application,\nconfiguring Redis with a Redis or ConnectionPool instance to use for overrides and scoped feature enablement (e.g. per-user),\nand/or troubleshooting specific Rails environment feature configurations)\n\n".freeze
43
43
  s.rubygems_version = "3.3.6".freeze
44
44
  s.summary = "abstract_feature_branch is a Ruby gem that provides a variation on the Branch by Abstraction Pattern by Paul Hammant and the Feature Toggles Pattern by Martin Fowler (aka Feature Flags) to enable Continuous Integration and Trunk-Based Development.".freeze
45
45
 
@@ -20,6 +20,8 @@ require 'abstract_feature_branch/configuration'
20
20
  module AbstractFeatureBranch
21
21
  ENV_FEATURE_PREFIX = "abstract_feature_branch_"
22
22
  REDIS_HKEY = "abstract_feature_branch"
23
+ VALUE_SCOPED = 'scoped'
24
+ SCOPED_SPECIAL_VALUES = [VALUE_SCOPED, 'per_user', 'per-user', 'per user']
23
25
 
24
26
  class << self
25
27
  extend Forwardable
@@ -27,7 +29,7 @@ module AbstractFeatureBranch
27
29
  :application_root, :application_root=, :initialize_application_root, :application_environment, :application_environment=, :initialize_application_environment,
28
30
  :logger, :logger=, :initialize_logger, :cacheable, :cacheable=, :initialize_cacheable, :feature_store, :feature_store=, :user_features_storage, :user_features_storage=,
29
31
  :feature_store_live_fetching, :feature_store_live_fetching=
30
-
32
+
31
33
  def configuration
32
34
  @configuration ||= Configuration.new
33
35
  end
@@ -86,23 +88,20 @@ module AbstractFeatureBranch
86
88
  merge(redis_overrides)
87
89
  end
88
90
 
89
- def redis_per_user_features
90
- @redis_per_user_features ||= load_redis_per_user_features
91
+ def redis_scoped_features
92
+ @redis_scoped_features ||= load_redis_scoped_features
91
93
  end
92
- def load_redis_per_user_features
93
- @redis_per_user_features = {}
94
- return @redis_per_user_features if AbstractFeatureBranch.configuration.feature_store_live_fetching?
94
+ def load_redis_scoped_features
95
+ @redis_scoped_features = {}
96
+ return @redis_scoped_features if AbstractFeatureBranch.configuration.feature_store_live_fetching?
95
97
 
96
98
  @environment_features.each do |environment, features|
97
99
  features.each do |feature, value|
98
- if value == 'per_user'
100
+ if SCOPED_SPECIAL_VALUES.include?(value.to_s.downcase)
99
101
  normalized_feature_name = feature.to_s.downcase
100
- @redis_per_user_features[normalized_feature_name] ||= []
102
+ @redis_scoped_features[normalized_feature_name] ||= []
101
103
  begin
102
- per_user_feature_user_ids = AbstractFeatureBranch.
103
- user_features_storage.
104
- smembers("#{AbstractFeatureBranch::ENV_FEATURE_PREFIX}#{normalized_feature_name}")
105
- @redis_per_user_features[normalized_feature_name] += per_user_feature_user_ids
104
+ @redis_scoped_features[normalized_feature_name] += scopes_for_feature(normalized_feature_name)
106
105
  rescue Exception => error
107
106
  AbstractFeatureBranch.logger.error "AbstractFeatureBranch encountered an error in retrieving Per-User values for feature \"#{normalized_feature_name}\"! Defaulting to no values...\n\nError: #{error.full_message}\n\n"
108
107
  nil
@@ -111,7 +110,7 @@ module AbstractFeatureBranch
111
110
  end
112
111
  end
113
112
 
114
- @redis_per_user_features
113
+ @redis_scoped_features
115
114
  end
116
115
 
117
116
  def application_features
@@ -124,7 +123,7 @@ module AbstractFeatureBranch
124
123
  AbstractFeatureBranch.load_features
125
124
  AbstractFeatureBranch.load_local_features
126
125
  AbstractFeatureBranch.load_environment_features(application_environment)
127
- AbstractFeatureBranch.load_redis_per_user_features
126
+ AbstractFeatureBranch.load_redis_scoped_features
128
127
  end
129
128
  def unload_application_features
130
129
  @redis_overrides = nil
@@ -132,7 +131,7 @@ module AbstractFeatureBranch
132
131
  @features = nil
133
132
  @local_features = nil
134
133
  @environment_features = nil
135
- @redis_per_user_features = nil
134
+ @redis_scoped_features = nil
136
135
  end
137
136
 
138
137
  def cacheable?
@@ -141,6 +140,10 @@ module AbstractFeatureBranch
141
140
  value
142
141
  end
143
142
 
143
+ def scoped_value?(value)
144
+ SCOPED_SPECIAL_VALUES.include?(value.to_s.downcase)
145
+ end
146
+
144
147
  # Sets feature value (true or false) in storage (e.g. Redis client)
145
148
  def set_store_feature(feature, value)
146
149
  raise 'Feature storage (e.g. Redis) is not setup!' if feature_store.nil?
@@ -161,7 +164,7 @@ module AbstractFeatureBranch
161
164
  value = feature_store.hget(REDIS_HKEY, matching_feature) if matching_feature
162
165
  end
163
166
  return nil if value.nil?
164
- return 'per_user' if value.to_s.downcase == 'per_user'
167
+ return VALUE_SCOPED if scoped_value?(value)
165
168
  value.to_s.downcase == 'true'
166
169
  end
167
170
 
@@ -186,16 +189,34 @@ module AbstractFeatureBranch
186
189
  end
187
190
  end
188
191
 
189
- def toggle_features_for_user(user_id, features)
192
+ def toggle_features_for_scope(scope, features)
190
193
  features.each do |name, value|
191
194
  if value
192
- feature_store.sadd("#{ENV_FEATURE_PREFIX}#{name.to_s.downcase}", user_id)
195
+ feature_store.sadd("#{ENV_FEATURE_PREFIX}#{name.to_s.downcase}", scope)
193
196
  else
194
- feature_store.srem("#{ENV_FEATURE_PREFIX}#{name.to_s.downcase}", user_id)
197
+ feature_store.srem("#{ENV_FEATURE_PREFIX}#{name.to_s.downcase}", scope)
195
198
  end
196
199
  end
197
200
  end
198
-
201
+ alias toggle_features_for_user toggle_features_for_scope
202
+
203
+ def toggled_features_for_scope(scope)
204
+ AbstractFeatureBranch.feature_store.keys.select do |key|
205
+ key.start_with?(AbstractFeatureBranch::ENV_FEATURE_PREFIX)
206
+ end.map do |key|
207
+ feature = key.sub(AbstractFeatureBranch::ENV_FEATURE_PREFIX, '')
208
+ end.select do |feature|
209
+ scopes_for_feature(feature).include?(scope.to_s)
210
+ end
211
+ end
212
+
213
+ def scopes_for_feature(feature)
214
+ normalized_feature_name = feature.to_s.downcase
215
+ AbstractFeatureBranch.
216
+ feature_store.
217
+ smembers("#{AbstractFeatureBranch::ENV_FEATURE_PREFIX}#{normalized_feature_name}")
218
+ end
219
+
199
220
  private
200
221
 
201
222
  def load_specific_features(features_hash, extension)
@@ -219,7 +240,7 @@ module AbstractFeatureBranch
219
240
  hash_values = hash.map do |k, v|
220
241
  normalized_value = v.to_s.downcase
221
242
  boolean_value = normalized_value == 'true'
222
- new_value = normalized_value == 'per_user' ? 'per_user' : boolean_value
243
+ new_value = scoped_value?(normalized_value) ? VALUE_SCOPED : boolean_value
223
244
  [k, new_value]
224
245
  end
225
246
  Hash[hash_values]
@@ -1,20 +1,20 @@
1
1
  class Object
2
2
  class << self
3
3
  raise 'Abstract feature branch conflicts with another Ruby library having Object::feature_branch' if respond_to?(:feature_branch)
4
- def feature_branch(feature_name, user_id = nil, &feature_work)
5
- if feature_enabled?(feature_name, user_id)
4
+ def feature_branch(feature_name, scope = nil, &feature_work)
5
+ if feature_enabled?(feature_name, scope)
6
6
  feature_work.call
7
7
  end
8
8
  end
9
9
 
10
10
  raise 'Abstract feature branch conflicts with another Ruby library having Object::feature_enabled?' if respond_to?(:feature_enabled?)
11
- def feature_enabled?(feature_name, user_id = nil)
11
+ def feature_enabled?(feature_name, scope = nil)
12
12
  normalized_feature_name = feature_name.to_s.downcase
13
13
 
14
14
  redis_override_value = feature_enabled_reddis_override_value(normalized_feature_name)
15
15
  value = !redis_override_value.nil? ? redis_override_value : AbstractFeatureBranch.application_features[normalized_feature_name]
16
- if value == 'per_user'
17
- value = !user_id.nil? && feature_enabled_per_user_value(feature_name, user_id)
16
+ if AbstractFeatureBranch.scoped_value?(value)
17
+ value = !scope.nil? && feature_enabled_scoped_value(feature_name, scope)
18
18
  end
19
19
  value
20
20
  end
@@ -33,28 +33,28 @@ class Object
33
33
  end
34
34
  end
35
35
 
36
- raise 'Abstract feature branch conflicts with another Ruby library having Object::feature_enabled_per_user_value' if Object.new.respond_to?(:feature_enabled_per_user_value, true)
37
- def feature_enabled_per_user_value(normalized_feature_name, user_id)
36
+ raise 'Abstract feature branch conflicts with another Ruby library having Object::feature_enabled_scoped_value' if Object.new.respond_to?(:feature_enabled_scoped_value, true)
37
+ def feature_enabled_scoped_value(normalized_feature_name, scope)
38
38
  if AbstractFeatureBranch.configuration.feature_store_live_fetching?
39
39
  begin
40
- AbstractFeatureBranch.user_features_storage.sismember("#{AbstractFeatureBranch::ENV_FEATURE_PREFIX}#{normalized_feature_name}", user_id)
40
+ AbstractFeatureBranch.feature_store.sismember("#{AbstractFeatureBranch::ENV_FEATURE_PREFIX}#{normalized_feature_name}", scope)
41
41
  rescue Exception => error
42
- AbstractFeatureBranch.logger.error "AbstractFeatureBranch encountered an error in retrieving Per-User value for feature \"#{normalized_feature_name}\" and user_id #{user_id}! Defaulting to nil value...\n\nError: #{error.full_message}\n\n"
42
+ AbstractFeatureBranch.logger.error "AbstractFeatureBranch encountered an error in retrieving Per-User value for feature \"#{normalized_feature_name}\" and scope #{scope}! Defaulting to nil value...\n\nError: #{error.full_message}\n\n"
43
43
  nil
44
44
  end
45
45
  else
46
- AbstractFeatureBranch.redis_per_user_features[normalized_feature_name]&.include?(user_id.to_s)
46
+ AbstractFeatureBranch.redis_scoped_features[normalized_feature_name]&.include?(scope.to_s)
47
47
  end
48
48
  end
49
49
  end
50
50
 
51
51
  raise 'Abstract feature branch conflicts with another Ruby library having Object#feature_branch' if Object.new.respond_to?(:feature_branch)
52
- def feature_branch(feature_name, user_id = nil, &feature_work)
53
- Object.feature_branch(feature_name.to_s, user_id, &feature_work)
52
+ def feature_branch(feature_name, scope = nil, &feature_work)
53
+ Object.feature_branch(feature_name.to_s, scope, &feature_work)
54
54
  end
55
55
 
56
56
  raise 'Abstract feature branch conflicts with another Ruby library having Object#feature_enabled?' if Object.new.respond_to?(:feature_enabled?)
57
- def feature_enabled?(feature_name, user_id = nil)
58
- Object.feature_enabled?(feature_name.to_s, user_id)
57
+ def feature_enabled?(feature_name, scope = nil)
58
+ Object.feature_enabled?(feature_name.to_s, scope)
59
59
  end
60
60
  end
@@ -9,7 +9,7 @@
9
9
 
10
10
  # Enable live fetching of feature configuration from storage system (Redis), to update features without app/server restart.
11
11
  # false by default to only load features on app/server start for faster performance (requires restart on change)
12
- # This also affects per-user feature loading from storage system (Redis), pre-caching user IDs for features on app/server
12
+ # This also affects scoped feature loading from storage system (Redis), pre-caching scope IDs for features on app/server
13
13
  # restart when false, for better performance.
14
14
  AbstractFeatureBranch.feature_store_live_fetching = false
15
15
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: abstract_feature_branch
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-05 00:00:00.000000000 Z
11
+ date: 2023-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: deep_merge
@@ -164,8 +164,9 @@ post_install_message: "\nRails-only post-install instructions:\n\n1) Run the fol
164
164
  following line to Gemfile above abstract_feature_branch:\n\ngem 'redis', '~> 5.0.5'\n\nAfterwards,
165
165
  run:\n\nbundle\n\n5) Optionally, customize configuration in config/initializers/abstract_feature_branch.rb\n\n(can
166
166
  be useful for changing location of feature files in Rails application,\nconfiguring
167
- Redis with a Redis or ConnectionPool instance to use for overrides and per-user
168
- feature enablement,\nand/or troubleshooting specific Rails environment feature configurations)\n\n"
167
+ Redis with a Redis or ConnectionPool instance to use for overrides and scoped feature
168
+ enablement (e.g. per-user),\nand/or troubleshooting specific Rails environment feature
169
+ configurations)\n\n"
169
170
  rdoc_options: []
170
171
  require_paths:
171
172
  - lib