flipper 0.10.2 → 0.11.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +42 -0
  3. data/.rubocop_todo.yml +188 -0
  4. data/Changelog.md +10 -0
  5. data/Gemfile +6 -3
  6. data/README.md +4 -3
  7. data/Rakefile +13 -13
  8. data/docs/Adapters.md +2 -1
  9. data/docs/DockerCompose.md +6 -3
  10. data/docs/Gates.md +25 -3
  11. data/docs/Optimization.md +27 -5
  12. data/docs/api/README.md +73 -32
  13. data/docs/http/README.md +34 -0
  14. data/docs/read-only/README.md +22 -0
  15. data/examples/percentage_of_actors_group.rb +49 -0
  16. data/flipper.gemspec +15 -15
  17. data/lib/flipper.rb +2 -5
  18. data/lib/flipper/adapter.rb +10 -0
  19. data/lib/flipper/adapters/http.rb +147 -0
  20. data/lib/flipper/adapters/http/client.rb +83 -0
  21. data/lib/flipper/adapters/http/error.rb +14 -0
  22. data/lib/flipper/adapters/instrumented.rb +36 -36
  23. data/lib/flipper/adapters/memoizable.rb +2 -6
  24. data/lib/flipper/adapters/memory.rb +10 -9
  25. data/lib/flipper/adapters/operation_logger.rb +1 -1
  26. data/lib/flipper/adapters/pstore.rb +12 -11
  27. data/lib/flipper/adapters/read_only.rb +6 -6
  28. data/lib/flipper/dsl.rb +1 -3
  29. data/lib/flipper/feature.rb +11 -16
  30. data/lib/flipper/gate.rb +3 -3
  31. data/lib/flipper/gate_values.rb +6 -6
  32. data/lib/flipper/gates/group.rb +2 -2
  33. data/lib/flipper/gates/percentage_of_actors.rb +2 -2
  34. data/lib/flipper/instrumentation/log_subscriber.rb +2 -4
  35. data/lib/flipper/instrumentation/metriks.rb +1 -1
  36. data/lib/flipper/instrumentation/statsd.rb +1 -1
  37. data/lib/flipper/instrumentation/statsd_subscriber.rb +1 -3
  38. data/lib/flipper/instrumentation/subscriber.rb +11 -10
  39. data/lib/flipper/instrumenters/memory.rb +1 -5
  40. data/lib/flipper/instrumenters/noop.rb +1 -1
  41. data/lib/flipper/middleware/memoizer.rb +11 -27
  42. data/lib/flipper/middleware/setup_env.rb +44 -0
  43. data/lib/flipper/registry.rb +8 -10
  44. data/lib/flipper/spec/shared_adapter_specs.rb +45 -67
  45. data/lib/flipper/test/shared_adapter_test.rb +25 -31
  46. data/lib/flipper/typecast.rb +2 -2
  47. data/lib/flipper/types/actor.rb +2 -4
  48. data/lib/flipper/types/group.rb +1 -1
  49. data/lib/flipper/types/percentage.rb +2 -1
  50. data/lib/flipper/version.rb +1 -1
  51. data/spec/fixtures/feature.json +31 -0
  52. data/spec/flipper/adapters/http_spec.rb +148 -0
  53. data/spec/flipper/adapters/instrumented_spec.rb +20 -20
  54. data/spec/flipper/adapters/memoizable_spec.rb +59 -59
  55. data/spec/flipper/adapters/operation_logger_spec.rb +16 -16
  56. data/spec/flipper/adapters/pstore_spec.rb +6 -6
  57. data/spec/flipper/adapters/read_only_spec.rb +28 -34
  58. data/spec/flipper/dsl_spec.rb +73 -84
  59. data/spec/flipper/feature_check_context_spec.rb +27 -27
  60. data/spec/flipper/feature_spec.rb +186 -196
  61. data/spec/flipper/gate_spec.rb +11 -11
  62. data/spec/flipper/gate_values_spec.rb +46 -45
  63. data/spec/flipper/gates/actor_spec.rb +2 -2
  64. data/spec/flipper/gates/boolean_spec.rb +24 -23
  65. data/spec/flipper/gates/group_spec.rb +19 -19
  66. data/spec/flipper/gates/percentage_of_actors_spec.rb +10 -10
  67. data/spec/flipper/gates/percentage_of_time_spec.rb +2 -2
  68. data/spec/flipper/instrumentation/log_subscriber_spec.rb +20 -20
  69. data/spec/flipper/instrumentation/metriks_subscriber_spec.rb +20 -20
  70. data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +11 -11
  71. data/spec/flipper/instrumenters/memory_spec.rb +5 -5
  72. data/spec/flipper/instrumenters/noop_spec.rb +6 -6
  73. data/spec/flipper/middleware/memoizer_spec.rb +83 -100
  74. data/spec/flipper/middleware/setup_env_spec.rb +76 -0
  75. data/spec/flipper/registry_spec.rb +35 -39
  76. data/spec/flipper/typecast_spec.rb +18 -18
  77. data/spec/flipper/types/actor_spec.rb +30 -29
  78. data/spec/flipper/types/boolean_spec.rb +8 -8
  79. data/spec/flipper/types/group_spec.rb +28 -28
  80. data/spec/flipper/types/percentage_spec.rb +14 -14
  81. data/spec/flipper_spec.rb +61 -54
  82. data/spec/helper.rb +26 -21
  83. data/spec/integration_spec.rb +121 -113
  84. data/spec/support/fake_udp_socket.rb +1 -1
  85. data/spec/support/spec_helpers.rb +32 -4
  86. data/test/adapters/pstore_test.rb +3 -3
  87. data/test/test_helper.rb +1 -1
  88. metadata +20 -5
