flipper 0.6.3 → 0.7.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -2
  3. data/Gemfile +10 -10
  4. data/Guardfile +2 -1
  5. data/README.md +54 -23
  6. data/Rakefile +1 -1
  7. data/examples/dsl.rb +2 -2
  8. data/examples/{percentage_of_random.rb → percentage_of_time.rb} +1 -1
  9. data/lib/flipper.rb +25 -12
  10. data/lib/flipper/dsl.rb +96 -8
  11. data/lib/flipper/feature.rb +216 -37
  12. data/lib/flipper/gate.rb +6 -38
  13. data/lib/flipper/gate_values.rb +42 -0
  14. data/lib/flipper/gates/actor.rb +11 -13
  15. data/lib/flipper/gates/boolean.rb +3 -12
  16. data/lib/flipper/gates/group.rb +14 -16
  17. data/lib/flipper/gates/percentage_of_actors.rb +12 -13
  18. data/lib/flipper/gates/{percentage_of_random.rb → percentage_of_time.rb} +8 -11
  19. data/lib/flipper/instrumentation/log_subscriber.rb +1 -1
  20. data/lib/flipper/instrumentation/subscriber.rb +1 -1
  21. data/lib/flipper/spec/shared_adapter_specs.rb +16 -16
  22. data/lib/flipper/type.rb +1 -1
  23. data/lib/flipper/typecast.rb +44 -0
  24. data/lib/flipper/types/actor.rb +2 -5
  25. data/lib/flipper/types/group.rb +6 -0
  26. data/lib/flipper/types/percentage.rb +7 -1
  27. data/lib/flipper/types/{percentage_of_random.rb → percentage_of_time.rb} +1 -1
  28. data/lib/flipper/version.rb +1 -1
  29. data/script/bootstrap +21 -0
  30. data/script/guard +15 -0
  31. data/script/release +15 -0
  32. data/script/test +30 -0
  33. data/spec/flipper/dsl_spec.rb +67 -8
  34. data/spec/flipper/feature_spec.rb +424 -12
  35. data/spec/flipper/gate_spec.rb +1 -20
  36. data/spec/flipper/gate_values_spec.rb +134 -0
  37. data/spec/flipper/gates/actor_spec.rb +1 -21
  38. data/spec/flipper/gates/boolean_spec.rb +3 -71
  39. data/spec/flipper/gates/group_spec.rb +3 -23
  40. data/spec/flipper/gates/percentage_of_actors_spec.rb +5 -26
  41. data/spec/flipper/gates/percentage_of_time_spec.rb +23 -0
  42. data/spec/flipper/middleware/memoizer_spec.rb +1 -2
  43. data/spec/flipper/typecast_spec.rb +63 -0
  44. data/spec/flipper/types/group_spec.rb +21 -1
  45. data/spec/flipper/types/percentage_of_time_spec.rb +6 -0
  46. data/spec/flipper/types/percentage_spec.rb +20 -0
  47. data/spec/flipper_spec.rb +31 -9
  48. data/spec/helper.rb +1 -3
  49. data/spec/integration_spec.rb +22 -22
  50. metadata +21 -11
  51. data/spec/flipper/gates/percentage_of_random_spec.rb +0 -46
  52. data/spec/flipper/types/percentage_of_random_spec.rb +0 -6
@@ -4,32 +4,13 @@ describe Flipper::Gate do
4
4
  let(:feature_name) { :stats }
5
5
 
6
6
  subject {
7
- described_class.new(feature_name)
7
+ described_class.new
8
8
  }
9
9
 
10
- describe "#initialize" do
11
- it "sets feature_name" do
12
- gate = described_class.new(feature_name)
13
- gate.feature_name.should be(feature_name)
14
- end
15
-
16
- it "defaults instrumenter" do
17
- gate = described_class.new(feature_name)
18
- gate.instrumenter.should be(Flipper::Instrumenters::Noop)
19
- end
20
-
21
- it "allows overriding instrumenter" do
22
- instrumenter = double('Instrumentor')
23
- gate = described_class.new(feature_name, :instrumenter => instrumenter)
24
- gate.instrumenter.should be(instrumenter)
25
- end
26
- end
27
-
28
10
  describe "#inspect" do
29
11
  it "returns easy to read string representation" do
30
12
  string = subject.inspect
31
13
  string.should include('Flipper::Gate')
32
- string.should include('feature_name=:stats')
33
14
  end
34
15
  end
35
16
  end
