flipper 0.6.3 → 0.7.0.beta1

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.
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
  }