@@ -3,14 +3,14 @@ require 'helper'
3
3
  RSpec.describe Flipper::Gate do
4
4
  let(:feature_name) { :stats }
5
5
 
6
- subject {
6
+ subject do
7
7
  described_class.new
8
- }
8
+ end
9
9
 
10
- describe "#inspect" do
11
- context "for subclass" do
12
- let(:subclass) {
13
- Class.new(described_class) {
10
+ describe '#inspect' do
11
+ context 'for subclass' do
12
+ let(:subclass) do
13
+ Class.new(described_class) do
14
14
  def name
15
15
  :name
16
16
  end
@@ -22,14 +22,14 @@ RSpec.describe Flipper::Gate do
22
22
  def data_type
23
23
  :set
24
24
  end
25
- }
26
- }
25
+ end
26
+ end
27
27
 
28
- subject {
28
+ subject do
29
29
  subclass.new
30
- }
30
+ end
31
31
 
32
- it "includes attributes" do
32
+ it 'includes attributes' do
33
33
  string = subject.inspect
34
34
  expect(string).to include(subject.object_id.to_s)
35
35
  expect(string).to include('name=:name')
@@ -4,15 +4,15 @@ require 'flipper/gate_values'
4
4
  RSpec.describe Flipper::GateValues do
5
5
  {
6
6
  nil => false,
7
- "" => false,
7
+ '' => false,
8
8
  0 => false,
9
9
  1 => true,
10
- "0" => false,
11
- "1" => true,
10
+ '0' => false,
11
+ '1' => true,
12
12
  true => true,
13
13
  false => false,
14
- "true" => true,
15
- "false" => false,
14
+ 'true' => true,
15
+ 'false' => false,
16
16
  }.each do |value, expected|
17
17
  context "with #{value.inspect} boolean" do
18
18
  it "returns #{expected}" do
@@ -23,11 +23,11 @@ RSpec.describe Flipper::GateValues do
23
23
 
24
24
  {
25
25
  nil => 0,
26
- "" => 0,
26
+ '' => 0,
27
27
  0 => 0,
28
28
  1 => 1,
29
- "1" => 1,
30
- "99" => 99,
29
+ '1' => 1,
30
+ '99' => 99,
31
31
  }.each do |value, expected|
32
32
  context "with #{value.inspect} percentage of time" do
33
33
  it "returns #{expected}" do
@@ -38,24 +38,25 @@ RSpec.describe Flipper::GateValues do
38
38
 
39
39
  {
40
40
  nil => 0,
41
- "" => 0,
41
+ '' => 0,
42
42
  0 => 0,
43
43
  1 => 1,
44
- "1" => 1,
45
- "99" => 99,
44
+ '1' => 1,
45
+ '99' => 99,
46
46
  }.each do |value, expected|
47
47
  context "with #{value.inspect} percentage of actors" do
48
48
  it "returns #{expected}" do
49
- expect(described_class.new(percentage_of_actors: value).percentage_of_actors).to be(expected)
49
+ expect(described_class.new(percentage_of_actors: value).percentage_of_actors)
50
+ .to be(expected)
50
51
  end
51
52
  end
52
53
  end
53
54
 
54
55
  {
55
56
  nil => Set.new,
56
- "" => Set.new,
57
+ '' => Set.new,
57
58
  Set.new([1, 2]) => Set.new([1, 2]),
58
- [1, 2] => Set.new([1, 2])
59
+ [1, 2] => Set.new([1, 2]),
59
60
  }.each do |value, expected|
