flipper 0.7.5 → 0.8.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.
- 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
|