flipper 0.10.2 → 0.11.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 (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
@@ -0,0 +1,76 @@
1
+ require 'helper'
2
+
3
+ RSpec.describe Flipper::Middleware::SetupEnv do
4
+ context 'with flipper instance' do
5
+ let(:app) do
6
+ app = lambda do |env|
7
+ [200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]]
8
+ end
9
+ builder = Rack::Builder.new
10
+ builder.use described_class, flipper
11
+ builder.run app
12
+ builder
13
+ end
14
+
15
+ it 'sets flipper in env' do
16
+ get '/'
17
+ expect(last_response.body).to eq(flipper.object_id.to_s)
18
+ end
19
+ end
20
+
21
+ context 'with block that returns flipper instance' do
22
+ let(:flipper_block) do
23
+ -> { flipper }
24
+ end
25
+ let(:app) do
26
+ app = lambda do |env|
27
+ [200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]]
28
+ end
29
+ builder = Rack::Builder.new
30
+ builder.use described_class, flipper_block
31
+ builder.run app
32
+ builder
33
+ end
34
+
35
+ it 'sets flipper in env' do
36
+ get '/'
37
+ expect(last_response.body).to eq(flipper.object_id.to_s)
38
+ end
39
+ end
40
+
41
+ context 'when env already has flipper setup' do
42
+ let(:app) do
43
+ app = lambda do |env|
44
+ [200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]]
45
+ end
46
+ builder = Rack::Builder.new
47
+ builder.use described_class, flipper
48
+ builder.run app
49
+ builder
50
+ end
51
+
52
+ it 'leaves env flipper alone' do
53
+ env_flipper = build_flipper
54
+ get '/', {}, 'flipper' => env_flipper
55
+ expect(last_response.body).to eq(env_flipper.object_id.to_s)
56
+ end
57
+ end
58
+
59
+ context 'when flipper instance is nil' do
60
+ let(:app) do
61
+ app = lambda do |env|
62
+ [200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]]
63
+ end
64
+ builder = Rack::Builder.new
65
+ builder.use described_class, nil
66
+ builder.run app
67
+ builder
68
+ end
69
+
70
+ it 'leaves env flipper alone' do
71
+ env_flipper = build_flipper
72
+ get '/', {}, 'flipper' => env_flipper
73
+ expect(last_response.body).to eq(env_flipper.object_id.to_s)
74
+ end
75
+ end
76
+ end
@@ -2,123 +2,119 @@ require 'helper'
2
2
  require 'flipper/registry'
3
3
 
4
4
  RSpec.describe Flipper::Registry do
5
- subject { Flipper::Registry.new(source) }
5
+ subject { described_class.new(source) }
6
6
 
7
7
  let(:source) { {} }
8
8
 
9
- describe "#add" do
10
- it "adds to source" do
9
+ describe '#add' do
10
+ it 'adds to source' do
11
11
  value = 'thing'
12
12
  subject.add(:admins, value)
13
13
  expect(source[:admins]).to eq(value)
14
14
  end
15
15
 
16
- it "converts key to symbol" do
16
+ it 'converts key to symbol' do
17
17
  value = 'thing'
18
18
  subject.add('admins', value)
19
19
  expect(source[:admins]).to eq(value)
20
20
  end
21
21
 
22
- it "raises exception if key already registered" do
22
+ it 'raises exception if key already registered' do
23
23
  subject.add(:admins, 'thing')
24
24
 
25
- expect {
25
+ expect do
26
26
  subject.add('admins', 'again')
27
- }.to raise_error(Flipper::Registry::DuplicateKey)
27
+ end.to raise_error(Flipper::Registry::DuplicateKey)
28
28
  end
29
29
  end
30
30
 
31
- describe "#get" do
32
- context "key registered" do
31
+ describe '#get' do
32
+ context 'key registered' do
33
33
  before do
34
34
  source[:admins] = 'thing'