60
61
  context "with #{value.inspect} actors" do
61
62
  it "returns #{expected}" do
@@ -66,9 +67,9 @@ RSpec.describe Flipper::GateValues do
66
67
 
67
68
  {
68
69
  nil => Set.new,
69
- "" => Set.new,
70
+ '' => Set.new,
70
71
  Set.new([:admins, :preview_features]) => Set.new([:admins, :preview_features]),
71
- [:admins, :preview_features] => Set.new([:admins, :preview_features])
72
+ [:admins, :preview_features] => Set.new([:admins, :preview_features]),
72
73
  }.each do |value, expected|
73
74
  context "with #{value.inspect} groups" do
74
75
  it "returns #{expected}" do
@@ -77,58 +78,58 @@ RSpec.describe Flipper::GateValues do
77
78
  end
78
79
  end
79
80
 
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))
81
+ it 'raises argument error for percentage of time value that cannot be converted to an integer' do
82
+ expect do
83
+ described_class.new(percentage_of_time: ['asdf'])
84
+ end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to an integer))
84
85
  end
85
86
 
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))
87
+ it 'raises argument error for percentage of actors value that cannot be converted to an int' do
88
+ expect do
89
+ described_class.new(percentage_of_actors: ['asdf'])
90
+ end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to an integer))
90
91
  end
91
92
 
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))
93
+ it 'raises argument error for actors value that cannot be converted to a set' do
94
+ expect do
95
+ described_class.new(actors: 'asdf')
96
+ end.to raise_error(ArgumentError, %("asdf" cannot be converted to a set))
96
97
  end
97
98
 
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))
99
+ it 'raises argument error for groups value that cannot be converted to a set' do
100
+ expect do
101
+ described_class.new(groups: 'asdf')
102
+ end.to raise_error(ArgumentError, %("asdf" cannot be converted to a set))
102
103
  end
103
104
 
104
- describe "#[]" do
105
- it "can read the boolean value" do
105
+ describe '#[]' do
106
+ it 'can read the boolean value' do
106
107
  expect(described_class.new(boolean: true)[:boolean]).to be(true)
107
- expect(described_class.new(boolean: true)["boolean"]).to be(true)
108
+ expect(described_class.new(boolean: true)['boolean']).to be(true)
108
109
  end
109
110
 
110
- it "can read the actors value" do
111
+ it 'can read the actors value' do
111
112
  expect(described_class.new(actors: Set[1, 2])[:actors]).to eq(Set[1, 2])
112
- expect(described_class.new(actors: Set[1, 2])["actors"]).to eq(Set[1, 2])
113
+ expect(described_class.new(actors: Set[1, 2])['actors']).to eq(Set[1, 2])
113
114
  end
114
115
 
115
- it "can read the groups value" do
116
+ it 'can read the groups value' do
116
117
  expect(described_class.new(groups: Set[:admins])[:groups]).to eq(Set[:admins])
117
- expect(described_class.new(groups: Set[:admins])["groups"]).to eq(Set[:admins])
118
+ expect(described_class.new(groups: Set[:admins])['groups']).to eq(Set[:admins])
118
119
  end
119
120
 
120
- it "can read the percentage of time value" do
121
+ it 'can read the percentage of time value' do
121
122
  expect(described_class.new(percentage_of_time: 15)[:percentage_of_time]).to eq(15)
122
- expect(described_class.new(percentage_of_time: 15)["percentage_of_time"]).to eq(15)
123
+ expect(described_class.new(percentage_of_time: 15)['percentage_of_time']).to eq(15)
123
124
  end
124
125
 
125
- it "can read the percentage of actors value" do
126
+ it 'can read the percentage of actors value' do
126
127
  expect(described_class.new(percentage_of_actors: 15)[:percentage_of_actors]).to eq(15)
127
- expect(described_class.new(percentage_of_actors: 15)["percentage_of_actors"]).to eq(15)
128
+ expect(described_class.new(percentage_of_actors: 15)['percentage_of_actors']).to eq(15)
128
129
  end
129
130
 
130
- it "returns nil for value that is not present" do
131
- expect(described_class.new({})["not legit"]).to be(nil)
131
+ it 'returns nil for value that is not present' do
132
+ expect(described_class.new({})['not legit']).to be(nil)
132
133
  end
133
134
  end
134
135
  end
@@ -3,7 +3,7 @@ require 'helper'
3
3
  RSpec.describe Flipper::Gates::Actor do
4
4
  let(:feature_name) { :search }
5
5
 
6
- subject {
6
+ subject do
7
7
  described_class.new
8
- }
8
+ end
9
9
  end
