flipper 0.7.5 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +12 -0
  3. data/Gemfile +1 -0
  4. data/Rakefile +9 -4
  5. data/docs/Optimization.md +8 -2
  6. data/lib/flipper/adapters/instrumented.rb +21 -17
  7. data/lib/flipper/adapters/memoizable.rb +20 -13
  8. data/lib/flipper/adapters/memory.rb +1 -1
  9. data/lib/flipper/adapters/operation_logger.rb +49 -12
  10. data/lib/flipper/adapters/pstore.rb +1 -1
  11. data/lib/flipper/adapters/read_only.rb +51 -0
  12. data/lib/flipper/dsl.rb +9 -0
  13. data/lib/flipper/feature.rb +8 -20
  14. data/lib/flipper/gate.rb +0 -4
  15. data/lib/flipper/gate_values.rb +4 -2
  16. data/lib/flipper/gates/actor.rb +1 -1
  17. data/lib/flipper/gates/boolean.rb +1 -1
  18. data/lib/flipper/gates/group.rb +1 -1
  19. data/lib/flipper/gates/percentage_of_actors.rb +3 -5
  20. data/lib/flipper/gates/percentage_of_time.rb +2 -3
  21. data/lib/flipper/instrumentation/log_subscriber.rb +0 -28
  22. data/lib/flipper/instrumentation/subscriber.rb +0 -22
  23. data/lib/flipper/middleware/memoizer.rb +2 -3
  24. data/lib/flipper/spec/shared_adapter_specs.rb +32 -0
  25. data/lib/flipper/test/shared_adapter_test.rb +249 -0
  26. data/lib/flipper/typecast.rb +1 -1
  27. data/lib/flipper/types/group.rb +1 -1
  28. data/lib/flipper/version.rb +1 -1
  29. data/spec/flipper/adapters/instrumented_spec.rb +6 -0
  30. data/spec/flipper/adapters/memoizable_spec.rb +6 -0
  31. data/spec/flipper/adapters/read_only_spec.rb +94 -0
  32. data/spec/flipper/dsl_spec.rb +14 -2
  33. data/spec/flipper/feature_spec.rb +11 -0
  34. data/spec/flipper/instrumentation/log_subscriber_spec.rb +0 -10
  35. data/spec/flipper/instrumentation/metriks_subscriber_spec.rb +0 -10
  36. data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +0 -10
  37. data/spec/flipper/types/group_spec.rb +14 -0
  38. data/spec/helper.rb +4 -0
  39. data/spec/integration_spec.rb +11 -0
  40. data/spec/support/spec_helpers.rb +4 -0
  41. data/test/adapters/memory_test.rb +10 -0
  42. data/test/adapters/pstore_test.rb +17 -0
  43. data/test/helper.rb +0 -2
  44. data/test/test_helper.rb +6 -0
  45. metadata +12 -4
  46. data/lib/flipper/adapters/decorator.rb +0 -11
  47. data/lib/flipper/decorator.rb +0 -6
@@ -5,7 +5,7 @@ module Flipper
5
5
  1 => true,
6
6
  "true" => true,
7
7
  "1" => true,
8
- }
8
+ }.freeze
9
9
 
10
10
  # Internal: Convert value to a boolean.
11
11
  #
@@ -15,7 +15,7 @@ module Flipper
15
15
  end
16
16
 
17
17
  def match?(*args)
18
- @block.call(*args) == true
18
+ @block.call(*args)
19
19
  end
20
20
  end
21
21
  end
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = "0.7.5"
2
+ VERSION = "0.8.0"
3
3
  end
@@ -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
@@ -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 wrapping" do
29
+ it "passes overridden instrumenter to instrumented adapter" do
30
30
  dsl = described_class.new(adapter, :instrumenter => instrumenter)
31
- expect(dsl.adapter.instrumenter).to be(instrumenter)
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
@@ -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
@@ -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
@@ -14,6 +14,10 @@ module SpecHelpers
14
14
  }
15
15
  end
16
16
 
17
+ def build_api(flipper)
18
+ Flipper::Api.app(flipper)
19
+ end
20
+
17
21
  def build_flipper(adapter = build_memory_adapter)
18
22
  Flipper.new(adapter)
19
23
  end
@@ -0,0 +1,10 @@
1
+ require 'test_helper'
2
+ require 'flipper/adapters/memory'
3
+
4
+ class MemoryTest < MiniTest::Test
5
+ prepend SharedAdapterTests
6
+
7
+ def setup
8
+ @adapter = Flipper::Adapters::Memory.new
9
+ end
10
+ end
@@ -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
@@ -1,5 +1,3 @@
1
- $:.unshift(File.expand_path('../../lib', __FILE__))
2
-
3
1
  require 'rubygems'
4
2
  require 'bundler'
5
3
  Bundler.setup(:default)
@@ -0,0 +1,6 @@
1
+ require 'flipper'
2
+ require 'minitest/autorun'
3
+ require 'minitest/unit'
4
+ Dir["./lib/flipper/test/*.rb"].each { |f| require(f) }
5
+
6
+ FlipperRoot = Pathname(__FILE__).dirname.join('..').expand_path
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.7.5
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-02-25 00:00:00.000000000 Z
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/decorator.rb
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