35
35
  end
36
36
 
37
- it "returns value" do
37
+ it 'returns value' do
38
38
  expect(subject.get(:admins)).to eq('thing')
39
39
  end
40
40
 
41
- it "returns value if given string key" do
41
+ it 'returns value if given string key' do
42
42
  expect(subject.get('admins')).to eq('thing')
43
43
  end
44
44
  end
45
45
 
46
- context "key not registered" do
47
- it "raises key not found" do
48
- expect {
49
- subject.get(:admins)
50
- }.to raise_error(Flipper::Registry::KeyNotFound)
46
+ context 'key not registered' do
47
+ it 'returns nil' do
48
+ expect(subject.get(:admins)).to be(nil)
51
49
  end
52
50
  end
53
51
  end
54
52
 
55
- describe "#key?" do
53
+ describe '#key?' do
56
54
  before do
57
- source[:admins] = "admins"
55
+ source[:admins] = 'admins'
58
56
  end
59
57
 
60
- it "returns true if the key exists" do
58
+ it 'returns true if the key exists' do
61
59
  expect(subject.key?(:admins)).to eq true
62
60
  end
63
61
 
64
- it "returns false if the key does not exists" do
62
+ it 'returns false if the key does not exists' do
65
63
  expect(subject.key?(:unknown_key)).to eq false
66
64
  end
67
65
  end
68
66
 
69
- describe "#each" do
67
+ describe '#each' do
70
68
  before do
71
69
  source[:admins] = 'admins'
72
70
  source[:devs] = 'devs'
73
71
  end
74
72
 
75
- it "iterates source keys and values" do
73
+ it 'iterates source keys and values' do
76
74
  results = {}
77
75
  subject.each do |key, value|
78
76
  results[key] = value
79
77
  end
80
- expect(results).to eq({
81
- :admins => 'admins',
82
- :devs => 'devs',
83
- })
78
+ expect(results).to eq(admins: 'admins',
79
+ devs: 'devs')
84
80
  end
85
81
  end
86
82
 
87
- describe "#keys" do
83
+ describe '#keys' do
88
84
  before do
89
85
  source[:admins] = 'admins'
90
86
  source[:devs] = 'devs'
91
87
  end
92
88
 
93
- it "returns the keys" do
94
- expect(subject.keys.map(&:to_s).sort).to eq(['admins', 'devs'])
89
+ it 'returns the keys' do
90
+ expect(subject.keys.map(&:to_s).sort).to eq(%w(admins devs))
95
91
  end
96
92
 
97
- it "returns the keys as symbols" do
93
+ it 'returns the keys as symbols' do
98
94
  subject.keys.each do |key|
99
95
  expect(key).to be_instance_of(Symbol)
100
96
  end
101
97
  end
102
98
  end
103
99
 
104
- describe "#values" do
100
+ describe '#values' do
105
101
  before do
106
102
  source[:admins] = 'admins'
107
103
  source[:devs] = 'devs'
108
104
  end
109
105
 
110
- it "returns the values" do
111
- expect(subject.values.map(&:to_s).sort).to eq(['admins', 'devs'])
106
+ it 'returns the values' do
107
+ expect(subject.values.map(&:to_s).sort).to eq(%w(admins devs))
112
108
  end
113
109
  end
114
110
 
115
- describe "enumeration" do
111
+ describe 'enumeration' do
116
112
  before do
117
113
  source[:admins] = 'admins'
118
114
  source[:devs] = 'devs'
119
115
  end
120
116
 
121
- it "works" do
117
+ it 'works' do
122
118
  keys = []
123
119
  values = []
124
120
 
@@ -127,17 +123,17 @@ RSpec.describe Flipper::Registry do
127
123
  values << value
128
124
  end
129
125
 
130
- expect(keys.map(&:to_s).sort).to eq(['admins', 'devs'])
131
- expect(values.sort).to eq(['admins', 'devs'])
126
+ expect(keys.map(&:to_s).sort).to eq(%w(admins devs))
127
+ expect(values.sort).to eq(%w(admins devs))
132
128
  end