@@ -3,71 +3,72 @@ require 'helper'
3
3
  RSpec.describe Flipper::Gates::Boolean do
4
4
  let(:feature_name) { :search }
5
5
 
6
- subject {
6
+ subject do
7
7
  described_class.new
8
- }
8
+ end
9
9
 
10
10
  def context(bool)
11
11
  Flipper::FeatureCheckContext.new(
12
12
  feature_name: feature_name,
13
- values: Flipper::GateValues.new({boolean: bool}),
14
- thing: Flipper::Types::Actor.new(Struct.new(:flipper_id).new(1)),
13
+ values: Flipper::GateValues.new(boolean: bool),
14
+ thing: Flipper::Types::Actor.new(Struct.new(:flipper_id).new(1))
15
15
  )
16
16
  end
17
17
 
18
- describe "#enabled?" do
19
- context "for true value" do
20
- it "returns true" do
18
+ describe '#enabled?' do
19
+ context 'for true value' do
20
+ it 'returns true' do
21
21
  expect(subject.enabled?(true)).to eq(true)
22
22
  end
23
23
  end
24
24
 
25
- context "for false value" do
26
- it "returns false" do
25
+ context 'for false value' do
26
+ it 'returns false' do
27
27
  expect(subject.enabled?(false)).to eq(false)
28
28
  end
29
29
  end
30
30
  end
31
31
 
32
- describe "#open?" do
33
- context "for true value" do
34
- it "returns true" do
32
+ describe '#open?' do
33
+ context 'for true value' do
34
+ it 'returns true' do
35
35
  expect(subject.open?(context(true))).to be(true)
36
36
  end
37
37
  end
38
38
 
39
- context "for false value" do
40
- it "returns false" do
39
+ context 'for false value' do
40
+ it 'returns false' do
41
41
  expect(subject.open?(context(false))).to be(false)
42
42
  end
43
43
  end
44
44
  end
45
45
 
46
- describe "#protects?" do
47
- it "returns true for boolean type" do
46
+ describe '#protects?' do
47
+ it 'returns true for boolean type' do
48
48
  expect(subject.protects?(Flipper::Types::Boolean.new(true))).to be(true)
49
49
  end
50
50
 
51
- it "returns true for true" do
51
+ it 'returns true for true' do
52
52
  expect(subject.protects?(true)).to be(true)
53
53
  end
54
54
 
55
- it "returns true for false" do
55
+ it 'returns true for false' do
56
56
  expect(subject.protects?(false)).to be(true)
57
57
  end
58
58
  end
59
59
 
60
- describe "#wrap" do
61
- it "returns boolean type for boolean type" do
62
- expect(subject.wrap(Flipper::Types::Boolean.new(true))).to be_instance_of(Flipper::Types::Boolean)
60
+ describe '#wrap' do
61
+ it 'returns boolean type for boolean type' do
62
+ expect(subject.wrap(Flipper::Types::Boolean.new(true)))
63
+ .to be_instance_of(Flipper::Types::Boolean)
63
64
  end
64
65
 
65
- it "returns boolean type for true" do
66
+ it 'returns boolean type for true' do
66
67
  expect(subject.wrap(true)).to be_instance_of(Flipper::Types::Boolean)
67
68
  expect(subject.wrap(true).value).to be(true)
68
69
  end
69
70
 
70
- it "returns boolean type for true" do
71
+ it 'returns boolean type for true' do
71
72
  expect(subject.wrap(false)).to be_instance_of(Flipper::Types::Boolean)
72
73
  expect(subject.wrap(false).value).to be(false)
73
74
  end
@@ -3,62 +3,62 @@ require 'helper'
3
3
  RSpec.describe Flipper::Gates::Group do
4
4
  let(:feature_name) { :search }
5
5
 
6
- subject {
6
+ subject do
7
7
  described_class.new
8
- }
8
+ end
9
9
 
10
10
  def context(set)
11
11
  Flipper::FeatureCheckContext.new(
12
12
  feature_name: feature_name,
13
- values: Flipper::GateValues.new({groups: set}),
14
- thing: Flipper::Types::Actor.new(Struct.new(:flipper_id).new("5")),
13
+ values: Flipper::GateValues.new(groups: set),
14
+ thing: Flipper::Types::Actor.new(Struct.new(:flipper_id).new('5'))
15
15
  )
16
16
  end
17
17
 
18
- describe "#open?" do
19
- context "with a group in adapter, but not registered" do
18
+ describe '#open?' do
19
+ context 'with a group in adapter, but not registered' do
20
20
  before do
