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
@@ -2,23 +2,23 @@ require 'helper'
2
2
  require 'flipper/types/boolean'
3
3
 
4
4
  RSpec.describe Flipper::Types::Boolean do
5
- it "defaults value to true" do
6
- boolean = Flipper::Types::Boolean.new
5
+ it 'defaults value to true' do
6
+ boolean = described_class.new
7
7
  expect(boolean.value).to be(true)
8
8
  end
9
9
 
10
- it "allows overriding default value" do
11
- boolean = Flipper::Types::Boolean.new(false)
10
+ it 'allows overriding default value' do
11
+ boolean = described_class.new(false)
12
12
  expect(boolean.value).to be(false)
13
13
  end
14
14
 
15
- it "returns true for nil value" do
16
- boolean = Flipper::Types::Boolean.new(nil)
15
+ it 'returns true for nil value' do
16
+ boolean = described_class.new(nil)
17
17
  expect(boolean.value).to be(true)
18
18
  end
19
19
 
20
- it "typecasts value" do
21
- boolean = Flipper::Types::Boolean.new(1)
20
+ it 'typecasts value' do
21
+ boolean = described_class.new(1)
22
22
  expect(boolean.value).to be(true)
23
23
  end
24
24
  end
@@ -2,85 +2,85 @@ require 'helper'
2
2
  require 'flipper/types/group'
3
3
 
4
4
  RSpec.describe Flipper::Types::Group do
5
- let(:fake_context) { double("FeatureCheckContext") }
5
+ let(:fake_context) { double('FeatureCheckContext') }
6
6
 
7
7
  subject do
8
- Flipper.register(:admins) { |actor| actor.admin? }
8
+ Flipper.register(:admins, &:admin?)
9
9
  end
10
10
 
11
- describe ".wrap" do
12
- context "with group instance" do
13
- it "returns group instance" do
11
+ describe '.wrap' do
12
+ context 'with group instance' do
13
+ it 'returns group instance' do
14
14
  expect(described_class.wrap(subject)).to eq(subject)
15
15
  end
16
16
  end
17
17
 
18
- context "with Symbol group name" do
19
- it "returns group instance" do
18
+ context 'with Symbol group name' do
19
+ it 'returns group instance' do
20
20
  expect(described_class.wrap(subject.name)).to eq(subject)
21
21
  end
22
22
  end
23
23
 
24
- context "with String group name" do
25
- it "returns group instance" do
24
+ context 'with String group name' do
25
+ it 'returns group instance' do
26
26
  expect(described_class.wrap(subject.name.to_s)).to eq(subject)
27
27
  end
28
28
  end
29
29
  end
30
30
 
31
- it "initializes with name" do
32
- group = Flipper::Types::Group.new(:admins)
33
- expect(group).to be_instance_of(Flipper::Types::Group)
31
+ it 'initializes with name' do
32
+ group = described_class.new(:admins)
33
+ expect(group).to be_instance_of(described_class)
34
34
  end
35
35
 
36
- describe "#name" do
37
- it "returns name" do
36
+ describe '#name' do
37
+ it 'returns name' do
38
38
  expect(subject.name).to eq(:admins)
39
39
  end
40
40
  end
41
41
 
42
- describe "#match?" do
43
- let(:admin_actor) { double('Actor', :admin? => true) }
44
- let(:non_admin_actor) { double('Actor', :admin? => false) }
42
+ describe '#match?' do
43
+ let(:admin_actor) { double('Actor', admin?: true) }
44
+ let(:non_admin_actor) { double('Actor', admin?: false) }
45
45
 
46
- it "returns true if block matches" do
46
+ it 'returns true if block matches' do
47
47
  expect(subject.match?(admin_actor, fake_context)).to eq(true)
48
48
  end
49
49
 
50
- it "returns false if block does not match" do
50
+ it 'returns false if block does not match' do
51
51
  expect(subject.match?(non_admin_actor, fake_context)).to eq(false)