133
129
  end
134
130
 
135
- describe "#clear" do
131
+ describe '#clear' do
136
132
  before do
137
133
  source[:admins] = 'admins'
138
134
  end
139
135
 
140
- it "clears the source" do
136
+ it 'clears the source' do
141
137
  subject.clear
142
138
  expect(source).to be_empty
143
139
  end
@@ -4,15 +4,15 @@ require 'flipper/typecast'
4
4
  RSpec.describe Flipper::Typecast 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 "#to_boolean for #{value.inspect}" do
18
18
  it "returns #{expected}" do
@@ -23,11 +23,11 @@ RSpec.describe Flipper::Typecast 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 "#to_integer for #{value.inspect}" do
33
33
  it "returns #{expected}" do
@@ -38,9 +38,9 @@ RSpec.describe Flipper::Typecast do
38
38
 
39
39
  {
40
40
  nil => Set.new,
41
- "" => Set.new,
41
+ '' => Set.new,
42
42
  Set.new([1, 2]) => Set.new([1, 2]),
43
- [1, 2] => Set.new([1, 2])
43
+ [1, 2] => Set.new([1, 2]),
44
44
  }.each do |value, expected|
45
45
  context "#to_set for #{value.inspect}" do
46
46
  it "returns #{expected}" do
@@ -49,15 +49,15 @@ RSpec.describe Flipper::Typecast do
49
49
  end
50
50
  end
51
51
 
52
- it "raises argument error for integer value that cannot be converted to an integer" do
53
- expect {
54
- described_class.to_integer(["asdf"])
55
- }.to raise_error(ArgumentError, %Q(["asdf"] cannot be converted to an integer))
52
+ it 'raises argument error for integer value that cannot be converted to an integer' do
53
+ expect do
54
+ described_class.to_integer(['asdf'])
55
+ end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to an integer))
56
56
  end
57
57
 
58
- it "raises argument error for set value that cannot be converted to a set" do
59
- expect {
60
- described_class.to_set("asdf")
61
- }.to raise_error(ArgumentError, %Q("asdf" cannot be converted to a set))
58
+ it 'raises argument error for set value that cannot be converted to a set' do
59
+ expect do
60
+ described_class.to_set('asdf')
61
+ end.to raise_error(ArgumentError, %("asdf" cannot be converted to a set))
62
62
  end
63
63
  end
@@ -2,13 +2,13 @@ require 'helper'
2
2
  require 'flipper/types/actor'
3
3
 
4
4
  RSpec.describe Flipper::Types::Actor do
5
- subject {
5
+ subject do
6
6
  thing = thing_class.new('2')
7
7
  described_class.new(thing)
8
- }
8
+ end
9
9
 
10
- let(:thing_class) {
11
- Class.new {
10
+ let(:thing_class) do
11
+ Class.new do
12
12
  attr_reader :flipper_id
13
13
 
14
14
  def initialize(flipper_id)
@@ -18,37 +18,37 @@ RSpec.describe Flipper::Types::Actor do
18
18
  def admin?
19
19
  true
20
20
  end
21
- }
22
- }
21
+ end
22
+ end
23
23
 
24
- describe ".wrappable?" do
25
- it "returns true if actor" do
24
+ describe '.wrappable?' do
25
+ it 'returns true if actor' do
26
26
  thing = thing_class.new('1')
27
27
  actor = described_class.new(thing)
28
28
  expect(described_class.wrappable?(actor)).to eq(true)
29
29
  end
30
30
 
31
- it "returns true if responds to flipper_id" do
31
+ it 'returns true if responds to flipper_id' do
32
32
  thing = thing_class.new(10)
33
33
  expect(described_class.wrappable?(thing)).to eq(true)
34
34
  end
35
35
 