@@ -0,0 +1,134 @@
1
+ require 'helper'
2
+ require 'flipper/gate_values'
3
+
4
+ describe Flipper::GateValues do
5
+ {
6
+ nil => false,
7
+ "" => false,
8
+ 0 => false,
9
+ 1 => true,
10
+ "0" => false,
11
+ "1" => true,
12
+ true => true,
13
+ false => false,
14
+ "true" => true,
15
+ "false" => false,
16
+ }.each do |value, expected|
17
+ context "with #{value.inspect} boolean" do
18
+ it "returns #{expected}" do
19
+ described_class.new(boolean: value).boolean.should be(expected)
20
+ end
21
+ end
22
+ end
23
+
24
+ {
25
+ nil => 0,
26
+ "" => 0,
27
+ 0 => 0,
28
+ 1 => 1,
29
+ "1" => 1,
30
+ "99" => 99,
31
+ }.each do |value, expected|
32
+ context "with #{value.inspect} percentage of time" do
33
+ it "returns #{expected}" do
34
+ described_class.new(percentage_of_time: value).percentage_of_time.should be(expected)
35
+ end
36
+ end
37
+ end
38
+
39
+ {
40
+ nil => 0,
41
+ "" => 0,
42
+ 0 => 0,
43
+ 1 => 1,
44
+ "1" => 1,
45
+ "99" => 99,
46
+ }.each do |value, expected|
47
+ context "with #{value.inspect} percentage of actors" do
48
+ it "returns #{expected}" do
49
+ described_class.new(percentage_of_actors: value).percentage_of_actors.should be(expected)
50
+ end
51
+ end
52
+ end
53
+
54
+ {
55
+ nil => Set.new,
56
+ "" => Set.new,
57
+ Set.new([1, 2]) => Set.new([1, 2]),
58
+ [1, 2] => Set.new([1, 2])
59
+ }.each do |value, expected|
60
+ context "with #{value.inspect} actors" do
61
+ it "returns #{expected}" do
62
+ described_class.new(actors: value).actors.should eq(expected)
63
+ end
64
+ end
65
+ end
66
+
67
+ {
68
+ nil => Set.new,
69
+ "" => Set.new,
70
+ Set.new([:admins, :preview_features]) => Set.new([:admins, :preview_features]),
71
+ [:admins, :preview_features] => Set.new([:admins, :preview_features])
72
+ }.each do |value, expected|
73
+ context "with #{value.inspect} groups" do
74
+ it "returns #{expected}" do
75
+ described_class.new(groups: value).groups.should eq(expected)
76
+ end
77
+ end
78
+ end
79
+
80
+ it "raises argument error for percentage of time value that cannot be converted to an integer" do
81
+ expect {
82
+ described_class.new(percentage_of_time: ["asdf"])
83
+ }.to raise_error(ArgumentError, %Q(["asdf"] cannot be converted to an integer))
84
+ end
85
+
86
+ it "raises argument error for percentage of actors value that cannot be converted to an integer" do
87
+ expect {
88
+ described_class.new(percentage_of_actors: ["asdf"])
89
+ }.to raise_error(ArgumentError, %Q(["asdf"] cannot be converted to an integer))
90
+ end
91
+
92
+ it "raises argument error for actors value that cannot be converted to a set" do
93
+ expect {
94
+ described_class.new(actors: "asdf")
95
+ }.to raise_error(ArgumentError, %Q("asdf" cannot be converted to a set))
96
+ end
97
+
98
+ it "raises argument error for groups value that cannot be converted to a set" do
99
+ expect {
100
+ described_class.new(groups: "asdf")
101
+ }.to raise_error(ArgumentError, %Q("asdf" cannot be converted to a set))
102
+ end
103
+
104
+ describe "#[]" do
105
+ it "can read the boolean value" do
106
+ described_class.new(boolean: true)[:boolean].should be(true)
107
+ described_class.new(boolean: true)["boolean"].should be(true)
108
+ end
109
+
110
+ it "can read the actors value" do
111
+ described_class.new(actors: Set[1, 2])[:actors].should eq(Set[1, 2])
112
+ described_class.new(actors: Set[1, 2])["actors"].should eq(Set[1, 2])
113
+ end
114
+
115
+ it "can read the groups value" do
116
+ described_class.new(groups: Set[:admins])[:groups].should eq(Set[:admins])
117
+ described_class.new(groups: Set[:admins])["groups"].should eq(Set[:admins])
118
+ end
119
+
120
+ it "can read the percentage of time value" do
121
+ described_class.new(percentage_of_time: 15)[:percentage_of_time].should eq(15)
122
+ described_class.new(percentage_of_time: 15)["percentage_of_time"].should eq(15)
123
+ end
124
+
125
+ it "can read the percentage of actors value" do
126
+ described_class.new(percentage_of_actors: 15)[:percentage_of_actors].should eq(15)
127
+ described_class.new(percentage_of_actors: 15)["percentage_of_actors"].should eq(15)
128
+ end
129
+
130
+ it "returns nil for value that is not present" do
131
+ described_class.new({})["not legit"].should be(nil)
132
+ end
133
+ end
134
+ end
@@ -1,32 +1,12 @@
1
1
  require 'helper'