52
52
  end
53
53
 
54
- it "returns true for truthy block results" do
55
- group = Flipper::Types::Group.new(:examples) do |actor|
54
+ it 'returns true for truthy block results' do
55
+ group = described_class.new(:examples) do |actor|
56
56
  actor.email =~ /@example\.com/
57
57
  end
58
- expect(group.match?(double('Actor', :email => "foo@example.com"), fake_context)).to be_truthy
58
+ expect(group.match?(double('Actor', email: 'foo@example.com'), fake_context)).to be_truthy
59
59
  end
60
60
 
61
- it "returns false for falsey block results" do
62
- group = Flipper::Types::Group.new(:examples) do |actor|
61
+ it 'returns false for falsey block results' do
62
+ group = described_class.new(:examples) do |_actor|
63
63
  nil
64
64
  end
65
65
  expect(group.match?(double('Actor'), fake_context)).to be_falsey
66
66
  end
67
67
 
68
- it "can yield without context as block argument" do
68
+ it 'can yield without context as block argument' do
69
69
  context = Flipper::FeatureCheckContext.new(
70
70
  feature_name: :my_feature,
71
71
  values: Flipper::GateValues.new({}),
72
- thing: Flipper::Types::Actor.new(Struct.new(:flipper_id).new(1)),
72
+ thing: Flipper::Types::Actor.new(Struct.new(:flipper_id).new(1))
73
73
  )
74
74
  group = Flipper.register(:group_with_context) { |actor| actor }
75
75
  yielded_actor = group.match?(admin_actor, context)
76
76
  expect(yielded_actor).to be(admin_actor)
77
77
  end
78
78
 
79
- it "can yield with context as block argument" do
79
+ it 'can yield with context as block argument' do
80
80
  context = Flipper::FeatureCheckContext.new(
81
81
  feature_name: :my_feature,
82
82
  values: Flipper::GateValues.new({}),
83
- thing: Flipper::Types::Actor.new(Struct.new(:flipper_id).new(1)),
83
+ thing: Flipper::Types::Actor.new(Struct.new(:flipper_id).new(1))
84
84
  )
85
85
  group = Flipper.register(:group_with_context) { |actor, context| [actor, context] }
86
86
  yielded_actor, yielded_context = group.match?(admin_actor, context)
@@ -2,45 +2,45 @@ require 'helper'
2
2
  require 'flipper/types/percentage_of_actors'
3
3
 
4
4
  RSpec.describe Flipper::Types::Percentage do
5
- subject {
5
+ subject do
6
6
  described_class.new(5)
7
- }
7
+ end
8
8
  it_should_behave_like 'a percentage'
9
9
 
10
- describe ".wrap" do
11
- context "with percentage instance" do
12
- it "returns percentage instance" do
10
+ describe '.wrap' do
11
+ context 'with percentage instance' do
12
+ it 'returns percentage instance' do
13
13
  expect(described_class.wrap(subject)).to eq(subject)
14
14
  end
15
15
  end
16
16
 
17
- context "with Integer" do
18
- it "returns percentage instance" do
17
+ context 'with Integer' do
18
+ it 'returns percentage instance' do
19
19
  expect(described_class.wrap(subject.value)).to eq(subject)
20
20
  end
21
21
  end
22
22
 
23
- context "with String" do
24
- it "returns percentage instance" do
23
+ context 'with String' do
24
+ it 'returns percentage instance' do
25
25
  expect(described_class.wrap(subject.value.to_s)).to eq(subject)
26
26
  end
27
27
  end
28
28
  end
29
29
 
30
- describe "#eql?" do
31
- it "returns true for same class and value" do
30
+ describe '#eql?' do
31
+ it 'returns true for same class and value' do
32
32
  expect(subject.eql?(described_class.new(subject.value))).to eq(true)
33
33
  end
34
34
 
