flipper 0.26.0 → 1.1.0

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 (199) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/ci.yml +19 -13
  4. data/.github/workflows/examples.yml +32 -15
  5. data/Changelog.md +294 -154
  6. data/Gemfile +15 -10
  7. data/README.md +13 -11
  8. data/benchmark/enabled_ips.rb +10 -0
  9. data/benchmark/enabled_multiple_actors_ips.rb +20 -0
  10. data/benchmark/enabled_profile.rb +20 -0
  11. data/benchmark/instrumentation_ips.rb +21 -0
  12. data/benchmark/typecast_ips.rb +27 -0
  13. data/docs/images/flipper_cloud.png +0 -0
  14. data/examples/api/basic.ru +3 -4
  15. data/examples/api/custom_memoized.ru +3 -4
  16. data/examples/api/memoized.ru +3 -4
  17. data/examples/cloud/app.ru +12 -0
  18. data/examples/cloud/backoff_policy.rb +13 -0
  19. data/examples/cloud/basic.rb +22 -0
  20. data/examples/cloud/cloud_setup.rb +20 -0
  21. data/examples/cloud/forked.rb +36 -0
  22. data/examples/cloud/import.rb +17 -0
  23. data/examples/cloud/threaded.rb +33 -0
  24. data/examples/dsl.rb +1 -15
  25. data/examples/enabled_for_actor.rb +4 -2
  26. data/examples/expressions.rb +213 -0
  27. data/examples/mirroring.rb +59 -0
  28. data/examples/strict.rb +18 -0
  29. data/flipper-cloud.gemspec +19 -0
  30. data/flipper.gemspec +3 -5
  31. data/lib/flipper/actor.rb +6 -3
  32. data/lib/flipper/adapter.rb +33 -7
  33. data/lib/flipper/adapter_builder.rb +44 -0
  34. data/lib/flipper/adapters/dual_write.rb +1 -3
  35. data/lib/flipper/adapters/failover.rb +0 -4
  36. data/lib/flipper/adapters/failsafe.rb +0 -4
  37. data/lib/flipper/adapters/http/client.rb +26 -7
  38. data/lib/flipper/adapters/http/error.rb +1 -1
  39. data/lib/flipper/adapters/http.rb +29 -16
  40. data/lib/flipper/adapters/instrumented.rb +25 -6
  41. data/lib/flipper/adapters/memoizable.rb +33 -21
  42. data/lib/flipper/adapters/memory.rb +81 -46
  43. data/lib/flipper/adapters/operation_logger.rb +16 -7
  44. data/lib/flipper/adapters/poll/poller.rb +2 -125
  45. data/lib/flipper/adapters/poll.rb +5 -3
  46. data/lib/flipper/adapters/pstore.rb +17 -11
  47. data/lib/flipper/adapters/read_only.rb +4 -4
  48. data/lib/flipper/adapters/strict.rb +47 -0
  49. data/lib/flipper/adapters/sync/feature_synchronizer.rb +10 -1
  50. data/lib/flipper/adapters/sync.rb +0 -4
  51. data/lib/flipper/cloud/configuration.rb +258 -0
  52. data/lib/flipper/cloud/dsl.rb +27 -0
  53. data/lib/flipper/cloud/message_verifier.rb +95 -0
  54. data/lib/flipper/cloud/middleware.rb +63 -0
  55. data/lib/flipper/cloud/routes.rb +14 -0
  56. data/lib/flipper/cloud/telemetry/backoff_policy.rb +93 -0
  57. data/lib/flipper/cloud/telemetry/instrumenter.rb +26 -0
  58. data/lib/flipper/cloud/telemetry/metric.rb +39 -0
  59. data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
  60. data/lib/flipper/cloud/telemetry/submitter.rb +98 -0
  61. data/lib/flipper/cloud/telemetry.rb +183 -0
  62. data/lib/flipper/cloud.rb +53 -0
  63. data/lib/flipper/configuration.rb +25 -4
  64. data/lib/flipper/dsl.rb +46 -45
  65. data/lib/flipper/engine.rb +88 -0
  66. data/lib/flipper/errors.rb +3 -3
  67. data/lib/flipper/export.rb +26 -0
  68. data/lib/flipper/exporter.rb +17 -0
  69. data/lib/flipper/exporters/json/export.rb +32 -0
  70. data/lib/flipper/exporters/json/v1.rb +33 -0
  71. data/lib/flipper/expression/builder.rb +73 -0
  72. data/lib/flipper/expression/constant.rb +25 -0
  73. data/lib/flipper/expression.rb +71 -0
  74. data/lib/flipper/expressions/all.rb +11 -0
  75. data/lib/flipper/expressions/any.rb +9 -0
  76. data/lib/flipper/expressions/boolean.rb +9 -0
  77. data/lib/flipper/expressions/comparable.rb +13 -0
  78. data/lib/flipper/expressions/duration.rb +28 -0
  79. data/lib/flipper/expressions/equal.rb +9 -0
  80. data/lib/flipper/expressions/greater_than.rb +9 -0
  81. data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
  82. data/lib/flipper/expressions/less_than.rb +9 -0
  83. data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
  84. data/lib/flipper/expressions/not_equal.rb +9 -0
  85. data/lib/flipper/expressions/now.rb +9 -0
  86. data/lib/flipper/expressions/number.rb +9 -0
  87. data/lib/flipper/expressions/percentage.rb +9 -0
  88. data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
  89. data/lib/flipper/expressions/property.rb +9 -0
  90. data/lib/flipper/expressions/random.rb +9 -0
  91. data/lib/flipper/expressions/string.rb +9 -0
  92. data/lib/flipper/expressions/time.rb +9 -0
  93. data/lib/flipper/feature.rb +87 -26
  94. data/lib/flipper/feature_check_context.rb +10 -6
  95. data/lib/flipper/gate.rb +13 -11
  96. data/lib/flipper/gate_values.rb +5 -18
  97. data/lib/flipper/gates/actor.rb +10 -17
  98. data/lib/flipper/gates/boolean.rb +1 -1
  99. data/lib/flipper/gates/expression.rb +75 -0
  100. data/lib/flipper/gates/group.rb +5 -7
  101. data/lib/flipper/gates/percentage_of_actors.rb +10 -13
  102. data/lib/flipper/gates/percentage_of_time.rb +1 -2
  103. data/lib/flipper/identifier.rb +2 -2
  104. data/lib/flipper/instrumentation/log_subscriber.rb +24 -5
  105. data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
  106. data/lib/flipper/instrumentation/subscriber.rb +8 -1
  107. data/lib/flipper/metadata.rb +5 -1
  108. data/lib/flipper/middleware/memoizer.rb +30 -14
  109. data/lib/flipper/poller.rb +117 -0
  110. data/lib/flipper/serializers/gzip.rb +24 -0
  111. data/lib/flipper/serializers/json.rb +19 -0
  112. data/lib/flipper/spec/shared_adapter_specs.rb +95 -54
  113. data/lib/flipper/test/shared_adapter_test.rb +91 -48
  114. data/lib/flipper/typecast.rb +56 -15
  115. data/lib/flipper/types/actor.rb +13 -13
  116. data/lib/flipper/types/group.rb +4 -4
  117. data/lib/flipper/types/percentage.rb +1 -1
  118. data/lib/flipper/version.rb +1 -1
  119. data/lib/flipper.rb +47 -10
  120. data/spec/fixtures/flipper_pstore_1679087600.json +46 -0
  121. data/spec/flipper/adapter_builder_spec.rb +73 -0
  122. data/spec/flipper/adapter_spec.rb +30 -2
  123. data/spec/flipper/adapters/dual_write_spec.rb +2 -2
  124. data/spec/flipper/adapters/http_spec.rb +64 -8
  125. data/spec/flipper/adapters/instrumented_spec.rb +29 -11
  126. data/spec/flipper/adapters/memoizable_spec.rb +51 -31
  127. data/spec/flipper/adapters/memory_spec.rb +14 -3
  128. data/spec/flipper/adapters/operation_logger_spec.rb +31 -12
  129. data/spec/flipper/adapters/read_only_spec.rb +32 -17
  130. data/spec/flipper/adapters/strict_spec.rb +62 -0
  131. data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
  132. data/spec/flipper/cloud/configuration_spec.rb +252 -0
  133. data/spec/flipper/cloud/dsl_spec.rb +82 -0
  134. data/spec/flipper/cloud/message_verifier_spec.rb +104 -0
  135. data/spec/flipper/cloud/middleware_spec.rb +289 -0
  136. data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +108 -0
  137. data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
  138. data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
  139. data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
  140. data/spec/flipper/cloud/telemetry_spec.rb +156 -0
  141. data/spec/flipper/cloud_spec.rb +180 -0
  142. data/spec/flipper/configuration_spec.rb +17 -0
  143. data/spec/flipper/dsl_spec.rb +54 -73
  144. data/spec/flipper/engine_spec.rb +291 -0
  145. data/spec/flipper/export_spec.rb +13 -0
  146. data/spec/flipper/exporter_spec.rb +16 -0
  147. data/spec/flipper/exporters/json/export_spec.rb +60 -0
  148. data/spec/flipper/exporters/json/v1_spec.rb +33 -0
  149. data/spec/flipper/expression/builder_spec.rb +248 -0
  150. data/spec/flipper/expression_spec.rb +188 -0
  151. data/spec/flipper/expressions/all_spec.rb +15 -0
  152. data/spec/flipper/expressions/any_spec.rb +15 -0
  153. data/spec/flipper/expressions/boolean_spec.rb +15 -0
  154. data/spec/flipper/expressions/duration_spec.rb +43 -0
  155. data/spec/flipper/expressions/equal_spec.rb +24 -0
  156. data/spec/flipper/expressions/greater_than_or_equal_to_spec.rb +28 -0
  157. data/spec/flipper/expressions/greater_than_spec.rb +28 -0
  158. data/spec/flipper/expressions/less_than_or_equal_to_spec.rb +28 -0
  159. data/spec/flipper/expressions/less_than_spec.rb +32 -0
  160. data/spec/flipper/expressions/not_equal_spec.rb +15 -0
  161. data/spec/flipper/expressions/now_spec.rb +11 -0
  162. data/spec/flipper/expressions/number_spec.rb +21 -0
  163. data/spec/flipper/expressions/percentage_of_actors_spec.rb +20 -0
  164. data/spec/flipper/expressions/percentage_spec.rb +15 -0
  165. data/spec/flipper/expressions/property_spec.rb +13 -0
  166. data/spec/flipper/expressions/random_spec.rb +9 -0
  167. data/spec/flipper/expressions/string_spec.rb +11 -0
  168. data/spec/flipper/expressions/time_spec.rb +13 -0
  169. data/spec/flipper/feature_check_context_spec.rb +17 -17
  170. data/spec/flipper/feature_spec.rb +436 -33
  171. data/spec/flipper/gate_values_spec.rb +2 -33
  172. data/spec/flipper/gates/boolean_spec.rb +1 -1
  173. data/spec/flipper/gates/expression_spec.rb +108 -0
  174. data/spec/flipper/gates/group_spec.rb +2 -3
  175. data/spec/flipper/gates/percentage_of_actors_spec.rb +61 -5
  176. data/spec/flipper/gates/percentage_of_time_spec.rb +2 -2
  177. data/spec/flipper/identifier_spec.rb +4 -5
  178. data/spec/flipper/instrumentation/log_subscriber_spec.rb +15 -5
  179. data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +25 -1
  180. data/spec/flipper/middleware/memoizer_spec.rb +67 -0
  181. data/spec/flipper/poller_spec.rb +47 -0
  182. data/spec/flipper/serializers/gzip_spec.rb +13 -0
  183. data/spec/flipper/serializers/json_spec.rb +13 -0
  184. data/spec/flipper/typecast_spec.rb +121 -6
  185. data/spec/flipper/types/actor_spec.rb +63 -46
  186. data/spec/flipper/types/group_spec.rb +2 -2
  187. data/spec/flipper_integration_spec.rb +168 -58
  188. data/spec/flipper_spec.rb +92 -28
  189. data/spec/spec_helper.rb +6 -13
  190. data/spec/support/actor_names.yml +1 -0
  191. data/spec/support/climate_control.rb +7 -0
  192. data/spec/support/fake_backoff_policy.rb +15 -0
  193. data/spec/support/skippable.rb +18 -0
  194. data/spec/support/spec_helpers.rb +11 -3
  195. metadata +166 -13
  196. data/.github/workflows/release.yml +0 -44
  197. data/.tool-versions +0 -1
  198. data/lib/flipper/railtie.rb +0 -47
  199. data/spec/flipper/railtie_spec.rb +0 -109