2
- require 'flipper/instrumenters/memory'
3
2
 
4
3
  describe Flipper::Gates::Actor do
5
- let(:instrumenter) { Flipper::Instrumenters::Memory.new }
6
4
  let(:feature_name) { :search }
7
5
 
8
6
  subject {
9
- described_class.new(feature_name, :instrumenter => instrumenter)
7
+ described_class.new
10
8
  }
11
9
 
12
- describe "instrumentation" do
13
- it "is recorded for open" do
14
- thing = Struct.new(:flipper_id).new('22')
15
- subject.open?(thing, Set.new)
16
-
17
- event = instrumenter.events.last
18
- event.should_not be_nil
19
- event.name.should eq('gate_operation.flipper')
20
- event.payload.should eq({
21
- :thing => thing,
22
- :operation => :open?,
23
- :result => false,
24
- :gate_name => :actor,
25
- :feature_name => :search,
26
- })
27
- end
28
- end
29
-
30
10
  describe "#description" do
31
11
  context "with actors in set" do
32
12
  it "returns text" do
@@ -1,12 +1,10 @@
1
1
  require 'helper'
2
- require 'flipper/instrumenters/memory'
3
2
 
4
3
  describe Flipper::Gates::Boolean do
5
- let(:instrumenter) { Flipper::Instrumenters::Memory.new }
6
4
  let(:feature_name) { :search }
7
5
 
8
6
  subject {
9
- described_class.new(feature_name, :instrumenter => instrumenter)
7
+ described_class.new
10
8
  }
11
9
 
12
10
  describe "#description" do
@@ -35,85 +33,19 @@ describe Flipper::Gates::Boolean do
35
33
  subject.enabled?(false).should eq(false)
36
34
  end
37
35
  end
38
-
39
- context "for nil value" do
40
- it "returns false" do
41
- subject.enabled?(nil).should eq(false)
42
- end
43
- end
44
-
45
- context "for empty string value" do
46
- it "returns false" do
47
- subject.enabled?('').should eq(false)
48
- end
49
- end
50
-
51
- context "for the string true value" do
52
- it "returns true" do
53
- subject.enabled?('true').should eq(true)
54
- end
55
- end
56
-
57
- context "for the string false value" do
58
- it "returns false" do
59
- subject.enabled?('false').should eq(false)
60
- end
61
- end
62
36
  end
63
37
 
64
38
  describe "#open?" do
65
39
  context "for true value" do
66
40
  it "returns true" do
67
- subject.open?(Object.new, true).should eq(true)
41
+ subject.open?(Object.new, true, feature_name: feature_name).should eq(true)
68
42
  end
69
43
  end
70
44
 
71
45
  context "for false value" do
72
46
  it "returns false" do
73
- subject.open?(Object.new, false).should eq(false)
74
- end
75
- end
76
-
77
- context "for nil value" do
78
- it "returns false" do
79
- subject.open?(Object.new, nil).should eq(false)
80
- end
81
- end
82
-
83
- context "for string true value" do
84
- it "returns true" do
85
- subject.open?(Object.new, 'true').should eq(true)
86
- end
87
- end
88
-
89
- context "for string false value" do
90
- it "returns false" do
91
- subject.open?(Object.new, 'false').should eq(false)
92
- end
93
- end
94
-
95
- context "for an empty string value" do
96
- it "returns false" do
97
- subject.open?(Object.new, '').should eq(false)
47
+ subject.open?(Object.new, false, feature_name: feature_name).should eq(false)
98
48
  end
99
49
  end
100
50
  end
101
-
102
- describe "instrumentation" do
103
- it "is recorded for open" do
104
- thing = nil
105
- subject.open?(thing, false)
106
-
107
- event = instrumenter.events.last
108
- event.should_not be_nil
109
- event.name.should eq('gate_operation.flipper')
110
- event.payload.should eq({
111
- :thing => thing,
112
- :operation => :open?,
113
- :result => false,
114
- :gate_name => :boolean,
115
- :feature_name => :search,
116
- })
117
- end
118
- end
119
51
  end
@@ -1,32 +1,12 @@
1
1
  require 'helper'
2
- require 'flipper/instrumenters/memory'
3
2
 
4
3
  describe Flipper::Gates::Group do
5
- let(:instrumenter) { Flipper::Instrumenters::Memory.new }
6
4
  let(:feature_name) { :search }
7
5
 
8
6
  subject {
9
- described_class.new(feature_name, :instrumenter => instrumenter)
7
+ described_class.new
10
8
  }
11
9
 