35
- it "returns false for different value" do
35
+ it 'returns false for different value' do
36
36
  expect(subject.eql?(described_class.new(subject.value + 1))).to eq(false)
37
37
  end
38
38
 
39
- it "returns false for different class" do
39
+ it 'returns false for different class' do
40
40
  expect(subject.eql?(Object.new)).to eq(false)
41
41
  end
42
42
 
43
- it "is aliased to ==" do
43
+ it 'is aliased to ==' do
44
44
  expect((subject == described_class.new(subject.value))).to eq(true)
45
45
  end
46
46
  end
@@ -1,109 +1,116 @@
1
1
  require 'helper'
2
2
 
3
3
  RSpec.describe Flipper do
4
- describe ".new" do
5
- it "returns new instance of dsl" do
6
- instance = Flipper.new(double('Adapter'))
4
+ describe '.new' do
5
+ it 'returns new instance of dsl' do
6
+ instance = described_class.new(double('Adapter'))
7
7
  expect(instance).to be_instance_of(Flipper::DSL)
8
8
  end
9
9
  end
10
10
 
11
- describe ".group_exists" do
12
- it "returns true if the group is already created" do
13
- group = Flipper.register('admins') { |actor| actor.admin? }
14
- expect(Flipper.group_exists?(:admins)).to eq(true)
11
+ describe '.group_exists' do
12
+ it 'returns true if the group is already created' do
13
+ group = described_class.register('admins', &:admin?)
14
+ expect(described_class.group_exists?(:admins)).to eq(true)
15
15
  end
16
16
 
17
- it "returns false when the group is not yet registered" do
18
- expect(Flipper.group_exists?(:non_existing)).to eq(false)
17
+ it 'returns false when the group is not yet registered' do
18
+ expect(described_class.group_exists?(:non_existing)).to eq(false)
19
19
  end
20
20
  end
21
21
 
22
- describe ".groups_registry" do
23
- it "returns a registry instance" do
24
- expect(Flipper.groups_registry).to be_instance_of(Flipper::Registry)
22
+ describe '.groups_registry' do
23
+ it 'returns a registry instance' do
24
+ expect(described_class.groups_registry).to be_instance_of(Flipper::Registry)
25
25
  end
26
26
  end
27
27
 
28
- describe ".groups_registry=" do
29
- it "sets groups_registry registry" do
28
+ describe '.groups_registry=' do
29
+ it 'sets groups_registry registry' do
30
30
  registry = Flipper::Registry.new
31
- Flipper.groups_registry = registry
32
- expect(Flipper.instance_variable_get("@groups_registry")).to eq(registry)
31
+ described_class.groups_registry = registry
32
+ expect(described_class.instance_variable_get('@groups_registry')).to eq(registry)
33
33
  end
34
34
  end
35
35
 
36
- describe ".register" do
37
- it "adds a group to the group_registry" do
36
+ describe '.register' do
37
+ it 'adds a group to the group_registry' do
38
38
  registry = Flipper::Registry.new
39
- Flipper.groups_registry = registry
40
- group = Flipper.register(:admins) { |actor| actor.admin? }
39
+ described_class.groups_registry = registry
40
+ group = described_class.register(:admins, &:admin?)
41
41
  expect(registry.get(:admins)).to eq(group)
42
42
  end
43
43
 
44
- it "adds a group to the group_registry for string name" do
44
+ it 'adds a group to the group_registry for string name' do
45
45
  registry = Flipper::Registry.new
46
- Flipper.groups_registry = registry
47
- group = Flipper.register('admins') { |actor| actor.admin? }
46
+ described_class.groups_registry = registry
47
+ group = described_class.register('admins', &:admin?)
48
48
  expect(registry.get(:admins)).to eq(group)
49
49
  end
50
50
 
51
- it "raises exception if group already registered" do
52
- Flipper.register(:admins) { }
51
+ it 'raises exception if group already registered' do
52
+ described_class.register(:admins) {}
53
53
 