@@ -2,34 +2,41 @@ require 'flipper/types/actor'
2
2
 
3
3
  RSpec.describe Flipper::Types::Actor do
4
4
  subject do
5
- thing = thing_class.new('2')
6
- described_class.new(thing)
5
+ actor = actor_class.new('2')
6
+ described_class.new(actor)
7
7
  end
8
8
 
9
- let(:thing_class) do
9
+ let(:actor_class) do
10
10
  Class.new do
11
11
  attr_reader :flipper_id
12
12
 
13
13
  def initialize(flipper_id)
14
- @flipper_id = flipper_id
14
+ @flipper_id = flipper_id.to_s
15
15
  end
16
16
 
17
17
  def admin?
18
18
  true
19
19
  end
20
+
21
+ def flipper_properties
22
+ {
23
+ "flipper_id" => flipper_id,
24
+ "admin" => admin?,
25
+ }
26
+ end
20
27
  end
21
28
  end
22
29
 
23
30
  describe '.wrappable?' do
24
31
  it 'returns true if actor' do
25
- thing = thing_class.new('1')
26
- actor = described_class.new(thing)
27
- expect(described_class.wrappable?(actor)).to eq(true)
32
+ actor = actor_class.new('1')
33
+ actor_type_instance = described_class.new(actor)
34
+ expect(described_class.wrappable?(actor_type_instance)).to eq(true)
28
35
  end