21
- Flipper.register(:staff) { |thing| true }
21
+ Flipper.register(:staff) { |_thing| true }
22
22
  end
23
23
 
24
- it "ignores group" do
24
+ it 'ignores group' do
25
25
  thing = Struct.new(:flipper_id).new('5')
26
26
  expect(subject.open?(context(Set[:newbs, :staff]))).to be(true)
27
27
  end
28
28
  end
29
29
 
30
- context "thing that does not respond to method in group block" do
30
+ context 'thing that does not respond to method in group block' do
31
31
  before do
32
- Flipper.register(:stinkers) { |thing| thing.stinker? }
32
+ Flipper.register(:stinkers, &:stinker?)
33
33
  end
34
34
 
35
- it "raises error" do
36
- expect {
35
+ it 'raises error' do
36
+ expect do
37
37
  subject.open?(context(Set[:stinkers]))
38
- }.to raise_error(NoMethodError)
38
+ end.to raise_error(NoMethodError)
39
39
  end
40
40
  end
41
41
  end
42
42
 
43
- describe "#wrap" do
44
- it "returns group instance for symbol" do
43
+ describe '#wrap' do
44
+ it 'returns group instance for symbol' do
45
45
  group = Flipper.register(:admins) {}
46
46
  expect(subject.wrap(:admins)).to eq(group)
47
47
  end
48
48
 
49
- it "returns group instance for group instance" do
49
+ it 'returns group instance for group instance' do
50
50
  group = Flipper.register(:admins) {}
51
51
  expect(subject.wrap(group)).to eq(group)
52
52
  end
53
53
  end
54
54
 
55
- describe "#protects?" do
56
- it "returns true for group" do
55
+ describe '#protects?' do
56
+ it 'returns true for group' do
57
57
  group = Flipper.register(:admins) {}
58
58
  expect(subject.protects?(group)).to be(true)
59
59
  end
60
60
 
61
- it "returns true for symbol" do
61
+ it 'returns true for symbol' do
62
62
  expect(subject.protects?(:admins)).to be(true)
63
63
  end
64
64
  end
@@ -3,27 +3,27 @@ require 'helper'
3
3
  RSpec.describe Flipper::Gates::PercentageOfActors do
4
4
  let(:feature_name) { :search }
5
5
 
6
- subject {
6
+ subject do
7
7
  described_class.new
8
- }
8
+ end
9
9
 
10
10
  def context(integer, feature = feature_name, thing = nil)
11
11
  Flipper::FeatureCheckContext.new(
12
12
  feature_name: feature,
13
- values: Flipper::GateValues.new({percentage_of_actors: integer}),
14
- thing: thing || Flipper::Types::Actor.new(Struct.new(:flipper_id).new(1)),
13
+ values: Flipper::GateValues.new(percentage_of_actors: integer),
14
+ thing: thing || Flipper::Types::Actor.new(Struct.new(:flipper_id).new(1))
15
15
  )
16
16
  end
17
17
 
18
- describe "#open?" do
19
- context "when compared against two features" do
18
+ describe '#open?' do
19
+ context 'when compared against two features' do
20
20
  let(:percentage) { 0.05 }
21
21
  let(:percentage_as_integer) { percentage * 100 }
22
22
  let(:number_of_actors) { 100 }
23
23
 
24
- let(:actors) {
24
+ let(:actors) do
25
25
  (1..number_of_actors).map { |n| Struct.new(:flipper_id).new(n) }
26
- }
26
+ end
27
27
 
28
28
  let(:feature_one_enabled_actors) do
29
29
  gate = described_class.new
@@ -35,11 +35,11 @@ RSpec.describe Flipper::Gates::PercentageOfActors do
35
35
  actors.select { |actor| gate.open? context(percentage_as_integer, :name_two, actor) }
36
36
  end
37
37
 
38
- it "does not enable both features for same set of actors" do
38
+ it 'does not enable both features for same set of actors' do
39
39
  expect(feature_one_enabled_actors).not_to eq(feature_two_enabled_actors)
40
40
  end
41
41
 
42
- it "enables feature for accurate number of actors for each feature" do
42
+ it 'enables feature for accurate number of actors for each feature' do
43
43
  margin_of_error = 0.02 * number_of_actors # 2 percent margin of error
44
44
  expected_enabled_size = number_of_actors * percentage
45
45
 
@@ -3,7 +3,7 @@ require 'helper'
3
3
  RSpec.describe Flipper::Gates::PercentageOfTime do
4
4
  let(:feature_name) { :search }
5
5
 
6
- subject {
6
+ subject do
7
7
  described_class.new
8
- }
8
+ end
9
9
  end