36
- it "returns false if nil" do
36
+ it 'returns false if nil' do
37
37
  expect(described_class.wrappable?(nil)).to be(false)
38
38
  end
39
39
  end
40
40
 
41
- describe ".wrap" do
42
- context "for actor" do
43
- it "returns actor" do
41
+ describe '.wrap' do
42
+ context 'for actor' do
43
+ it 'returns actor' do
44
44
  actor = described_class.wrap(subject)
45
45
  expect(actor).to be_instance_of(described_class)
46
46
  expect(actor).to be(subject)
47
47
  end
48
48
  end
49
49
 
50
- context "for other thing" do
51
- it "returns actor" do
50
+ context 'for other thing' do
51
+ it 'returns actor' do
52
52
  thing = thing_class.new('1')
53
53
  actor = described_class.wrap(thing)
54
54
  expect(actor).to be_instance_of(described_class)
@@ -56,57 +56,58 @@ RSpec.describe Flipper::Types::Actor do
56
56
  end
57
57
  end
58
58
 
59
- it "initializes with thing that responds to id" do
59
+ it 'initializes with thing that responds to id' do
60
60
  thing = thing_class.new('1')
61
61
  actor = described_class.new(thing)
62
62
  expect(actor.value).to eq('1')
63
63
  end
64
64
 
65
- it "raises error when initialized with nil" do
66
- expect {
65
+ it 'raises error when initialized with nil' do
66
+ expect do
67
67
  described_class.new(nil)
68
- }.to raise_error(ArgumentError)
68
+ end.to raise_error(ArgumentError)
69
69
  end
70
70
 
71
- it "raises error when initalized with non-wrappable object" do
71
+ it 'raises error when initalized with non-wrappable object' do
72
72
  unwrappable_thing = Struct.new(:id).new(1)
73
- expect {
73
+ expect do
74
74
  described_class.new(unwrappable_thing)
75
- }.to raise_error(ArgumentError, "#{unwrappable_thing.inspect} must respond to flipper_id, but does not")
75
+ end.to raise_error(ArgumentError,
76
+ "#{unwrappable_thing.inspect} must respond to flipper_id, but does not")
76
77
  end
77
78
 
78
- it "converts id to string" do
79
+ it 'converts id to string' do
79
80
  thing = thing_class.new(2)
80
81
  actor = described_class.new(thing)
81
82
  expect(actor.value).to eq('2')
82
83
  end
83
84
 
84
- it "proxies everything to thing" do
85
+ it 'proxies everything to thing' do
85
86
  thing = thing_class.new(10)
86
87
  actor = described_class.new(thing)
87
88
  expect(actor.admin?).to eq(true)
88
89
  end
89
90
 
90
- it "exposes thing" do
91
+ it 'exposes thing' do
91
92
  thing = thing_class.new(10)
92
93
  actor = described_class.new(thing)
93
94
  expect(actor.thing).to be(thing)
94
95
  end
95
96
 
96
- describe "#respond_to?" do
97
- it "returns true if responds to method" do
97
+ describe '#respond_to?' do
98
+ it 'returns true if responds to method' do
98
99
  thing = thing_class.new('1')
99
100
  actor = described_class.new(thing)
100
101
  expect(actor.respond_to?(:value)).to eq(true)
101
102
  end
102
103
 
103
- it "returns true if thing responds to method" do
104
+ it 'returns true if thing responds to method' do
104
105
  thing = thing_class.new(10)
105
106
  actor = described_class.new(thing)
106
107
  expect(actor.respond_to?(:admin?)).to eq(true)
107
108
  end
108
109
 
109
- it "returns false if does not respond to method and thing does not respond to method" do
110
+ it 'returns false if does not respond to method and thing does not respond to method' do
110
111
  thing = thing_class.new(10)
111
112
  actor = described_class.new(thing)
112
113
  expect(actor.respond_to?(:frankenstein)).to eq(false)