29
36
 
30
37
  it 'returns true if responds to flipper_id' do
31
- thing = thing_class.new(10)
32
- expect(described_class.wrappable?(thing)).to eq(true)
38
+ actor = actor_class.new(10)
39
+ expect(described_class.wrappable?(actor)).to eq(true)
33
40
  end
34
41
 
35
42
  it 'returns false if nil' do
@@ -38,27 +45,27 @@ RSpec.describe Flipper::Types::Actor do
38
45
  end
39
46
 
40
47
  describe '.wrap' do
41
- context 'for actor' do
42
- it 'returns actor' do
43
- actor = described_class.wrap(subject)
44
- expect(actor).to be_instance_of(described_class)
45
- expect(actor).to be(subject)
48
+ context 'for actor type instance' do
49
+ it 'returns actor type instance' do
50
+ actor_type_instance = described_class.wrap(subject)
51
+ expect(actor_type_instance).to be_instance_of(described_class)
52
+ expect(actor_type_instance).to be(subject)
46
53
  end
47
54
  end
48
55
 
49
- context 'for other thing' do
50
- it 'returns actor' do
51
- thing = thing_class.new('1')
52
- actor = described_class.wrap(thing)
53
- expect(actor).to be_instance_of(described_class)
56
+ context 'for other object' do
57
+ it 'returns actor type instance' do
58
+ actor = actor_class.new('1')
59
+ actor_type_instance = described_class.wrap(actor)
60
+ expect(actor_type_instance).to be_instance_of(described_class)
54
61
  end
