flipper 0.4.0 → 0.5.0
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.
- data/Guardfile +3 -8
- data/README.md +26 -38
- data/examples/percentage_of_actors.rb +17 -12
- data/examples/percentage_of_random.rb +3 -7
- data/lib/flipper.rb +8 -1
- data/lib/flipper/adapter.rb +2 -208
- data/lib/flipper/adapters/decorator.rb +9 -0
- data/lib/flipper/adapters/instrumented.rb +92 -0
- data/lib/flipper/adapters/memoizable.rb +88 -0
- data/lib/flipper/adapters/memory.rb +89 -7
- data/lib/flipper/adapters/operation_logger.rb +31 -45
- data/lib/flipper/decorator.rb +6 -0
- data/lib/flipper/dsl.rb +29 -2
- data/lib/flipper/feature.rb +83 -49
- data/lib/flipper/gate.rb +24 -41
- data/lib/flipper/gates/actor.rb +24 -24
- data/lib/flipper/gates/boolean.rb +28 -15
- data/lib/flipper/gates/group.rb +25 -34
- data/lib/flipper/gates/percentage_of_actors.rb +21 -13
- data/lib/flipper/gates/percentage_of_random.rb +20 -12
- data/lib/flipper/instrumentation/log_subscriber.rb +14 -22
- data/lib/flipper/middleware/memoizer.rb +23 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +141 -92
- data/lib/flipper/types/boolean.rb +5 -1
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/instrumented_spec.rb +92 -0
- data/spec/flipper/adapters/memoizable_spec.rb +184 -0
- data/spec/flipper/adapters/memory_spec.rb +1 -11
- data/spec/flipper/adapters/operation_logger_spec.rb +93 -0
- data/spec/flipper/dsl_spec.rb +18 -43
- data/spec/flipper/feature_spec.rb +25 -9
- data/spec/flipper/gate_spec.rb +8 -20
- data/spec/flipper/gates/actor_spec.rb +6 -14
- data/spec/flipper/gates/boolean_spec.rb +80 -13
- data/spec/flipper/gates/group_spec.rb +8 -18
- data/spec/flipper/gates/percentage_of_actors_spec.rb +12 -28
- data/spec/flipper/gates/percentage_of_random_spec.rb +6 -14
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +15 -8
- data/spec/flipper/instrumentation/metriks_subscriber_spec.rb +3 -6
- data/spec/flipper/middleware/{local_cache_spec.rb → memoizer_spec.rb} +25 -55
- data/spec/flipper/types/boolean_spec.rb +13 -3
- data/spec/flipper_spec.rb +7 -0
- data/spec/helper.rb +21 -3
- data/spec/integration_spec.rb +115 -116
- metadata +17 -27
- data/lib/flipper/adapters/memoized.rb +0 -55
- data/lib/flipper/key.rb +0 -38
- data/lib/flipper/middleware/local_cache.rb +0 -36
- data/lib/flipper/toggle.rb +0 -54
- data/lib/flipper/toggles/boolean.rb +0 -54
- data/lib/flipper/toggles/set.rb +0 -25
- data/lib/flipper/toggles/value.rb +0 -25
- data/spec/flipper/adapter_spec.rb +0 -463
- data/spec/flipper/adapters/memoized_spec.rb +0 -93
- data/spec/flipper/key_spec.rb +0 -23
- data/spec/flipper/toggle_spec.rb +0 -22
- data/spec/flipper/toggles/boolean_spec.rb +0 -40
- data/spec/flipper/toggles/set_spec.rb +0 -35
- data/spec/flipper/toggles/value_spec.rb +0 -55
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'flipper/adapters/memoizable'
|
3
|
+
require 'flipper/adapters/memory'
|
4
|
+
require 'flipper/spec/shared_adapter_specs'
|
5
|
+
|
6
|
+
describe Flipper::Adapters::Memoizable do
|
7
|
+
let(:features_key) { described_class::FeaturesKey }
|
8
|
+
|
9
|
+
let(:adapter) { Flipper::Adapters::Memory.new }
|
10
|
+
let(:flipper) { Flipper.new(adapter) }
|
11
|
+
let(:cache) { Thread.current[:flipper_memoize_cache] }
|
12
|
+
|
13
|
+
after do
|
14
|
+
described_class.memoize = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
subject { described_class.new(adapter) }
|
18
|
+
|
19
|
+
it_should_behave_like 'a flipper adapter'
|
20
|
+
|
21
|
+
describe "#memoize=" do
|
22
|
+
it "sets value" do
|
23
|
+
subject.memoize = true
|
24
|
+
subject.memoizing?.should be_true
|
25
|
+
|
26
|
+
subject.memoize = false
|
27
|
+
subject.memoizing?.should be_false
|
28
|
+
end
|
29
|
+
|
30
|
+
it "clears the local cache" do
|
31
|
+
subject.cache['some'] = 'thing'
|
32
|
+
subject.memoize = true
|
33
|
+
subject.cache.should be_empty
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "#memoizing?" do
|
38
|
+
it "returns true if enabled" do
|
39
|
+
subject.memoize = true
|
40
|
+
subject.memoizing?.should be_true
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns false if disabled" do
|
44
|
+
subject.memoize = false
|
45
|
+
subject.memoizing?.should be_false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#get" do
|
50
|
+
context "with memoization enabled" do
|
51
|
+
before do
|
52
|
+
subject.memoize = true
|
53
|
+
end
|
54
|
+
|
55
|
+
it "memoizes feature" do
|
56
|
+
feature = flipper[:stats]
|
57
|
+
result = subject.get(feature)
|
58
|
+
cache[feature].should be(result)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "with memoization disabled" do
|
63
|
+
before do
|
64
|
+
subject.memoize = false
|
65
|
+
end
|
66
|
+
|
67
|
+
it "returns result" do
|
68
|
+
feature = flipper[:stats]
|
69
|
+
result = subject.get(feature)
|
70
|
+
adapter_result = adapter.get(feature)
|
71
|
+
result.should eq(adapter_result)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#enable" do
|
77
|
+
context "with memoization enabled" do
|
78
|
+
before do
|
79
|
+
subject.memoize = true
|
80
|
+
end
|
81
|
+
|
82
|
+
it "unmemoizes feature" do
|
83
|
+
feature = flipper[:stats]
|
84
|
+
gate = feature.gate(:boolean)
|
85
|
+
cache[feature] = {:some => 'thing'}
|
86
|
+
subject.enable(feature, gate, flipper.bool)
|
87
|
+
cache[feature].should be_nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "with memoization disabled" do
|
92
|
+
before do
|
93
|
+
subject.memoize = false
|
94
|
+
end
|
95
|
+
|
96
|
+
it "returns result" do
|
97
|
+
feature = flipper[:stats]
|
98
|
+
gate = feature.gate(:boolean)
|
99
|
+
result = subject.enable(feature, gate, flipper.bool)
|
100
|
+
adapter_result = adapter.enable(feature, gate, flipper.bool)
|
101
|
+
result.should eq(adapter_result)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#disable" do
|
107
|
+
context "with memoization enabled" do
|
108
|
+
before do
|
109
|
+
subject.memoize = true
|
110
|
+
end
|
111
|
+
|
112
|
+
it "unmemoizes feature" do
|
113
|
+
feature = flipper[:stats]
|
114
|
+
gate = feature.gate(:boolean)
|
115
|
+
cache[feature] = {:some => 'thing'}
|
116
|
+
subject.disable(feature, gate, flipper.bool)
|
117
|
+
cache[feature].should be_nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context "with memoization disabled" do
|
122
|
+
before do
|
123
|
+
subject.memoize = false
|
124
|
+
end
|
125
|
+
|
126
|
+
it "returns result" do
|
127
|
+
feature = flipper[:stats]
|
128
|
+
gate = feature.gate(:boolean)
|
129
|
+
result = subject.disable(feature, gate, flipper.bool)
|
130
|
+
adapter_result = adapter.disable(feature, gate, flipper.bool)
|
131
|
+
result.should eq(adapter_result)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "#features" do
|
137
|
+
context "with memoization enabled" do
|
138
|
+
before do
|
139
|
+
subject.memoize = true
|
140
|
+
end
|
141
|
+
|
142
|
+
it "memoizes features" do
|
143
|
+
flipper[:stats].enable
|
144
|
+
flipper[:search].disable
|
145
|
+
result = subject.features
|
146
|
+
cache[:flipper_features].should be(result)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context "with memoization disabled" do
|
151
|
+
before do
|
152
|
+
subject.memoize = false
|
153
|
+
end
|
154
|
+
|
155
|
+
it "returns result" do
|
156
|
+
subject.features.should eq(adapter.features)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe "#add" do
|
162
|
+
context "with memoization enabled" do
|
163
|
+
before do
|
164
|
+
subject.memoize = true
|
165
|
+
end
|
166
|
+
|
167
|
+
it "unmemoizes features" do
|
168
|
+
cache[features_key] = {:some => 'thing'}
|
169
|
+
subject.add(flipper[:stats])
|
170
|
+
cache.should be_empty
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
context "with memoization disabled" do
|
175
|
+
before do
|
176
|
+
subject.memoize = false
|
177
|
+
end
|
178
|
+
|
179
|
+
it "returns result" do
|
180
|
+
subject.add(flipper[:stats]).should eq(adapter.add(flipper[:stats]))
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -3,17 +3,7 @@ require 'flipper/adapters/memory'
|
|
3
3
|
require 'flipper/spec/shared_adapter_specs'
|
4
4
|
|
5
5
|
describe Flipper::Adapters::Memory do
|
6
|
-
|
7
|
-
|
8
|
-
subject { described_class.new(source) }
|
9
|
-
|
10
|
-
def read_key(key)
|
11
|
-
source[key.to_s]
|
12
|
-
end
|
13
|
-
|
14
|
-
def write_key(key, value)
|
15
|
-
source[key.to_s] = value
|
16
|
-
end
|
6
|
+
subject { described_class.new }
|
17
7
|
|
18
8
|
it_should_behave_like 'a flipper adapter'
|
19
9
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'flipper/adapters/operation_logger'
|
3
|
+
require 'flipper/adapters/memory'
|
4
|
+
require 'flipper/spec/shared_adapter_specs'
|
5
|
+
|
6
|
+
describe Flipper::Adapters::OperationLogger do
|
7
|
+
let(:operations) { [] }
|
8
|
+
let(:adapter) { Flipper::Adapters::Memory.new }
|
9
|
+
let(:flipper) { Flipper.new(adapter) }
|
10
|
+
|
11
|
+
subject { described_class.new(adapter, operations) }
|
12
|
+
|
13
|
+
it_should_behave_like 'a flipper adapter'
|
14
|
+
|
15
|
+
describe "#get" do
|
16
|
+
before do
|
17
|
+
@feature = flipper[:stats]
|
18
|
+
@result = subject.get(@feature)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "logs operation" do
|
22
|
+
subject.count(:get).should be(1)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns result" do
|
26
|
+
@result.should eq(adapter.get(@feature))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#enable" do
|
31
|
+
before do
|
32
|
+
@feature = flipper[:stats]
|
33
|
+
@gate = @feature.gate(:boolean)
|
34
|
+
@thing = flipper.bool
|
35
|
+
@result = subject.enable(@feature, @gate, @thing)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "logs operation" do
|
39
|
+
subject.count(:enable).should be(1)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns result" do
|
43
|
+
@result.should eq(adapter.enable(@feature, @gate, @thing))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#disable" do
|
48
|
+
before do
|
49
|
+
@feature = flipper[:stats]
|
50
|
+
@gate = @feature.gate(:boolean)
|
51
|
+
@thing = flipper.bool
|
52
|
+
@result = subject.disable(@feature, @gate, @thing)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "logs operation" do
|
56
|
+
subject.count(:disable).should be(1)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns result" do
|
60
|
+
@result.should eq(adapter.disable(@feature, @gate, @thing))
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#features" do
|
65
|
+
before do
|
66
|
+
flipper[:stats].enable
|
67
|
+
@result = subject.features
|
68
|
+
end
|
69
|
+
|
70
|
+
it "logs operation" do
|
71
|
+
subject.count(:features).should be(1)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "returns result" do
|
75
|
+
@result.should eq(adapter.features)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#add" do
|
80
|
+
before do
|
81
|
+
@feature = flipper[:stats]
|
82
|
+
@result = subject.add(@feature)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "logs operation" do
|
86
|
+
subject.count(:add).should be(1)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "returns result" do
|
90
|
+
@result.should eq(adapter.add(@feature))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/spec/flipper/dsl_spec.rb
CHANGED
@@ -8,13 +8,10 @@ describe Flipper::DSL do
|
|
8
8
|
let(:source) { {} }
|
9
9
|
let(:adapter) { Flipper::Adapters::Memory.new(source) }
|
10
10
|
|
11
|
-
let(:admins_feature) { Flipper::Feature.new(:admins, adapter) }
|
12
|
-
|
13
11
|
describe "#initialize" do
|
14
|
-
it "
|
12
|
+
it "sets adapter" do
|
15
13
|
dsl = described_class.new(adapter)
|
16
|
-
dsl.adapter.
|
17
|
-
dsl.adapter.adapter.should eq(adapter)
|
14
|
+
dsl.adapter.should_not be_nil
|
18
15
|
end
|
19
16
|
|
20
17
|
it "defaults instrumenter to noop" do
|
@@ -37,58 +34,36 @@ describe Flipper::DSL do
|
|
37
34
|
end
|
38
35
|
end
|
39
36
|
|
40
|
-
describe "#enabled?" do
|
41
|
-
before do
|
42
|
-
subject.stub(:feature => admins_feature)
|
43
|
-
end
|
44
|
-
|
45
|
-
it "passes arguments to feature enabled check and returns result" do
|
46
|
-
admins_feature.should_receive(:enabled?).with(:foo).and_return(true)
|
47
|
-
subject.should_receive(:feature).with(:stats).and_return(admins_feature)
|
48
|
-
subject.enabled?(:stats, :foo).should be_true
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
describe "#enable" do
|
53
|
-
before do
|
54
|
-
subject.stub(:feature => admins_feature)
|
55
|
-
end
|
56
|
-
|
57
|
-
it "calls enable for feature with arguments" do
|
58
|
-
admins_feature.should_receive(:enable).with(:foo)
|
59
|
-
subject.should_receive(:feature).with(:stats).and_return(admins_feature)
|
60
|
-
subject.enable :stats, :foo
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe "#disable" do
|
65
|
-
before do
|
66
|
-
subject.stub(:feature => admins_feature)
|
67
|
-
end
|
68
|
-
|
69
|
-
it "calls disable for feature with arguments" do
|
70
|
-
admins_feature.should_receive(:disable).with(:foo)
|
71
|
-
subject.should_receive(:feature).with(:stats).and_return(admins_feature)
|
72
|
-
subject.disable :stats, :foo
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
37
|
describe "#feature" do
|
77
38
|
it_should_behave_like "a DSL feature" do
|
39
|
+
let(:method_name) { :feature }
|
78
40
|
let(:instrumenter) { double('Instrumentor', :instrument => nil) }
|
79
|
-
let(:feature) { dsl.
|
41
|
+
let(:feature) { dsl.send(method_name, :stats) }
|
80
42
|
let(:dsl) { Flipper::DSL.new(adapter, :instrumenter => instrumenter) }
|
81
43
|
end
|
82
44
|
end
|
83
45
|
|
84
46
|
describe "#[]" do
|
85
47
|
it_should_behave_like "a DSL feature" do
|
48
|
+
let(:method_name) { :[] }
|
86
49
|
let(:instrumenter) { double('Instrumentor', :instrument => nil) }
|
87
|
-
let(:feature) { dsl
|
50
|
+
let(:feature) { dsl.send(method_name, :stats) }
|
88
51
|
let(:dsl) { Flipper::DSL.new(adapter, :instrumenter => instrumenter) }
|
89
52
|
end
|
90
53
|
end
|
91
54
|
|
55
|
+
describe "#boolean" do
|
56
|
+
it_should_behave_like "a DSL boolean method" do
|
57
|
+
let(:method_name) { :boolean }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#bool" do
|
62
|
+
it_should_behave_like "a DSL boolean method" do
|
63
|
+
let(:method_name) { :bool }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
92
67
|
describe "#group" do
|
93
68
|
context "for registered group" do
|
94
69
|
before do
|
@@ -17,7 +17,7 @@ describe Flipper::Feature do
|
|
17
17
|
|
18
18
|
it "sets adapter" do
|
19
19
|
feature = described_class.new(:search, adapter)
|
20
|
-
feature.adapter.should eq(
|
20
|
+
feature.adapter.should eq(adapter)
|
21
21
|
end
|
22
22
|
|
23
23
|
it "defaults instrumenter" do
|
@@ -34,13 +34,6 @@ describe Flipper::Feature do
|
|
34
34
|
})
|
35
35
|
feature.instrumenter.should be(instrumenter)
|
36
36
|
end
|
37
|
-
|
38
|
-
it "passes overridden instrumenter to adapter wrapping" do
|
39
|
-
feature = described_class.new(:search, adapter, {
|
40
|
-
:instrumenter => instrumenter,
|
41
|
-
})
|
42
|
-
feature.adapter.instrumenter.should be(instrumenter)
|
43
|
-
end
|
44
37
|
end
|
45
38
|
end
|
46
39
|
|
@@ -67,12 +60,35 @@ describe Flipper::Feature do
|
|
67
60
|
end
|
68
61
|
end
|
69
62
|
|
63
|
+
describe "#gate" do
|
64
|
+
context "with symbol name" do
|
65
|
+
it "returns gate by name" do
|
66
|
+
boolean_gate = subject.gates.detect { |gate| gate.name == :boolean }
|
67
|
+
subject.gate(:boolean).should eq(boolean_gate)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "with string name" do
|
72
|
+
it "returns gate by name" do
|
73
|
+
boolean_gate = subject.gates.detect { |gate| gate.name == :boolean }
|
74
|
+
subject.gate('boolean').should eq(boolean_gate)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "with name that does not exist" do
|
79
|
+
it "returns nil" do
|
80
|
+
subject.gate(:poo).should be_nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
70
85
|
describe "#inspect" do
|
71
86
|
it "returns easy to read string representation" do
|
72
87
|
string = subject.inspect
|
73
88
|
string.should include('Flipper::Feature')
|
74
89
|
string.should include('name=:search')
|
75
90
|
string.should include('state=:off')
|
91
|
+
string.should include('description="Disabled"')
|
76
92
|
string.should include("adapter=#{subject.adapter.name.inspect}")
|
77
93
|
end
|
78
94
|
end
|
@@ -191,7 +207,7 @@ describe Flipper::Feature do
|
|
191
207
|
end
|
192
208
|
|
193
209
|
it "returns text" do
|
194
|
-
subject.description.should eq('Enabled for actors (5), 5% of the time')
|
210
|
+
subject.description.should eq('Enabled for actors ("5"), 5% of the time')
|
195
211
|
end
|
196
212
|
end
|
197
213
|
end
|