abstract_feature_branch 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +15 -15
- data/VERSION +1 -1
- data/abstract_feature_branch.gemspec +4 -4
- data/lib/abstract_feature_branch.rb +26 -19
- data/lib/ext/feature_branch.rb +14 -14
- data/lib/generators/templates/config/initializers/abstract_feature_branch.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0240cf2db5b90b0e267e2655cdc6492dc601983959d5438f6e8489ab2eb2c93b
|
4
|
+
data.tar.gz: 17a136533fdc8dc4332428d302581aa154463272a0a3dff94ca43d3a076a85b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf380b5f6c9fd78ea192887bad5628a8632e26ade45636ee4ee10028efa69e3413c92d19748349e21421bed4666ac72c7b88a3ab8979be97621075a4abc36f0e
|
7
|
+
data.tar.gz: 441c92f87e112691be34ee9b703e039a7a368b6a7e4d0e082242892dc14907472e3892d3b81a95378eee8a70197e12b31daff631e017772a00d60b0ffce28e24
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 1.5.0
|
4
|
+
|
5
|
+
- 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)
|
6
|
+
- Rename `toggle_features_for_user` to `toggle_features_for_scope`
|
7
|
+
|
3
8
|
## 1.4.0
|
4
9
|
|
5
10
|
- 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.
|
1
|
+
# Abstract Feature Branch 1.5.0
|
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)
|
@@ -38,17 +38,17 @@ Setup
|
|
38
38
|
### Rails Application Use
|
39
39
|
|
40
40
|
1. Configure Rubygem
|
41
|
-
- With `rails` between `~> 7.0` and `~> 2.0`: Add the following to Gemfile <pre>gem 'abstract_feature_branch', '~> 1.
|
42
|
-
- With `rails` `~> 2.0` only: Add the following to config/environment.rb <pre>config.gem 'abstract_feature_branch', :version => '1.
|
41
|
+
- With `rails` between `~> 7.0` and `~> 2.0`: Add the following to Gemfile <pre>gem 'abstract_feature_branch', '~> 1.5.0'</pre>
|
42
|
+
- With `rails` `~> 2.0` only: Add the following to config/environment.rb <pre>config.gem 'abstract_feature_branch', :version => '1.5.0'</pre>
|
43
43
|
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
44
|
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 [
|
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 [scoped feature enablement](#scoped-feature-enablement) (e.g. per-user), or troubleshooting a specific Rails environment feature configuration)
|
46
46
|
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
47
|
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
48
|
|
49
49
|
### Ruby Application General Use
|
50
50
|
|
51
|
-
1. <pre>gem install abstract_feature_branch -v 1.
|
51
|
+
1. <pre>gem install abstract_feature_branch -v 1.5.0</pre>
|
52
52
|
2. Add code <code>require 'abstract_feature_branch'</code>
|
53
53
|
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
54
|
4. [Optional] Create <code>config/features.local.yml</code> under <code>AbstractFeatureBranch.application_root</code> (more details under [**instructions**](#instructions))
|
@@ -58,8 +58,8 @@ Setup
|
|
58
58
|
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
59
|
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
60
|
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 [
|
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
|
61
|
+
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)
|
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 scoped feature enablement too, pre-caching all user IDs for features on app/server restart when disabled)
|
63
63
|
|
64
64
|
Instructions
|
65
65
|
------------
|
@@ -84,7 +84,7 @@ enabled (true) or disabled (false) per environment (e.g. production).
|
|
84
84
|
> feature1: true
|
85
85
|
> feature2: true
|
86
86
|
> feature3: false
|
87
|
-
> feature4:
|
87
|
+
> feature4: scoped
|
88
88
|
>
|
89
89
|
> development:
|
90
90
|
> <<: *defaults
|
@@ -131,17 +131,17 @@ Note that <code>feature_enabled?</code> returns false if the feature is disabled
|
|
131
131
|
> AbstractFeatureBranch.environment_features('development')
|
132
132
|
> # => {"feature1"=>true, "feature2"=>false, "feature3"=>false, "feature4"=>true}
|
133
133
|
|
134
|
-
###
|
134
|
+
### Scoped Feature Enablement
|
135
135
|
|
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>
|
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>scoped</code>, and then toggling features for specific users (or other entities).
|
137
137
|
|
138
|
-
1. Use <code>
|
138
|
+
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
139
|
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
140
|
|
141
|
-
>
|
142
|
-
> AbstractFeatureBranch.
|
141
|
+
> scope_id = current_user.email
|
142
|
+
> AbstractFeatureBranch.toggle_features_for_scope(scope_id, :feature1 => true, :feature2 => false, :feature3 => true, :feature5 => true)
|
143
143
|
|
144
|
-
Use alternate version of <code>feature_branch</code> and <code>feature_enabled?</code> passing extra <code>
|
144
|
+
Use alternate version of <code>feature_branch</code> and <code>feature_enabled?</code> passing extra <code>scope_id</code> argument
|
145
145
|
|
146
146
|
Examples:
|
147
147
|
|
@@ -168,7 +168,7 @@ Examples:
|
|
168
168
|
Note:
|
169
169
|
|
170
170
|
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
|
171
|
+
like features.local.yml or environment variable overrides), then it overrides toggled scoped feature restrictions, becoming
|
172
172
|
enabled or disabled globally.
|
173
173
|
|
174
174
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.5.0
|
@@ -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.
|
5
|
+
# stub: abstract_feature_branch 1.5.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "abstract_feature_branch".freeze
|
9
|
-
s.version = "1.
|
9
|
+
s.version = "1.5.0"
|
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-
|
14
|
+
s.date = "2023-02-12"
|
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
|
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,23 @@ module AbstractFeatureBranch
|
|
86
88
|
merge(redis_overrides)
|
87
89
|
end
|
88
90
|
|
89
|
-
def
|
90
|
-
@
|
91
|
+
def redis_scoped_features
|
92
|
+
@redis_scoped_features ||= load_redis_scoped_features
|
91
93
|
end
|
92
|
-
def
|
93
|
-
@
|
94
|
-
return @
|
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
|
100
|
+
if SCOPED_SPECIAL_VALUES.include?(value.to_s.downcase)
|
99
101
|
normalized_feature_name = feature.to_s.downcase
|
100
|
-
@
|
102
|
+
@redis_scoped_features[normalized_feature_name] ||= []
|
101
103
|
begin
|
102
|
-
|
103
|
-
|
104
|
+
scoped_feature_scope_ids = AbstractFeatureBranch.
|
105
|
+
feature_store.
|
104
106
|
smembers("#{AbstractFeatureBranch::ENV_FEATURE_PREFIX}#{normalized_feature_name}")
|
105
|
-
@
|
107
|
+
@redis_scoped_features[normalized_feature_name] += scoped_feature_scope_ids
|
106
108
|
rescue Exception => error
|
107
109
|
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
110
|
nil
|
@@ -111,7 +113,7 @@ module AbstractFeatureBranch
|
|
111
113
|
end
|
112
114
|
end
|
113
115
|
|
114
|
-
@
|
116
|
+
@redis_scoped_features
|
115
117
|
end
|
116
118
|
|
117
119
|
def application_features
|
@@ -124,7 +126,7 @@ module AbstractFeatureBranch
|
|
124
126
|
AbstractFeatureBranch.load_features
|
125
127
|
AbstractFeatureBranch.load_local_features
|
126
128
|
AbstractFeatureBranch.load_environment_features(application_environment)
|
127
|
-
AbstractFeatureBranch.
|
129
|
+
AbstractFeatureBranch.load_redis_scoped_features
|
128
130
|
end
|
129
131
|
def unload_application_features
|
130
132
|
@redis_overrides = nil
|
@@ -132,7 +134,7 @@ module AbstractFeatureBranch
|
|
132
134
|
@features = nil
|
133
135
|
@local_features = nil
|
134
136
|
@environment_features = nil
|
135
|
-
@
|
137
|
+
@redis_scoped_features = nil
|
136
138
|
end
|
137
139
|
|
138
140
|
def cacheable?
|
@@ -141,6 +143,10 @@ module AbstractFeatureBranch
|
|
141
143
|
value
|
142
144
|
end
|
143
145
|
|
146
|
+
def scoped_value?(value)
|
147
|
+
SCOPED_SPECIAL_VALUES.include?(value.to_s.downcase)
|
148
|
+
end
|
149
|
+
|
144
150
|
# Sets feature value (true or false) in storage (e.g. Redis client)
|
145
151
|
def set_store_feature(feature, value)
|
146
152
|
raise 'Feature storage (e.g. Redis) is not setup!' if feature_store.nil?
|
@@ -161,7 +167,7 @@ module AbstractFeatureBranch
|
|
161
167
|
value = feature_store.hget(REDIS_HKEY, matching_feature) if matching_feature
|
162
168
|
end
|
163
169
|
return nil if value.nil?
|
164
|
-
return
|
170
|
+
return VALUE_SCOPED if scoped_value?(value)
|
165
171
|
value.to_s.downcase == 'true'
|
166
172
|
end
|
167
173
|
|
@@ -186,15 +192,16 @@ module AbstractFeatureBranch
|
|
186
192
|
end
|
187
193
|
end
|
188
194
|
|
189
|
-
def
|
195
|
+
def toggle_features_for_scope(scope_id, features)
|
190
196
|
features.each do |name, value|
|
191
197
|
if value
|
192
|
-
feature_store.sadd("#{ENV_FEATURE_PREFIX}#{name.to_s.downcase}",
|
198
|
+
feature_store.sadd("#{ENV_FEATURE_PREFIX}#{name.to_s.downcase}", scope_id)
|
193
199
|
else
|
194
|
-
feature_store.srem("#{ENV_FEATURE_PREFIX}#{name.to_s.downcase}",
|
200
|
+
feature_store.srem("#{ENV_FEATURE_PREFIX}#{name.to_s.downcase}", scope_id)
|
195
201
|
end
|
196
202
|
end
|
197
203
|
end
|
204
|
+
alias toggle_features_for_user toggle_features_for_scope
|
198
205
|
|
199
206
|
private
|
200
207
|
|
@@ -219,7 +226,7 @@ module AbstractFeatureBranch
|
|
219
226
|
hash_values = hash.map do |k, v|
|
220
227
|
normalized_value = v.to_s.downcase
|
221
228
|
boolean_value = normalized_value == 'true'
|
222
|
-
new_value = normalized_value
|
229
|
+
new_value = scoped_value?(normalized_value) ? VALUE_SCOPED : boolean_value
|
223
230
|
[k, new_value]
|
224
231
|
end
|
225
232
|
Hash[hash_values]
|
data/lib/ext/feature_branch.rb
CHANGED
@@ -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,
|
5
|
-
if feature_enabled?(feature_name,
|
4
|
+
def feature_branch(feature_name, scope_id = nil, &feature_work)
|
5
|
+
if feature_enabled?(feature_name, scope_id)
|
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,
|
11
|
+
def feature_enabled?(feature_name, scope_id = 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
|
17
|
-
value = !
|
16
|
+
if AbstractFeatureBranch.scoped_value?(value)
|
17
|
+
value = !scope_id.nil? && feature_enabled_scoped_value(feature_name, scope_id)
|
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::
|
37
|
-
def
|
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_id)
|
38
38
|
if AbstractFeatureBranch.configuration.feature_store_live_fetching?
|
39
39
|
begin
|
40
|
-
AbstractFeatureBranch.
|
40
|
+
AbstractFeatureBranch.feature_store.sismember("#{AbstractFeatureBranch::ENV_FEATURE_PREFIX}#{normalized_feature_name}", scope_id)
|
41
41
|
rescue Exception => error
|
42
|
-
AbstractFeatureBranch.logger.error "AbstractFeatureBranch encountered an error in retrieving Per-User value for feature \"#{normalized_feature_name}\" and
|
42
|
+
AbstractFeatureBranch.logger.error "AbstractFeatureBranch encountered an error in retrieving Per-User value for feature \"#{normalized_feature_name}\" and scope_id #{scope_id}! Defaulting to nil value...\n\nError: #{error.full_message}\n\n"
|
43
43
|
nil
|
44
44
|
end
|
45
45
|
else
|
46
|
-
AbstractFeatureBranch.
|
46
|
+
AbstractFeatureBranch.redis_scoped_features[normalized_feature_name]&.include?(scope_id.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,
|
53
|
-
Object.feature_branch(feature_name.to_s,
|
52
|
+
def feature_branch(feature_name, scope_id = nil, &feature_work)
|
53
|
+
Object.feature_branch(feature_name.to_s, scope_id, &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,
|
58
|
-
Object.feature_enabled?(feature_name.to_s,
|
57
|
+
def feature_enabled?(feature_name, scope_id = nil)
|
58
|
+
Object.feature_enabled?(feature_name.to_s, scope_id)
|
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
|
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
|
+
version: 1.5.0
|
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-
|
11
|
+
date: 2023-02-12 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
|
168
|
-
|
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
|