55
62
  end
56
63
  end
57
64
 
58
- it 'initializes with thing that responds to id' do
59
- thing = thing_class.new('1')
60
- actor = described_class.new(thing)
61
- expect(actor.value).to eq('1')
65
+ it 'initializes with object that responds to flipper_id' do
66
+ actor = actor_class.new('1')
67
+ actor_type_instance = described_class.new(actor)
68
+ expect(actor_type_instance.value).to eq('1')
62
69
  end
63
70
 
64
71
  it 'raises error when initialized with nil' do
@@ -68,48 +75,58 @@ RSpec.describe Flipper::Types::Actor do
68
75
  end
69
76
 
70
77
  it 'raises error when initalized with non-wrappable object' do
71
- unwrappable_thing = Struct.new(:id).new(1)
78
+ unwrappable_object = Struct.new(:id).new(1)
72
79
  expect do
73
- described_class.new(unwrappable_thing)
80
+ described_class.new(unwrappable_object)
74
81
  end.to raise_error(ArgumentError,
75
- "#{unwrappable_thing.inspect} must respond to flipper_id, but does not")
82
+ "#{unwrappable_object.inspect} must respond to flipper_id, but does not")
76
83
  end
77
84
 
78
85
  it 'converts id to string' do
79
- thing = thing_class.new(2)
80
- actor = described_class.new(thing)
86
+ actor = actor_class.new(2)
87
+ actor = described_class.new(actor)
81
88
  expect(actor.value).to eq('2')
82
89
  end
83
90
 
84
- it 'proxies everything to thing' do
85
- thing = thing_class.new(10)
86
- actor = described_class.new(thing)
91
+ it 'proxies everything to actor' do
92
+ actor = actor_class.new(10)
93
+ actor = described_class.new(actor)
87
94
  expect(actor.admin?).to eq(true)
88
95
  end
89
96
 
90
- it 'exposes thing' do
91
- thing = thing_class.new(10)
92
- actor = described_class.new(thing)
93
- expect(actor.thing).to be(thing)
97
+ it 'proxies flipper_properties to actor' do
98
+ actor = actor_class.new(10)
99
+ actor = described_class.new(actor)
100
+ expect(actor.flipper_properties).to eq({
101
+ "flipper_id" => "10",
102
+ "admin" => true,
103
+ })
104
+ end
105
+
106
+ it 'exposes actor' do
107
+ actor = actor_class.new(10)
108
+ actor_type_instance = described_class.new(actor)
109
+ expect(actor_type_instance.actor).to be(actor)
94
110
  end
95
111
 
96
112
  describe '#respond_to?' do
97
113
  it 'returns true if responds to method' do
98
- thing = thing_class.new('1')
99
- actor = described_class.new(thing)
100
- expect(actor.respond_to?(:value)).to eq(true)
114
+ actor = actor_class.new('1')
115
+ actor_type_instance = described_class.new(actor)
116
+ expect(actor_type_instance.respond_to?(:value)).to eq(true)
101
117
  end
102
118
 
103
- it 'returns true if thing responds to method' do
104
- thing = thing_class.new(10)
105
- actor = described_class.new(thing)
106
- expect(actor.respond_to?(:admin?)).to eq(true)
119
+ it 'returns true if actor responds to method' do
120
+ actor = actor_class.new(10)
121
+ actor_type_instance = described_class.new(actor)
122
+ expect(actor_type_instance.respond_to?(:admin?)).to eq(true)
123
+ expect(actor_type_instance.respond_to?(:flipper_properties)).to eq(true)
107
124
  end
108
125
 
109
- it 'returns false if does not respond to method and thing does not respond to method' do
110
- thing = thing_class.new(10)
111
- actor = described_class.new(thing)
112
- expect(actor.respond_to?(:frankenstein)).to eq(false)
126
+ it 'returns false if does not respond to method and actor does not respond to method' do
127
+ actor = actor_class.new(10)
128
+ actor_type_instance = described_class.new(actor)
129
+ expect(actor_type_instance.respond_to?(:frankenstein)).to eq(false)
113
130
  end
114
131
  end
115
132
  end
@@ -90,7 +90,7 @@ RSpec.describe Flipper::Types::Group do
90
90
  context = Flipper::FeatureCheckContext.new(
91
91
  feature_name: :my_feature,
92
92
  values: Flipper::GateValues.new({}),
93
- thing: Flipper::Types::Actor.new(Flipper::Actor.new(1))
93
+ actors: [Flipper::Types::Actor.new(Flipper::Actor.new('1'))]
94
94
  )
95
95
  group = Flipper.register(:group_with_context) { |actor| actor }
