flipper 0.7.5 → 0.8.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 +12 -0
- data/Gemfile +1 -0
- data/Rakefile +9 -4
- data/docs/Optimization.md +8 -2
- data/lib/flipper/adapters/instrumented.rb +21 -17
- data/lib/flipper/adapters/memoizable.rb +20 -13
- data/lib/flipper/adapters/memory.rb +1 -1
- data/lib/flipper/adapters/operation_logger.rb +49 -12
- data/lib/flipper/adapters/pstore.rb +1 -1
- data/lib/flipper/adapters/read_only.rb +51 -0
- data/lib/flipper/dsl.rb +9 -0
- data/lib/flipper/feature.rb +8 -20
- data/lib/flipper/gate.rb +0 -4
- data/lib/flipper/gate_values.rb +4 -2
- data/lib/flipper/gates/actor.rb +1 -1
- data/lib/flipper/gates/boolean.rb +1 -1
- data/lib/flipper/gates/group.rb +1 -1
- data/lib/flipper/gates/percentage_of_actors.rb +3 -5
- data/lib/flipper/gates/percentage_of_time.rb +2 -3
- data/lib/flipper/instrumentation/log_subscriber.rb +0 -28
- data/lib/flipper/instrumentation/subscriber.rb +0 -22
- data/lib/flipper/middleware/memoizer.rb +2 -3
- data/lib/flipper/spec/shared_adapter_specs.rb +32 -0
- data/lib/flipper/test/shared_adapter_test.rb +249 -0
- data/lib/flipper/typecast.rb +1 -1
- data/lib/flipper/types/group.rb +1 -1
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/instrumented_spec.rb +6 -0
- data/spec/flipper/adapters/memoizable_spec.rb +6 -0
- data/spec/flipper/adapters/read_only_spec.rb +94 -0
- data/spec/flipper/dsl_spec.rb +14 -2
- data/spec/flipper/feature_spec.rb +11 -0
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +0 -10
- data/spec/flipper/instrumentation/metriks_subscriber_spec.rb +0 -10
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +0 -10
- data/spec/flipper/types/group_spec.rb +14 -0
- data/spec/helper.rb +4 -0
- data/spec/integration_spec.rb +11 -0
- data/spec/support/spec_helpers.rb +4 -0
- data/test/adapters/memory_test.rb +10 -0
- data/test/adapters/pstore_test.rb +17 -0
- data/test/helper.rb +0 -2
- data/test/test_helper.rb +6 -0
- metadata +12 -4
- data/lib/flipper/adapters/decorator.rb +0 -11
- data/lib/flipper/decorator.rb +0 -6
data/lib/flipper/typecast.rb
CHANGED
data/lib/flipper/types/group.rb
CHANGED
data/lib/flipper/version.rb
CHANGED
@@ -19,6 +19,12 @@ RSpec.describe Flipper::Adapters::Instrumented do
|
|
19
19
|
|
20
20
|
it_should_behave_like 'a flipper adapter'
|
21
21
|
|
22
|
+
describe "#name" do
|
23
|
+
it "is instrumented" do
|
24
|
+
expect(subject.name).to be(:instrumented)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
22
28
|
describe "#get" do
|
23
29
|
it "records instrumentation" do
|
24
30
|
result = subject.get(feature)
|
@@ -13,6 +13,12 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
13
13
|
|
14
14
|
it_should_behave_like 'a flipper adapter'
|
15
15
|
|
16
|
+
describe "#name" do
|
17
|
+
it "is instrumented" do
|
18
|
+
expect(subject.name).to be(:memoizable)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
16
22
|
describe "#memoize=" do
|
17
23
|
it "sets value" do
|
18
24
|
subject.memoize = true
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'flipper/adapters/read_only'
|
3
|
+
|
4
|
+
RSpec.describe Flipper::Adapters::ReadOnly do
|
5
|
+
let(:actor_class) { Struct.new(:flipper_id) }
|
6
|
+
|
7
|
+
let(:adapter) { Flipper::Adapters::Memory.new }
|
8
|
+
let(:flipper) { Flipper.new(subject) }
|
9
|
+
let(:feature) { flipper[:stats] }
|
10
|
+
|
11
|
+
let(:boolean_gate) { feature.gate(:boolean) }
|
12
|
+
let(:group_gate) { feature.gate(:group) }
|
13
|
+
let(:actor_gate) { feature.gate(:actor) }
|
14
|
+
let(:actors_gate) { feature.gate(:percentage_of_actors) }
|
15
|
+
let(:time_gate) { feature.gate(:percentage_of_time) }
|
16
|
+
|
17
|
+
subject { described_class.new(adapter) }
|
18
|
+
|
19
|
+
before do
|
20
|
+
Flipper.register(:admins) { |actor|
|
21
|
+
actor.respond_to?(:admin?) && actor.admin?
|
22
|
+
}
|
23
|
+
|
24
|
+
Flipper.register(:early_access) { |actor|
|
25
|
+
actor.respond_to?(:early_access?) && actor.early_access?
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
after do
|
30
|
+
Flipper.unregister_groups
|
31
|
+
end
|
32
|
+
|
33
|
+
it "has name that is a symbol" do
|
34
|
+
expect(subject.name).to_not be_nil
|
35
|
+
expect(subject.name).to be_instance_of(Symbol)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "has included the flipper adapter module" do
|
39
|
+
expect(subject.class.ancestors).to include(Flipper::Adapter)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns correct default values for the gates if none are enabled" do
|
43
|
+
expect(subject.get(feature)).to eq({
|
44
|
+
:boolean => nil,
|
45
|
+
:groups => Set.new,
|
46
|
+
:actors => Set.new,
|
47
|
+
:percentage_of_actors => nil,
|
48
|
+
:percentage_of_time => nil,
|
49
|
+
})
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can get feature" do
|
53
|
+
actor_22 = actor_class.new('22')
|
54
|
+
adapter.enable(feature, boolean_gate, flipper.boolean)
|
55
|
+
adapter.enable(feature, group_gate, flipper.group(:admins))
|
56
|
+
adapter.enable(feature, actor_gate, flipper.actor(actor_22))
|
57
|
+
adapter.enable(feature, actors_gate, flipper.actors(25))
|
58
|
+
adapter.enable(feature, time_gate, flipper.time(45))
|
59
|
+
|
60
|
+
expect(subject.get(feature)).to eq({
|
61
|
+
:boolean => "true",
|
62
|
+
:groups => Set["admins"],
|
63
|
+
:actors => Set["22"],
|
64
|
+
:percentage_of_actors => "25",
|
65
|
+
:percentage_of_time => "45",
|
66
|
+
})
|
67
|
+
end
|
68
|
+
|
69
|
+
it "can get features" do
|
70
|
+
expect(subject.features).to eq(Set.new)
|
71
|
+
adapter.add(feature)
|
72
|
+
expect(subject.features).to eq(Set["stats"])
|
73
|
+
end
|
74
|
+
|
75
|
+
it "raises error on add" do
|
76
|
+
expect { subject.add(feature) }.to raise_error(Flipper::Adapters::ReadOnly::WriteAttempted)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "raises error on remove" do
|
80
|
+
expect { subject.remove(feature) }.to raise_error(Flipper::Adapters::ReadOnly::WriteAttempted)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "raises on clear" do
|
84
|
+
expect { subject.clear(feature) }.to raise_error(Flipper::Adapters::ReadOnly::WriteAttempted)
|
85
|
+
end
|
86
|
+
|
87
|
+
it "raises error on enable" do
|
88
|
+
expect { subject.enable(feature, boolean_gate, flipper.boolean) }.to raise_error(Flipper::Adapters::ReadOnly::WriteAttempted)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "raises error on disable" do
|
92
|
+
expect { subject.disable(feature, boolean_gate, flipper.boolean) }.to raise_error(Flipper::Adapters::ReadOnly::WriteAttempted)
|
93
|
+
end
|
94
|
+
end
|
data/spec/flipper/dsl_spec.rb
CHANGED
@@ -26,9 +26,11 @@ RSpec.describe Flipper::DSL do
|
|
26
26
|
expect(dsl.instrumenter).to be(instrumenter)
|
27
27
|
end
|
28
28
|
|
29
|
-
it "passes overridden instrumenter to adapter
|
29
|
+
it "passes overridden instrumenter to instrumented adapter" do
|
30
30
|
dsl = described_class.new(adapter, :instrumenter => instrumenter)
|
31
|
-
|
31
|
+
memoized = dsl.adapter
|
32
|
+
instrumented = memoized.adapter
|
33
|
+
expect(instrumented.instrumenter).to be(instrumenter)
|
32
34
|
end
|
33
35
|
end
|
34
36
|
end
|
@@ -233,4 +235,14 @@ RSpec.describe Flipper::DSL do
|
|
233
235
|
expect(subject[:stats].percentage_of_actors_value).to be(0)
|
234
236
|
end
|
235
237
|
end
|
238
|
+
|
239
|
+
describe '#remove' do
|
240
|
+
it "removes the feature" do
|
241
|
+
subject.enable(:stats)
|
242
|
+
|
243
|
+
expect { subject.remove(:stats) }.to change { subject.enabled?(:stats) }.to(false)
|
244
|
+
|
245
|
+
expect(subject.features).to be_empty
|
246
|
+
end
|
247
|
+
end
|
236
248
|
end
|
@@ -197,6 +197,17 @@ RSpec.describe Flipper::Feature do
|
|
197
197
|
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(thing))
|
198
198
|
end
|
199
199
|
|
200
|
+
it "is recorded for remove" do
|
201
|
+
subject.remove
|
202
|
+
|
203
|
+
event = instrumenter.events.last
|
204
|
+
expect(event).not_to be_nil
|
205
|
+
expect(event.name).to eq('feature_operation.flipper')
|
206
|
+
expect(event.payload[:feature_name]).to eq(:search)
|
207
|
+
expect(event.payload[:operation]).to eq(:remove)
|
208
|
+
expect(event.payload[:result]).not_to be_nil
|
209
|
+
end
|
210
|
+
|
200
211
|
it "is recorded for enabled?" do
|
201
212
|
thing = Flipper::Types::Actor.new(Struct.new(:flipper_id).new("1"))
|
202
213
|
gate = subject.gate_for(thing)
|
@@ -42,11 +42,6 @@ RSpec.describe Flipper::Instrumentation::LogSubscriber do
|
|
42
42
|
expect(adapter_line).to include('[ result={')
|
43
43
|
expect(adapter_line).to include('} ]')
|
44
44
|
end
|
45
|
-
|
46
|
-
it "logs gate calls" do
|
47
|
-
gate_line = find_line('Flipper feature(search) gate(boolean) open? false')
|
48
|
-
expect(gate_line).to include('[ thing=nil ]')
|
49
|
-
end
|
50
45
|
end
|
51
46
|
|
52
47
|
context "feature enabled checks with a thing" do
|
@@ -61,11 +56,6 @@ RSpec.describe Flipper::Instrumentation::LogSubscriber do
|
|
61
56
|
feature_line = find_line('Flipper feature(search) enabled?')
|
62
57
|
expect(feature_line).to include(user.inspect)
|
63
58
|
end
|
64
|
-
|
65
|
-
it "logs thing for gate" do
|
66
|
-
gate_line = find_line('Flipper feature(search) gate(boolean) open')
|
67
|
-
expect(gate_line).to include(user.inspect)
|
68
|
-
end
|
69
59
|
end
|
70
60
|
|
71
61
|
context "changing feature enabled state" do
|
@@ -46,14 +46,4 @@ RSpec.describe Flipper::Instrumentation::MetriksSubscriber do
|
|
46
46
|
flipper[:stats].disable(user)
|
47
47
|
expect(Metriks.timer("flipper.adapter.memory.disable").count).to be(1)
|
48
48
|
end
|
49
|
-
|
50
|
-
it "updates gate metrics when calls happen" do
|
51
|
-
flipper[:stats].enable(user)
|
52
|
-
flipper[:stats].enabled?(user)
|
53
|
-
|
54
|
-
expect(Metriks.timer("flipper.gate_operation.boolean.open").count).to be(1)
|
55
|
-
expect(Metriks.timer("flipper.feature.stats.gate_operation.boolean.open").count).to be(1)
|
56
|
-
expect(Metriks.meter("flipper.feature.stats.gate.actor.open").count).to be(1)
|
57
|
-
expect(Metriks.meter("flipper.feature.stats.gate.boolean.closed").count).to be(1)
|
58
|
-
end
|
59
49
|
end
|
@@ -65,14 +65,4 @@ RSpec.describe Flipper::Instrumentation::StatsdSubscriber do
|
|
65
65
|
flipper[:stats].disable(user)
|
66
66
|
assert_timer 'flipper.adapter.memory.disable'
|
67
67
|
end
|
68
|
-
|
69
|
-
it "updates gate metrics when calls happen" do
|
70
|
-
flipper[:stats].enable(user)
|
71
|
-
flipper[:stats].enabled?(user)
|
72
|
-
|
73
|
-
assert_timer 'flipper.gate_operation.boolean.open'
|
74
|
-
assert_timer 'flipper.feature.stats.gate_operation.boolean.open'
|
75
|
-
assert_counter 'flipper.feature.stats.gate.actor.open'
|
76
|
-
assert_counter 'flipper.feature.stats.gate.boolean.closed'
|
77
|
-
end
|
78
68
|
end
|
@@ -48,5 +48,19 @@ RSpec.describe Flipper::Types::Group do
|
|
48
48
|
it "returns false if block does not match" do
|
49
49
|
expect(subject.match?(non_admin_actor)).to eq(false)
|
50
50
|
end
|
51
|
+
|
52
|
+
it "returns true for truthy block results" do
|
53
|
+
group = Flipper::Types::Group.new(:examples) do |actor|
|
54
|
+
actor.email =~ /@example\.com/
|
55
|
+
end
|
56
|
+
expect(group.match?(double('Actor', :email => "foo@example.com"))).to be_truthy
|
57
|
+
end
|
58
|
+
|
59
|
+
it "returns false for falsey block results" do
|
60
|
+
group = Flipper::Types::Group.new(:examples) do |actor|
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
expect(group.match?(double('Actor'))).to be_falsey
|
64
|
+
end
|
51
65
|
end
|
52
66
|
end
|
data/spec/helper.rb
CHANGED
@@ -10,6 +10,7 @@ Bundler.setup(:default)
|
|
10
10
|
|
11
11
|
require 'flipper'
|
12
12
|
require 'flipper-ui'
|
13
|
+
require 'flipper-api'
|
13
14
|
|
14
15
|
Dir[FlipperRoot.join("spec/support/**/*.rb")].each { |f| require f }
|
15
16
|
|
@@ -17,6 +18,9 @@ RSpec.configure do |config|
|
|
17
18
|
config.before(:example) do
|
18
19
|
Flipper.unregister_groups
|
19
20
|
end
|
21
|
+
|
22
|
+
config.filter_run focus: true
|
23
|
+
config.run_all_when_everything_filtered = true
|
20
24
|
end
|
21
25
|
|
22
26
|
RSpec.shared_examples_for 'a percentage' do
|
data/spec/integration_spec.rb
CHANGED
@@ -15,6 +15,9 @@ RSpec.describe Flipper do
|
|
15
15
|
let(:admin_thing) { double 'Non Flipper Thing', :flipper_id => 1, :admin? => true, :dev? => false }
|
16
16
|
let(:dev_thing) { double 'Non Flipper Thing', :flipper_id => 10, :admin? => false, :dev? => true }
|
17
17
|
|
18
|
+
let(:admin_truthy_thing) { double 'Non Flipper Thing', :flipper_id => 1, :admin? => "true-ish", :dev? => false }
|
19
|
+
let(:admin_falsey_thing) { double 'Non Flipper Thing', :flipper_id => 1, :admin? => nil, :dev? => false }
|
20
|
+
|
18
21
|
let(:pitt) { actor_class.new(1) }
|
19
22
|
let(:clooney) { actor_class.new(10) }
|
20
23
|
|
@@ -347,6 +350,10 @@ RSpec.describe Flipper do
|
|
347
350
|
expect(feature.enabled?(flipper.actor(admin_thing))).to eq(true)
|
348
351
|
expect(feature.enabled?(admin_thing)).to eq(true)
|
349
352
|
end
|
353
|
+
|
354
|
+
it "returns true for truthy block values" do
|
355
|
+
expect(feature.enabled?(flipper.actor(admin_truthy_thing))).to eq(true)
|
356
|
+
end
|
350
357
|
end
|
351
358
|
|
352
359
|
context "for actor in disabled group" do
|
@@ -354,6 +361,10 @@ RSpec.describe Flipper do
|
|
354
361
|
expect(feature.enabled?(flipper.actor(dev_thing))).to eq(false)
|
355
362
|
expect(feature.enabled?(dev_thing)).to eq(false)
|
356
363
|
end
|
364
|
+
|
365
|
+
it "returns false for falsey block values" do
|
366
|
+
expect(feature.enabled?(flipper.actor(admin_falsey_thing))).to eq(false)
|
367
|
+
end
|
357
368
|
end
|
358
369
|
|
359
370
|
context "for enabled actor" do
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'flipper/adapters/pstore'
|
3
|
+
|
4
|
+
class PstoreTest < MiniTest::Test
|
5
|
+
prepend SharedAdapterTests
|
6
|
+
|
7
|
+
def setup
|
8
|
+
dir = FlipperRoot.join("tmp").tap { |d| d.mkpath }
|
9
|
+
pstore_file = dir.join("flipper.pstore")
|
10
|
+
pstore_file.unlink if pstore_file.exist?
|
11
|
+
@adapter = Flipper::Adapters::PStore.new(pstore_file)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_defaults_path_to_flipper_pstore
|
15
|
+
assert_equal Flipper::Adapters::PStore.new.path, "flipper.pstore"
|
16
|
+
end
|
17
|
+
end
|
data/test/helper.rb
CHANGED
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-06-21 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Feature flipper is the act of enabling/disabling features in your application,
|
14
14
|
ideally without re-deploying or changing anything in your code base. Flipper makes
|
@@ -43,13 +43,12 @@ files:
|
|
43
43
|
- flipper.gemspec
|
44
44
|
- lib/flipper.rb
|
45
45
|
- lib/flipper/adapter.rb
|
46
|
-
- lib/flipper/adapters/decorator.rb
|
47
46
|
- lib/flipper/adapters/instrumented.rb
|
48
47
|
- lib/flipper/adapters/memoizable.rb
|
49
48
|
- lib/flipper/adapters/memory.rb
|
50
49
|
- lib/flipper/adapters/operation_logger.rb
|
51
50
|
- lib/flipper/adapters/pstore.rb
|
52
|
-
- lib/flipper/
|
51
|
+
- lib/flipper/adapters/read_only.rb
|
53
52
|
- lib/flipper/dsl.rb
|
54
53
|
- lib/flipper/errors.rb
|
55
54
|
- lib/flipper/feature.rb
|
@@ -71,6 +70,7 @@ files:
|
|
71
70
|
- lib/flipper/middleware/memoizer.rb
|
72
71
|
- lib/flipper/registry.rb
|
73
72
|
- lib/flipper/spec/shared_adapter_specs.rb
|
73
|
+
- lib/flipper/test/shared_adapter_test.rb
|
74
74
|
- lib/flipper/type.rb
|
75
75
|
- lib/flipper/typecast.rb
|
76
76
|
- lib/flipper/types/actor.rb
|
@@ -85,6 +85,7 @@ files:
|
|
85
85
|
- spec/flipper/adapters/memory_spec.rb
|
86
86
|
- spec/flipper/adapters/operation_logger_spec.rb
|
87
87
|
- spec/flipper/adapters/pstore_spec.rb
|
88
|
+
- spec/flipper/adapters/read_only_spec.rb
|
88
89
|
- spec/flipper/dsl_spec.rb
|
89
90
|
- spec/flipper/feature_spec.rb
|
90
91
|
- spec/flipper/gate_spec.rb
|
@@ -113,7 +114,10 @@ files:
|
|
113
114
|
- spec/integration_spec.rb
|
114
115
|
- spec/support/fake_udp_socket.rb
|
115
116
|
- spec/support/spec_helpers.rb
|
117
|
+
- test/adapters/memory_test.rb
|
118
|
+
- test/adapters/pstore_test.rb
|
116
119
|
- test/helper.rb
|
120
|
+
- test/test_helper.rb
|
117
121
|
homepage: https://github.com/jnunemaker/flipper
|
118
122
|
licenses:
|
119
123
|
- MIT
|
@@ -144,6 +148,7 @@ test_files:
|
|
144
148
|
- spec/flipper/adapters/memory_spec.rb
|
145
149
|
- spec/flipper/adapters/operation_logger_spec.rb
|
146
150
|
- spec/flipper/adapters/pstore_spec.rb
|
151
|
+
- spec/flipper/adapters/read_only_spec.rb
|
147
152
|
- spec/flipper/dsl_spec.rb
|
148
153
|
- spec/flipper/feature_spec.rb
|
149
154
|
- spec/flipper/gate_spec.rb
|
@@ -172,4 +177,7 @@ test_files:
|
|
172
177
|
- spec/integration_spec.rb
|
173
178
|
- spec/support/fake_udp_socket.rb
|
174
179
|
- spec/support/spec_helpers.rb
|
180
|
+
- test/adapters/memory_test.rb
|
181
|
+
- test/adapters/pstore_test.rb
|
175
182
|
- test/helper.rb
|
183
|
+
- test/test_helper.rb
|