54
- expect {
55
- Flipper.register(:admins) { }
56
- }.to raise_error(Flipper::DuplicateGroup, "Group :admins has already been registered")
54
+ expect do
55
+ described_class.register(:admins) {}
56
+ end.to raise_error(Flipper::DuplicateGroup, 'Group :admins has already been registered')
57
57
  end
58
58
  end
59
59
 
60
- describe ".unregister_groups" do
61
- it "clear group registry" do
62
- expect(Flipper.groups_registry).to receive(:clear)
63
- Flipper.unregister_groups
60
+ describe '.unregister_groups' do
61
+ it 'clear group registry' do
62
+ expect(described_class.groups_registry).to receive(:clear)
63
+ described_class.unregister_groups
64
64
  end
65
65
  end
66
66
 
67
- describe ".group" do
68
- context "for registered group" do
67
+ describe '.group' do
68
+ context 'for registered group' do
69
69
  before do
70
- @group = Flipper.register(:admins) { }
70
+ @group = described_class.register(:admins) {}
71
71
  end
72
72
 
73
- it "returns group" do
74
- expect(Flipper.group(:admins)).to eq(@group)
73
+ it 'returns group' do
74
+ expect(described_class.group(:admins)).to eq(@group)
75
75
  end
76
76
 
77
- it "returns group with string key" do
78
- expect(Flipper.group('admins')).to eq(@group)
77
+ it 'returns group with string key' do
78
+ expect(described_class.group('admins')).to eq(@group)
79
79
  end
80
80
  end
81
81
 
82
- context "for unregistered group" do
83
- it "raises group not registered error" do
84
- expect {
85
- Flipper.group(:cats)
86
- }.to raise_error(Flipper::GroupNotRegistered, 'Group :cats has not been registered')
82
+ context 'for unregistered group' do
83
+ before do
84
+ @group = described_class.group(:cats)
85
+ end
86
+
87
+ it 'returns group' do
88
+ expect(@group).to be_instance_of(Flipper::Types::Group)
89
+ expect(@group.name).to eq(:cats)
90
+ end
91
+
92
+ it 'does not add group to registry' do
93
+ expect(described_class.group_exists?(@group.name)).to be(false)
87
94
  end
88
95
  end
89
96
  end
90
97
 