96
96
  yielded_actor = group.match?(admin_actor, context)
@@ -101,7 +101,7 @@ RSpec.describe Flipper::Types::Group do
101
101
  context = Flipper::FeatureCheckContext.new(
102
102
  feature_name: :my_feature,
103
103
  values: Flipper::GateValues.new({}),
104
- thing: Flipper::Types::Actor.new(Flipper::Actor.new(1))
104
+ actors: [Flipper::Types::Actor.new(Flipper::Actor.new('1'))]
105
105
  )
106
106
  group = Flipper.register(:group_with_context) { |actor, context| [actor, context] }
107
107
  yielded_actor, yielded_context = group.match?(admin_actor, context)
@@ -7,25 +7,32 @@ RSpec.describe Flipper do
7
7
  let(:admin_group) { flipper.group(:admins) }
8
8
  let(:dev_group) { flipper.group(:devs) }
9
9
 
10
- let(:admin_thing) do
11
- double 'Non Flipper Thing', flipper_id: 1, admin?: true, dev?: false
10
+ let(:admin_actor) do
11
+ double 'Non Flipper Thing', flipper_id: 1, admin?: true, dev?: false, flipper_properties: {"admin" => true, "dev" => false}
12
12
  end
13
- let(:dev_thing) do
14
- double 'Non Flipper Thing', flipper_id: 10, admin?: false, dev?: true
13
+ let(:dev_actor) do
14
+ double 'Non Flipper Thing', flipper_id: 10, admin?: false, dev?: true, flipper_properties: {"admin" => false, "dev" => true}
15
15
  end
16
16
 
17
- let(:admin_truthy_thing) do
18
- double 'Non Flipper Thing', flipper_id: 1, admin?: 'true-ish', dev?: false
17
+ let(:admin_truthy_actor) do
18
+ double 'Non Flipper Thing', flipper_id: 1, admin?: 'true-ish', dev?: false, flipper_properties: {"admin" => "true-ish", "dev" => false}
19
19
  end
20
- let(:admin_falsey_thing) do
21
- double 'Non Flipper Thing', flipper_id: 1, admin?: nil, dev?: false
20
+ let(:admin_falsey_actor) do
21
+ double 'Non Flipper Thing', flipper_id: 1, admin?: nil, dev?: false, flipper_properties: {"admin" => nil, "dev" => false}
22
+ end
23
+
24
+ let(:basic_plan_actor) do
25
+ double 'Non Flipper Thing', flipper_id: 1, flipper_properties: {"plan" => "basic"}
26
+ end
27
+ let(:premium_plan_actor) do
28
+ double 'Non Flipper Thing', flipper_id: 10, flipper_properties: {"plan" => "premium"}
22
29
  end
23
30
 
24
31
  let(:pitt) { Flipper::Actor.new(1) }
25
32
  let(:clooney) { Flipper::Actor.new(10) }
26
33
 
27
- let(:five_percent_of_actors) { flipper.actors(5) }
28
- let(:five_percent_of_time) { flipper.time(5) }
34
+ let(:five_percent_of_actors) { Flipper::Types::PercentageOfActors.new(5) }
35
+ let(:five_percent_of_time) { Flipper::Types::PercentageOfTime.new(5) }
29
36
 
30
37
  before do
31
38
  described_class.register(:admins, &:admin?)
@@ -60,20 +67,22 @@ RSpec.describe Flipper do
60
67
  expect(@result).to eq(true)
61
68
  end
62
69
 
63
- it 'enables feature for non flipper thing in group' do
64
- expect(feature.enabled?(admin_thing)).to eq(true)
70
+ it 'enables feature for non flipper actor in group' do
71
+ expect(feature.enabled?(admin_actor)).to eq(true)
65
72
  end
66
73
 
67
- it 'does not enable feature for non flipper thing in other group' do
68
- expect(feature.enabled?(dev_thing)).to eq(false)
74
+ it 'does not enable feature for non flipper actor in other group' do
75
+ expect(feature.enabled?(dev_actor)).to eq(false)
69
76
  end
70
77
 
71
78
  it 'enables feature for flipper actor in group' do
72
- expect(feature.enabled?(flipper.actor(admin_thing))).to eq(true)
79
+ expect(feature.enabled?(Flipper::Types::Actor.new(admin_actor))).to eq(true)
80
+ expect(feature.enabled?(admin_actor)).to eq(true)
73
81
  end
74
82
 
75
83
  it 'does not enable for flipper actor not in group' do
76
- expect(feature.enabled?(flipper.actor(dev_thing))).to eq(false)
84
+ expect(feature.enabled?(Flipper::Types::Actor.new(dev_actor))).to eq(false)
85
+ expect(feature.enabled?(dev_actor)).to eq(false)
77
86
  end
78
87
 
79
88
  it 'does not enable feature for all' do
@@ -118,8 +127,8 @@ RSpec.describe Flipper do
118
127
 
119
128
  it 'enables feature for actor within percentage' do
120
129
  enabled = (1..100).select do |i|
