feature_guard 0.1.2 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +19 -0
- data/feature_guard.gemspec +2 -2
- data/lib/feature_guard.rb +20 -1
- data/lib/feature_guard/guard.rb +6 -18
- data/lib/feature_guard/version.rb +1 -1
- data/spec/feature_guard/guard_spec.rb +4 -4
- data/spec/feature_guard_spec.rb +42 -2
- metadata +29 -26
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ac443b4c9e81abf91976dd0bc5ab802b7ea2b11f
|
4
|
+
data.tar.gz: 5e95d160aa879d4867bd33274f035c0690c1ff22
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 435e28575454290cc3da192e53a3a84c8f5c008ee504a11fe9d195774c1562e26ad3f1dee945191064d6b3c4e18ac5746c8b29813e6469aee76b6e59992005b2
|
7
|
+
data.tar.gz: 8d7898ad67a3b76ad8aa33767b12a1f3113114001a30798d6e5ed58a84a352fb4ad67bc60e5910170ad71f815a422d713ad352467a60e1a25bcd2869e691600e
|
data/README.md
CHANGED
@@ -60,6 +60,25 @@ FeatureGuard.allow? :my_feature
|
|
60
60
|
|
61
61
|
The optional second argument to`.allow?` can be of any type (e.g., user ID or name or even an object). It is hashed with the feature name to create a reproducible numeric value for checking whether to return true or false based on the current ramp-up value. With no second argument, `.allow?` uses a new random value on every call.
|
62
62
|
|
63
|
+
To retrieve information on all binary-enabled/disabled features, use `.all_flags`:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
FeatureGuard.enable :my_feature
|
67
|
+
FeatureGuard.enable :another_feature
|
68
|
+
FeatureGuard.toggle :my_feature
|
69
|
+
FeatureGuard.all_flags
|
70
|
+
# {"my_feature"=>false, "another_feature"=>true}
|
71
|
+
```
|
72
|
+
|
73
|
+
To retrieve information on all ramp values, use `.all_ramps`:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
FeatureGuard.set_ramp :my_feature, 50
|
77
|
+
FeatureGuard.set_ramp :another_feature, 30
|
78
|
+
FeatureGuard.all_ramps
|
79
|
+
# {"my_feature"=>50.0, "another_feature"=>30.0}
|
80
|
+
```
|
81
|
+
|
63
82
|
## Configuration
|
64
83
|
|
65
84
|
Optionally change the Redis client with:
|
data/feature_guard.gemspec
CHANGED
@@ -3,7 +3,7 @@ require File.expand_path('../lib/feature_guard/version', __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["Ted Dumitrescu"]
|
6
|
-
gem.email = ["
|
6
|
+
gem.email = ["webdev@cmme.org"]
|
7
7
|
gem.summary = %q{Simple Redis-based feature-flagging}
|
8
8
|
gem.description = <<end
|
9
9
|
Feature flags made simple: turn code on or off with Redis controls, allowing
|
@@ -22,5 +22,5 @@ end
|
|
22
22
|
gem.add_dependency "redis", "~> 3.0"
|
23
23
|
|
24
24
|
gem.add_development_dependency "fakeredis", "~> 0.4"
|
25
|
-
gem.add_development_dependency "rspec", "~> 2.
|
25
|
+
gem.add_development_dependency "rspec", "~> 2.99"
|
26
26
|
end
|
data/lib/feature_guard.rb
CHANGED
@@ -7,11 +7,30 @@ module FeatureGuard
|
|
7
7
|
class << self
|
8
8
|
attr_writer :redis
|
9
9
|
|
10
|
+
def all_flags
|
11
|
+
redis.hgetall(flags_hkey).keys.inject({}) { |h, f| h[f] = enabled? f; h }
|
12
|
+
end
|
13
|
+
|
14
|
+
def all_ramps
|
15
|
+
redis.hgetall(ramps_hkey).keys.inject({}) { |h, f| h[f] = ramp_val f; h }
|
16
|
+
end
|
17
|
+
|
18
|
+
def flags_hkey
|
19
|
+
"featureguard_flags"
|
20
|
+
end
|
21
|
+
|
22
|
+
def ramps_hkey
|
23
|
+
"featureguard_ramps"
|
24
|
+
end
|
25
|
+
|
10
26
|
def redis
|
11
27
|
@redis ||= Redis.new
|
12
28
|
end
|
13
29
|
|
14
|
-
[
|
30
|
+
[
|
31
|
+
:allow?, :bump_ramp, :disable, :enable, :toggle, :enabled?,
|
32
|
+
:ramp_val, :set_ramp
|
33
|
+
].each do |mname|
|
15
34
|
define_method(mname) do |key, *args|
|
16
35
|
Guard.new(key).send(mname, *args)
|
17
36
|
end
|
data/lib/feature_guard/guard.rb
CHANGED
@@ -7,15 +7,15 @@ module FeatureGuard; class Guard
|
|
7
7
|
|
8
8
|
# binary flag methods (enabled/disabled)
|
9
9
|
def disable
|
10
|
-
redis.
|
10
|
+
redis.hset(FeatureGuard.flags_hkey, feature_name, 0)
|
11
11
|
end
|
12
12
|
|
13
13
|
def enable
|
14
|
-
redis.
|
14
|
+
redis.hset(FeatureGuard.flags_hkey, feature_name, 1)
|
15
15
|
end
|
16
16
|
|
17
17
|
def enabled?
|
18
|
-
redis.
|
18
|
+
redis.hget(FeatureGuard.flags_hkey, feature_name).tap { |v| return (!v.nil? && v.to_i > 0) }
|
19
19
|
rescue
|
20
20
|
false
|
21
21
|
end
|
@@ -35,7 +35,7 @@ module FeatureGuard; class Guard
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def ramp_val
|
38
|
-
redis.
|
38
|
+
redis.hget(FeatureGuard.ramps_hkey, feature_name).to_f
|
39
39
|
end
|
40
40
|
|
41
41
|
def set_ramp(new_val)
|
@@ -43,26 +43,14 @@ module FeatureGuard; class Guard
|
|
43
43
|
new_val = 100.0 if new_val > 100.0
|
44
44
|
new_val = 0.0 if new_val < 0.0
|
45
45
|
|
46
|
-
redis.
|
46
|
+
redis.hset(FeatureGuard.ramps_hkey, feature_name, new_val)
|
47
47
|
new_val
|
48
48
|
end
|
49
49
|
|
50
50
|
private
|
51
51
|
|
52
|
-
def feature_key
|
53
|
-
@feature_key ||= feature_name.to_s.split.join('_')
|
54
|
-
end
|
55
|
-
|
56
|
-
def flag_key
|
57
|
-
@flag_key ||= "fgf_#{feature_key}"
|
58
|
-
end
|
59
|
-
|
60
|
-
def ramp_key
|
61
|
-
@ramp_key ||= "fgr_#{feature_key}"
|
62
|
-
end
|
63
|
-
|
64
52
|
def hashed_val(s)
|
65
|
-
(Digest::MD5.hexdigest("#{
|
53
|
+
(Digest::MD5.hexdigest("#{feature_name}_#{s}").to_i(16) % 10000).to_f / 100.0
|
66
54
|
end
|
67
55
|
|
68
56
|
def random_val
|
@@ -12,7 +12,7 @@ describe FeatureGuard::Guard do
|
|
12
12
|
|
13
13
|
it 'uses a random value' do
|
14
14
|
expect(guard).to receive(:random_val).and_return(29.9)
|
15
|
-
expect(subject).to
|
15
|
+
expect(subject).to eq(true)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -21,7 +21,7 @@ describe FeatureGuard::Guard do
|
|
21
21
|
|
22
22
|
it 'hashes the value together with the feature name' do
|
23
23
|
expect(guard).to receive(:hashed_val).and_return(30.1)
|
24
|
-
expect(subject).to
|
24
|
+
expect(subject).to eq(false)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
@@ -42,13 +42,13 @@ describe FeatureGuard::Guard do
|
|
42
42
|
subject { guard.enabled? }
|
43
43
|
|
44
44
|
context 'for a non-existent flag' do
|
45
|
-
it {
|
45
|
+
it { is_expected.to eq(false) }
|
46
46
|
end
|
47
47
|
|
48
48
|
context 'for an enabled flag' do
|
49
49
|
before { guard.enable }
|
50
50
|
|
51
|
-
it {
|
51
|
+
it { is_expected.to eq(true) }
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
data/spec/feature_guard_spec.rb
CHANGED
@@ -41,13 +41,53 @@ describe FeatureGuard do
|
|
41
41
|
subject { FeatureGuard.enabled? feature }
|
42
42
|
|
43
43
|
context 'for a non-existent flag' do
|
44
|
-
it {
|
44
|
+
it { is_expected.to eq(false) }
|
45
45
|
end
|
46
46
|
|
47
47
|
context 'when the Redis client blows up or is non-existent' do
|
48
48
|
before { FeatureGuard.stub(redis: nil) }
|
49
49
|
|
50
|
-
it {
|
50
|
+
it { is_expected.to eq(false) }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe '.all_flags' do
|
55
|
+
subject { FeatureGuard.all_flags }
|
56
|
+
|
57
|
+
it 'returns information on enabled flags' do
|
58
|
+
expect {
|
59
|
+
FeatureGuard.enable feature
|
60
|
+
}.to change {
|
61
|
+
FeatureGuard.all_flags
|
62
|
+
}.from({}).to({feature.to_s => true})
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'returns information on multiple flags' do
|
66
|
+
FeatureGuard.enable feature
|
67
|
+
FeatureGuard.enable 'another feature'
|
68
|
+
expect(subject.keys.size).to eq(2)
|
69
|
+
expect(subject.keys).to include(feature.to_s)
|
70
|
+
expect(subject.keys).to include('another feature')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe '.all_ramps' do
|
75
|
+
subject { FeatureGuard.all_ramps }
|
76
|
+
|
77
|
+
it 'returns information on ramped flags' do
|
78
|
+
expect {
|
79
|
+
FeatureGuard.set_ramp feature, 50
|
80
|
+
}.to change {
|
81
|
+
FeatureGuard.all_ramps
|
82
|
+
}.from({}).to({feature.to_s => 50.0})
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'returns information on multiple flags' do
|
86
|
+
FeatureGuard.set_ramp feature, 99
|
87
|
+
FeatureGuard.set_ramp 'another feature', 33
|
88
|
+
expect(subject.keys.size).to eq(2)
|
89
|
+
expect(subject.keys).to include(feature.to_s)
|
90
|
+
expect(subject.keys).to include('another feature')
|
51
91
|
end
|
52
92
|
end
|
53
93
|
end
|
metadata
CHANGED
@@ -1,57 +1,62 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feature_guard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Ted Dumitrescu
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2015-02-03 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: redis
|
16
|
-
requirement:
|
17
|
-
none: false
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '3.0'
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
|
-
version_requirements:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
25
27
|
- !ruby/object:Gem::Dependency
|
26
28
|
name: fakeredis
|
27
|
-
requirement:
|
28
|
-
none: false
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
29
30
|
requirements:
|
30
31
|
- - ~>
|
31
32
|
- !ruby/object:Gem::Version
|
32
33
|
version: '0.4'
|
33
34
|
type: :development
|
34
35
|
prerelease: false
|
35
|
-
version_requirements:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.4'
|
36
41
|
- !ruby/object:Gem::Dependency
|
37
42
|
name: rspec
|
38
|
-
requirement:
|
39
|
-
none: false
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
40
44
|
requirements:
|
41
45
|
- - ~>
|
42
46
|
- !ruby/object:Gem::Version
|
43
|
-
version: '2.
|
47
|
+
version: '2.99'
|
44
48
|
type: :development
|
45
49
|
prerelease: false
|
46
|
-
version_requirements:
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.99'
|
55
|
+
description: |
|
56
|
+
Feature flags made simple: turn code on or off with Redis controls, allowing
|
50
57
|
plain enabled/disabled states as well as finer-grained percentage-based control.
|
51
|
-
|
52
|
-
'
|
53
58
|
email:
|
54
|
-
-
|
59
|
+
- webdev@cmme.org
|
55
60
|
executables: []
|
56
61
|
extensions: []
|
57
62
|
extra_rdoc_files: []
|
@@ -72,30 +77,28 @@ files:
|
|
72
77
|
homepage: https://github.com/tdumitrescu/feature_guard
|
73
78
|
licenses:
|
74
79
|
- MIT
|
80
|
+
metadata: {}
|
75
81
|
post_install_message:
|
76
82
|
rdoc_options: []
|
77
83
|
require_paths:
|
78
84
|
- lib
|
79
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
-
none: false
|
81
86
|
requirements:
|
82
|
-
- -
|
87
|
+
- - '>='
|
83
88
|
- !ruby/object:Gem::Version
|
84
89
|
version: '0'
|
85
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
-
none: false
|
87
91
|
requirements:
|
88
|
-
- -
|
92
|
+
- - '>='
|
89
93
|
- !ruby/object:Gem::Version
|
90
94
|
version: '0'
|
91
95
|
requirements: []
|
92
96
|
rubyforge_project:
|
93
|
-
rubygems_version:
|
97
|
+
rubygems_version: 2.0.14
|
94
98
|
signing_key:
|
95
|
-
specification_version:
|
99
|
+
specification_version: 4
|
96
100
|
summary: Simple Redis-based feature-flagging
|
97
101
|
test_files:
|
98
102
|
- spec/feature_guard/guard_spec.rb
|
99
103
|
- spec/feature_guard_spec.rb
|
100
104
|
- spec/spec_helper.rb
|
101
|
-
has_rdoc:
|