91
- describe ".groups" do
92
- it "returns array of group instances" do
93
- admins = Flipper.register(:admins) { |actor| actor.admin? }
94
- preview_features = Flipper.register(:preview_features) { |actor| actor.preview_features? }
95
- expect(Flipper.groups).to eq(Set[
98
+ describe '.groups' do
99
+ it 'returns array of group instances' do
100
+ admins = described_class.register(:admins, &:admin?)
101
+ preview_features = described_class.register(:preview_features, &:preview_features?)
102
+ expect(described_class.groups).to eq(Set[
96
103
  admins,
97
104
  preview_features,
98
105
  ])
99
106
  end
100
107
  end
101
108
 
102
- describe ".group_names" do
103
- it "returns array of group names" do
104
- Flipper.register(:admins) { |actor| actor.admin? }
105
- Flipper.register(:preview_features) { |actor| actor.preview_features? }
106
- expect(Flipper.group_names).to eq(Set[
109
+ describe '.group_names' do
110
+ it 'returns array of group names' do
111
+ described_class.register(:admins, &:admin?)
112
+ described_class.register(:preview_features, &:preview_features?)
113
+ expect(described_class.group_names).to eq(Set[
107
114
  :admins,
108
115
  :preview_features,
109
116
  ])
@@ -1,4 +1,4 @@
1
- $:.unshift(File.expand_path('../../lib', __FILE__))
1
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
2
2
 
3
3
  require 'pp'
4
4
  require 'pathname'
@@ -9,11 +9,14 @@ require 'bundler'
9
9
 
10
10
  Bundler.setup(:default)
11
11
 
12
+ require 'webmock/rspec'
13
+ WebMock.disable_net_connect!(allow_localhost: true)
14
+
12
15
  require 'flipper'
13
16
  require 'flipper-ui'
14
17
  require 'flipper-api'
15
18
 
16
- Dir[FlipperRoot.join("spec/support/**/*.rb")].each { |f| require f }
19
+ Dir[FlipperRoot.join('spec/support/**/*.rb')].each { |f| require f }
17
20
 
18
21
  RSpec.configure do |config|
19
22
  config.before(:example) do
@@ -27,64 +30,66 @@ RSpec.configure do |config|
27
30
  end
28
31
 
29
32
  RSpec.shared_examples_for 'a percentage' do
30
- it "initializes with value" do
33
+ it 'initializes with value' do
31
34
  percentage = described_class.new(12)
32
35
  expect(percentage).to be_instance_of(described_class)
33
36
  end
34
37
 
35
- it "converts string values to integers when initializing" do
38
+ it 'converts string values to integers when initializing' do
36
39
  percentage = described_class.new('15')
37
40
  expect(percentage.value).to eq(15)
38
41
  end
39
42
 
40
- it "has a value" do
43
+ it 'has a value' do
41
44
  percentage = described_class.new(19)
42
45
  expect(percentage.value).to eq(19)
43
46
  end
44
47
 
45
- it "raises exception for value higher than 100" do
46
- expect {
48
+ it 'raises exception for value higher than 100' do
49
+ expect do
47
50
  described_class.new(101)
48
- }.to raise_error(ArgumentError, "value must be a positive number less than or equal to 100, but was 101")
51
+ end.to raise_error(ArgumentError,
52
+ 'value must be a positive number less than or equal to 100, but was 101')
49
53
  end
50
54
 
51
- it "raises exception for negative value" do
52
- expect {
55
+ it 'raises exception for negative value' do
56
+ expect do
53
57
  described_class.new(-1)
54
- }.to raise_error(ArgumentError, "value must be a positive number less than or equal to 100, but was -1")
58
+ end.to raise_error(ArgumentError,
59
+ 'value must be a positive number less than or equal to 100, but was -1')
55
60
  end
56
61
  end
57
62
 
58
63
  RSpec.shared_examples_for 'a DSL feature' do
59
- it "returns instance of feature" do
64
+ it 'returns instance of feature' do
60
65
  expect(feature).to be_instance_of(Flipper::Feature)
61
66
  end
62
67
 
63
- it "sets name" do
68
+ it 'sets name' do
64
69
  expect(feature.name).to eq(:stats)
65
70
  end
66
71
 
67
- it "sets adapter" do
72
+ it 'sets adapter' do
68
73
  expect(feature.adapter.name).to eq(dsl.adapter.name)
69
74
  end
70
75
 
71
- it "sets instrumenter" do
76
+ it 'sets instrumenter' do
72
77
  expect(feature.instrumenter).to eq(dsl.instrumenter)
73
78
  end
74
79
 
75
- it "memoizes the feature" do
80
+ it 'memoizes the feature' do
76
81
  expect(dsl.send(method_name, :stats)).to equal(feature)
77
82
  end
78
83
 
79
- it "raises argument error if not string or symbol" do
80
- expect {
84
+ it 'raises argument error if not string or symbol' do
85
+ expect do
81
86
  dsl.send(method_name, Object.new)
82
- }.to raise_error(ArgumentError, /must be a String or Symbol/)
87
+ end.to raise_error(ArgumentError, /must be a String or Symbol/)
83
88
  end
84
89
  end
85
90
 
86
- RSpec.shared_examples_for "a DSL boolean method" do
87
- it "returns boolean with value set" do
91
+ RSpec.shared_examples_for 'a DSL boolean method' do
92
+ it 'returns boolean with value set' do
88
93
  result = subject.send(method_name, true)
89
94
  expect(result).to be_instance_of(Flipper::Types::Boolean)
90
95
  expect(result.value).to be(true)