121
- thing = Flipper::Actor.new(i)
122
- feature.enabled?(thing)
130
+ actor = Flipper::Actor.new(i)
131
+ feature.enabled?(actor)
123
132
  end.size
124
133
 
125
134
  expect(enabled).to be_within(2).of(5)
@@ -141,8 +150,8 @@ RSpec.describe Flipper do
141
150
 
142
151
  it 'enables feature for actor within percentage' do
143
152
  enabled = (1..100).select do |i|
144
- thing = Flipper::Actor.new(i)
145
- feature.enabled?(thing)
153
+ actor = Flipper::Actor.new(i)
154
+ feature.enabled?(actor)
146
155
  end.size
147
156
 
148
157
  expect(enabled).to be_within(2).of(5)
@@ -180,10 +189,10 @@ RSpec.describe Flipper do
180
189
 
181
190
  context 'with argument that has no gate' do
182
191
  it 'raises error' do
183
- thing = Object.new
192
+ actor = Object.new
184
193
  expect do
185
- feature.enable(thing)
186
- end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{thing.inspect}")
194
+ feature.enable(actor)
195
+ end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{actor.inspect}")
187
196
  end
188
197
  end
189
198
  end
@@ -215,13 +224,13 @@ RSpec.describe Flipper do
215
224
  end
216
225
 
217
226
  it 'disables actor in group' do
218
- expect(feature.enabled?(admin_thing)).to eq(false)
227
+ expect(feature.enabled?(admin_actor)).to eq(false)
219
228
  end
220
229
 
221
230
  it 'disables actor in percentage of actors' do
222
231
  enabled = (1..100).select do |i|
223
- thing = Flipper::Actor.new(i)
224
- feature.enabled?(thing)
232
+ actor = Flipper::Actor.new(i)
233
+ feature.enabled?(actor)
225
234
  end.size
226
235
 
227
236
  expect(enabled).to be(0)
@@ -247,20 +256,22 @@ RSpec.describe Flipper do
247
256
  expect(@result).to eq(true)
248
257
  end
249
258
 
250
- it 'disables the feature for non flipper thing in the group' do
251
- expect(feature.enabled?(admin_thing)).to eq(false)
259
+ it 'disables the feature for non flipper actor in the group' do
260
+ expect(feature.enabled?(admin_actor)).to eq(false)
252
261
  end
253
262
 
254
- it 'does not disable feature for non flipper thing in other groups' do
255
- expect(feature.enabled?(dev_thing)).to eq(true)
263
+ it 'does not disable feature for non flipper actor in other groups' do
264
+ expect(feature.enabled?(dev_actor)).to eq(true)
256
265
  end
257
266
 
258
267
  it 'disables feature for flipper actor in group' do
259
- expect(feature.enabled?(flipper.actor(admin_thing))).to eq(false)
268
+ expect(feature.enabled?(Flipper::Types::Actor.new(admin_actor))).to eq(false)
269
+ expect(feature.enabled?(admin_actor)).to eq(false)
260
270
  end
261
271
 
262
272
  it 'does not disable feature for flipper actor in other groups' do
263
- expect(feature.enabled?(flipper.actor(dev_thing))).to eq(true)
273
+ expect(feature.enabled?(Flipper::Types::Actor.new(dev_actor))).to eq(true)
274
+ expect(feature.enabled?(dev_actor)).to eq(true)
264
275
  end
265
276
 
266
277
  it 'adds feature to set of features' do
@@ -294,7 +305,7 @@ RSpec.describe Flipper do
294
305
 
295
306
  context 'with a percentage of actors' do
296
307
  before do
297
- @result = feature.disable(flipper.actors(0))
308
+ @result = feature.disable(Flipper::Types::PercentageOfActors.new(0))
298
309
  end
299
310
 
300
311
  it 'returns true' do
@@ -303,8 +314,8 @@ RSpec.describe Flipper do
303
314
 
304
315
  it 'disables feature' do
305
316
  enabled = (1..100).select do |i|
306
- thing = Flipper::Actor.new(i)
307
- feature.enabled?(thing)
317
+ actor = Flipper::Actor.new(i)
318
+ feature.enabled?(actor)
308
319
  end.size
309
320
 
310
321
  expect(enabled).to be(0)
@@ -318,7 +329,7 @@ RSpec.describe Flipper do
318
329
  context 'with a percentage of time' do
319
330
  before do
320
331
  @gate = feature.gate(:percentage_of_time)
321
- @result = feature.disable(flipper.time(0))
332
+ @result = feature.disable(Flipper::Types::PercentageOfTime.new(0))
322
333
  end
323
334
 
324
335
  it 'returns true' do
@@ -342,10 +353,10 @@ RSpec.describe Flipper do
342
353
 
343
354
  context 'with argument that has no gate' do
344
355
  it 'raises error' do
345
- thing = Object.new
356
+ actor = Object.new
346
357
  expect do
347
- feature.disable(thing)
348
- end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{thing.inspect}")
358
+ feature.disable(actor)
359
+ end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{actor.inspect}")
349
360
  end
350
361
  end
351
362
  end
@@ -373,23 +384,29 @@ RSpec.describe Flipper do
373
384
  end