12
- describe "instrumentation" do
13
- it "is recorded for open" do
14
- thing = Struct.new(:flipper_id).new('22')
15
- subject.open?(thing, Set.new)
16
-
17
- event = instrumenter.events.last
18
- event.should_not be_nil
19
- event.name.should eq('gate_operation.flipper')
20
- event.payload.should eq({
21
- :thing => thing,
22
- :operation => :open?,
23
- :result => false,
24
- :gate_name => :group,
25
- :feature_name => :search,
26
- })
27
- end
28
- end
29
-
30
10
  describe "#description" do
31
11
  context "with groups in set" do
32
12
  it "returns text" do
@@ -50,7 +30,7 @@ describe Flipper::Gates::Group do
50
30
 
51
31
  it "ignores group" do
52
32
  thing = Struct.new(:flipper_id).new('5')
53
- subject.open?(thing, Set[:newbs, :staff]).should eq(true)
33
+ subject.open?(thing, Set[:newbs, :staff], feature_name: feature_name).should eq(true)
54
34
  end
55
35
  end
56
36
 
@@ -61,7 +41,7 @@ describe Flipper::Gates::Group do
61
41
 
62
42
  it "raises error" do
63
43
  expect {
64
- subject.open?(Object.new, Set[:stinkers])
44
+ subject.open?(Object.new, Set[:stinkers], feature_name: feature_name)
65
45
  }.to raise_error(NoMethodError)
66
46
  end
67
47
  end
@@ -1,32 +1,12 @@
1
1
  require 'helper'
2
- require 'flipper/instrumenters/memory'
3
2
 
4
3
  describe Flipper::Gates::PercentageOfActors do
5
- let(:instrumenter) { Flipper::Instrumenters::Memory.new }
6
4
  let(:feature_name) { :search }
7
5
 
8
6
  subject {
9
- described_class.new(feature_name, :instrumenter => instrumenter)
7
+ described_class.new
10
8
  }
11
9
 
12
- describe "instrumentation" do
13
- it "is recorded for open" do
14
- thing = Struct.new(:flipper_id).new('22')
15
- subject.open?(thing, 0)
16
-
17
- event = instrumenter.events.last
18
- event.should_not be_nil
19
- event.name.should eq('gate_operation.flipper')
20
- event.payload.should eq({
21
- :thing => thing,
22
- :operation => :open?,
23
- :result => false,
24
- :gate_name => :percentage_of_actors,
25
- :feature_name => :search,
26
- })
27
- end
28
- end
29
-
30
10
  describe "#description" do
31
11
  context "when enabled" do
32
12
  it "returns text" do
@@ -36,7 +16,6 @@ describe Flipper::Gates::PercentageOfActors do
36
16
 
37
17
  context "when disabled" do
38
18
  it "returns disabled" do
39
- subject.description(nil).should eq('disabled')
40
19
  subject.description(0).should eq('disabled')
41
20
  end
42
21
  end
@@ -53,13 +32,13 @@ describe Flipper::Gates::PercentageOfActors do
53
32
  }
54
33
 
55
34
  let(:feature_one_enabled_actors) do
56
- gate = described_class.new(:name_one)
57
- actors.select { |actor| gate.open? actor, percentage_as_integer }
35
+ gate = described_class.new
36
+ actors.select { |actor| gate.open? actor, percentage_as_integer, feature_name: :name_one }
58
37
  end
59
38
 
60
39
  let(:feature_two_enabled_actors) do
61
- gate = described_class.new(:name_two)
62
- actors.select { |actor| gate.open? actor, percentage_as_integer }
40
+ gate = described_class.new
41
+ actors.select { |actor| gate.open? actor, percentage_as_integer, feature_name: :name_two }
63
42
  end
64
43
 
65
44
  it "does not enable both features for same set of actors" do
@@ -0,0 +1,23 @@
1
+ require 'helper'
2
+
3
+ describe Flipper::Gates::PercentageOfTime do
4
+ let(:feature_name) { :search }
5
+
6
+ subject {
7
+ described_class.new
8
+ }
9
+
10
+ describe "#description" do
11
+ context "when enabled" do
12
+ it "returns text" do
13
+ subject.description(22).should eq('22% of the time')
14
+ end
15
+ end
16
+
17
+ context "when disabled" do
18
+ it "returns disabled" do
19
+ subject.description(0).should eq('disabled')
20
+ end
21
+ end
22
+ end
23
+ end
@@ -7,8 +7,7 @@ require 'flipper/adapters/memory'
7
7
  describe Flipper::Middleware::Memoizer do
8
8
  include Rack::Test::Methods
9
9
 
10
- let(:source) { {} }
11
- let(:memory_adapter) { Flipper::Adapters::Memory.new(source) }
10
+ let(:memory_adapter) { Flipper::Adapters::Memory.new }
12
11
  let(:adapter) {
13
12
  Flipper::Adapters::OperationLogger.new(memory_adapter)
14
13
  }