374
385
 
375
386
  it 'returns true' do
376
- expect(feature.enabled?(flipper.actor(admin_thing))).to eq(true)
377
- expect(feature.enabled?(admin_thing)).to eq(true)
387
+ expect(feature.enabled?(Flipper::Types::Actor.new(admin_actor))).to eq(true)
388
+ expect(feature.enabled?(admin_actor)).to eq(true)
378
389
  end
379
390
 
380
391
  it 'returns true for truthy block values' do
381
- expect(feature.enabled?(flipper.actor(admin_truthy_thing))).to eq(true)
392
+ expect(feature.enabled?(Flipper::Types::Actor.new(admin_truthy_actor))).to eq(true)
393
+ expect(feature.enabled?(admin_truthy_actor)).to eq(true)
394
+ end
395
+
396
+ it 'returns true if any actor is in enabled group' do
397
+ expect(feature.enabled?(dev_actor, admin_actor)).to be(true)
382
398
  end
383
399
  end
384
400
 
385
401
  context 'for actor in disabled group' do
386
402
  it 'returns false' do
387
- expect(feature.enabled?(flipper.actor(dev_thing))).to eq(false)
388
- expect(feature.enabled?(dev_thing)).to eq(false)
403
+ expect(feature.enabled?(Flipper::Types::Actor.new(dev_actor))).to eq(false)
404
+ expect(feature.enabled?(dev_actor)).to eq(false)
389
405
  end
390
406
 
391
407
  it 'returns false for falsey block values' do
392
- expect(feature.enabled?(flipper.actor(admin_falsey_thing))).to eq(false)
408
+ expect(feature.enabled?(Flipper::Types::Actor.new(admin_falsey_actor))).to eq(false)
409
+ expect(feature.enabled?(admin_falsey_actor)).to eq(false)
393
410
  end
394
411
  end
395
412
 
@@ -408,6 +425,10 @@ RSpec.describe Flipper do
408
425
  expect(feature.enabled?(clooney)).to eq(false)
409
426
  end
410
427
 
428
+ it 'returns false if all actors are disabled' do
429
+ expect(feature.enabled?(clooney, pitt)).to be(false)
430
+ end
431
+
411
432
  it 'returns true if boolean enabled' do
412
433
  feature.enable
413
434
  expect(feature.enabled?(clooney)).to eq(true)
@@ -428,7 +449,7 @@ RSpec.describe Flipper do
428
449
  expect(feature.enabled?).to eq(true)
429
450
  expect(feature.enabled?(nil)).to eq(true)
430
451
  expect(feature.enabled?(pitt)).to eq(true)
431
- expect(feature.enabled?(admin_thing)).to eq(true)
452
+ expect(feature.enabled?(admin_actor)).to eq(true)
432
453
  end
433
454
  end
434
455
 
@@ -446,7 +467,7 @@ RSpec.describe Flipper do
446
467
  expect(feature.enabled?).to eq(true)
447
468
  expect(feature.enabled?(nil)).to eq(true)
448
469
  expect(feature.enabled?(pitt)).to eq(true)
449
- expect(feature.enabled?(admin_thing)).to eq(true)
470
+ expect(feature.enabled?(admin_actor)).to eq(true)
450
471
  end
451
472
  end
452
473
 
@@ -463,7 +484,7 @@ RSpec.describe Flipper do
463
484
  expect(feature.enabled?).to eq(false)
464
485
  expect(feature.enabled?(nil)).to eq(false)
465
486
  expect(feature.enabled?(pitt)).to eq(false)
466
- expect(feature.enabled?(admin_thing)).to eq(false)
487
+ expect(feature.enabled?(admin_actor)).to eq(false)
467
488
  end
468
489
 
469
490
  it 'returns true if boolean enabled' do
@@ -471,7 +492,7 @@ RSpec.describe Flipper do
471
492
  expect(feature.enabled?).to eq(true)
472
493
  expect(feature.enabled?(nil)).to eq(true)
473
494
  expect(feature.enabled?(pitt)).to eq(true)
474
- expect(feature.enabled?(admin_thing)).to eq(true)
495
+ expect(feature.enabled?(admin_actor)).to eq(true)
475
496
  end
476
497
  end
477
498
 
@@ -488,7 +509,7 @@ RSpec.describe Flipper do
488
509
  expect(feature.enabled?).to eq(false)
489
510
  expect(feature.enabled?(nil)).to eq(false)
490
511
  expect(feature.enabled?(pitt)).to eq(false)
491
- expect(feature.enabled?(admin_thing)).to eq(false)
512
+ expect(feature.enabled?(admin_actor)).to eq(false)
492
513
  end
493
514
 
494
515
  it 'returns true if boolean enabled' do
@@ -496,27 +517,31 @@ RSpec.describe Flipper do
496
517
  expect(feature.enabled?).to eq(true)
497
518
  expect(feature.enabled?(nil)).to eq(true)
498
519
  expect(feature.enabled?(pitt)).to eq(true)
499
- expect(feature.enabled?(admin_thing)).to eq(true)
520
+ expect(feature.enabled?(admin_actor)).to eq(true)
500
521
  end
501
522
  end
502
523
 
503
- context 'for a non flipper thing' do
524
+ context 'for a non flipper actor' do
504
525
  before do
505
526
  feature.enable admin_group
506
527
  end
507
528
 
508
529
  it 'returns true if in enabled group' do
509
- expect(feature.enabled?(admin_thing)).to eq(true)
530
+ expect(feature.enabled?(admin_actor)).to eq(true)
510
531
  end
511
532
 
512
533
  it 'returns false if not in enabled group' do
513
- expect(feature.enabled?(dev_thing)).to eq(false)
534
+ expect(feature.enabled?(dev_actor)).to eq(false)
535
+ end
536
+
537
+ it 'retruns true if any actor is true' do
538
+ expect(feature.enabled?(admin_actor, dev_actor)).to eq(true)
514
539
  end
515
540
 
516
541
  it 'returns true if boolean enabled' do
517
542
  feature.enable
518
- expect(feature.enabled?(admin_thing)).to eq(true)
519
- expect(feature.enabled?(dev_thing)).to eq(true)
543
+ expect(feature.enabled?(admin_actor)).to eq(true)
544
+ expect(feature.enabled?(dev_actor)).to eq(true)
520
545
  end
521
546
  end
522
547
  end
@@ -530,11 +555,96 @@ RSpec.describe Flipper do
530
555
  end
531
556
 
532
557
  it 'enables feature for object in enabled group' do
533
- expect(feature.enabled?(admin_thing)).to eq(true)
558
+ expect(feature.enabled?(admin_actor)).to eq(true)
534
559
  end
535
560
 
536
561
  it 'does not enable feature for object in not enabled group' do
537
- expect(feature.enabled?(dev_thing)).to eq(false)
562
+ expect(feature.enabled?(dev_actor)).to eq(false)
563
+ end
564
+ end
565
+
566
+ context "for expression" do
567
+ it "works" do
568
+ feature.enable Flipper.property(:plan).eq("basic")
569
+
570
+ expect(feature.enabled?).to be(false)
571
+ expect(feature.enabled?(basic_plan_actor)).to be(true)
572
+ expect(feature.enabled?(premium_plan_actor)).to be(false)
573
+ expect(feature.enabled?(admin_actor)).to be(false)
574
+ end
575
+
576
+ it "works for true expression with no actor" do
577
+ feature.enable Flipper.boolean(true)
578
+ expect(feature.enabled?).to be(true)
579
+ end
580
+
581
+ it "works for multiple actors" do
582
+ feature.enable Flipper.property(:plan).eq("basic")
583
+
584
+ expect(feature.enabled?(basic_plan_actor, premium_plan_actor)).to be(true)
585
+ expect(feature.enabled?(premium_plan_actor, basic_plan_actor)).to be(true)
586
+ expect(feature.enabled?(premium_plan_actor, admin_actor)).to be(false)
587
+ end
588
+ end
589
+
590
+ context "for Any" do
591
+ it "works" do
592
+ expression = Flipper.any(
593
+ Flipper.property(:plan).eq("basic"),
594
+ Flipper.property(:plan).eq("plus"),
595
+ )
596
+ feature.enable expression
597
+
598
+ expect(feature.enabled?(basic_plan_actor)).to be(true)
599
+ expect(feature.enabled?(premium_plan_actor)).to be(false)
600
+ end
601
+ end
602
+
603
+ context "for All" do
604
+ it "works" do
605
+ true_actor = Flipper::Actor.new("User;1", {
606
+ "plan" => "basic",
607
+ "age" => 21,
608
+ })
609
+ false_actor = Flipper::Actor.new("User;1", {
610
+ "plan" => "basic",
611
+ "age" => 20,
612
+ })
613
+ expression = Flipper.all(
614
+ Flipper.property(:plan).eq("basic"),
615
+ Flipper.property(:age).eq(21)
616
+ )
617
+ feature.enable expression
618
+
619
+ expect(feature.enabled?(true_actor)).to be(true)
620
+ expect(feature.enabled?(false_actor)).to be(false)
621
+ end
622
+
623
+ it "works when nested" do
624
+ admin_actor = Flipper::Actor.new("User;1", {
625
+ "admin" => true,
626
+ })
627
+ true_actor = Flipper::Actor.new("User;1", {
628
+ "plan" => "basic",
629
+ "age" => 21,
630
+ })
631
+ false_actor = Flipper::Actor.new("User;1", {
632
+ "plan" => "basic",
633
+ "age" => 20,
634
+ })
635
+ expression = Flipper.any(
636
+ Flipper.property(:admin).eq(true),
637
+ Flipper.all(
638
+ Flipper.property(:plan).eq("basic"),
639
+ Flipper.property(:age).eq(21)
640
+ )
641
+ )
642
+
643
+ feature.enable expression
644
+
645
+ expect(feature.enabled?(admin_actor)).to be(true)
646
+ expect(feature.enabled?(true_actor)).to be(true)
647
+ expect(feature.enabled?(false_actor)).to be(false)
538
648
  end
539
649
  end
540
650
  end