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.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/ci.yml +19 -13
- data/.github/workflows/examples.yml +32 -15
- data/Changelog.md +294 -154
- data/Gemfile +15 -10
- data/README.md +13 -11
- data/benchmark/enabled_ips.rb +10 -0
- data/benchmark/enabled_multiple_actors_ips.rb +20 -0
- data/benchmark/enabled_profile.rb +20 -0
- data/benchmark/instrumentation_ips.rb +21 -0
- data/benchmark/typecast_ips.rb +27 -0
- data/docs/images/flipper_cloud.png +0 -0
- data/examples/api/basic.ru +3 -4
- data/examples/api/custom_memoized.ru +3 -4
- data/examples/api/memoized.ru +3 -4
- data/examples/cloud/app.ru +12 -0
- data/examples/cloud/backoff_policy.rb +13 -0
- data/examples/cloud/basic.rb +22 -0
- data/examples/cloud/cloud_setup.rb +20 -0
- data/examples/cloud/forked.rb +36 -0
- data/examples/cloud/import.rb +17 -0
- data/examples/cloud/threaded.rb +33 -0
- data/examples/dsl.rb +1 -15
- data/examples/enabled_for_actor.rb +4 -2
- data/examples/expressions.rb +213 -0
- data/examples/mirroring.rb +59 -0
- data/examples/strict.rb +18 -0
- data/flipper-cloud.gemspec +19 -0
- data/flipper.gemspec +3 -5
- data/lib/flipper/actor.rb +6 -3
- data/lib/flipper/adapter.rb +33 -7
- data/lib/flipper/adapter_builder.rb +44 -0
- data/lib/flipper/adapters/dual_write.rb +1 -3
- data/lib/flipper/adapters/failover.rb +0 -4
- data/lib/flipper/adapters/failsafe.rb +0 -4
- data/lib/flipper/adapters/http/client.rb +26 -7
- data/lib/flipper/adapters/http/error.rb +1 -1
- data/lib/flipper/adapters/http.rb +29 -16
- data/lib/flipper/adapters/instrumented.rb +25 -6
- data/lib/flipper/adapters/memoizable.rb +33 -21
- data/lib/flipper/adapters/memory.rb +81 -46
- data/lib/flipper/adapters/operation_logger.rb +16 -7
- data/lib/flipper/adapters/poll/poller.rb +2 -125
- data/lib/flipper/adapters/poll.rb +5 -3
- data/lib/flipper/adapters/pstore.rb +17 -11
- data/lib/flipper/adapters/read_only.rb +4 -4
- data/lib/flipper/adapters/strict.rb +47 -0
- data/lib/flipper/adapters/sync/feature_synchronizer.rb +10 -1
- data/lib/flipper/adapters/sync.rb +0 -4
- data/lib/flipper/cloud/configuration.rb +258 -0
- data/lib/flipper/cloud/dsl.rb +27 -0
- data/lib/flipper/cloud/message_verifier.rb +95 -0
- data/lib/flipper/cloud/middleware.rb +63 -0
- data/lib/flipper/cloud/routes.rb +14 -0
- data/lib/flipper/cloud/telemetry/backoff_policy.rb +93 -0
- data/lib/flipper/cloud/telemetry/instrumenter.rb +26 -0
- data/lib/flipper/cloud/telemetry/metric.rb +39 -0
- data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
- data/lib/flipper/cloud/telemetry/submitter.rb +98 -0
- data/lib/flipper/cloud/telemetry.rb +183 -0
- data/lib/flipper/cloud.rb +53 -0
- data/lib/flipper/configuration.rb +25 -4
- data/lib/flipper/dsl.rb +46 -45
- data/lib/flipper/engine.rb +88 -0
- data/lib/flipper/errors.rb +3 -3
- data/lib/flipper/export.rb +26 -0
- data/lib/flipper/exporter.rb +17 -0
- data/lib/flipper/exporters/json/export.rb +32 -0
- data/lib/flipper/exporters/json/v1.rb +33 -0
- data/lib/flipper/expression/builder.rb +73 -0
- data/lib/flipper/expression/constant.rb +25 -0
- data/lib/flipper/expression.rb +71 -0
- data/lib/flipper/expressions/all.rb +11 -0
- data/lib/flipper/expressions/any.rb +9 -0
- data/lib/flipper/expressions/boolean.rb +9 -0
- data/lib/flipper/expressions/comparable.rb +13 -0
- data/lib/flipper/expressions/duration.rb +28 -0
- data/lib/flipper/expressions/equal.rb +9 -0
- data/lib/flipper/expressions/greater_than.rb +9 -0
- data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/less_than.rb +9 -0
- data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/not_equal.rb +9 -0
- data/lib/flipper/expressions/now.rb +9 -0
- data/lib/flipper/expressions/number.rb +9 -0
- data/lib/flipper/expressions/percentage.rb +9 -0
- data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
- data/lib/flipper/expressions/property.rb +9 -0
- data/lib/flipper/expressions/random.rb +9 -0
- data/lib/flipper/expressions/string.rb +9 -0
- data/lib/flipper/expressions/time.rb +9 -0
- data/lib/flipper/feature.rb +87 -26
- data/lib/flipper/feature_check_context.rb +10 -6
- data/lib/flipper/gate.rb +13 -11
- data/lib/flipper/gate_values.rb +5 -18
- data/lib/flipper/gates/actor.rb +10 -17
- data/lib/flipper/gates/boolean.rb +1 -1
- data/lib/flipper/gates/expression.rb +75 -0
- data/lib/flipper/gates/group.rb +5 -7
- data/lib/flipper/gates/percentage_of_actors.rb +10 -13
- data/lib/flipper/gates/percentage_of_time.rb +1 -2
- data/lib/flipper/identifier.rb +2 -2
- data/lib/flipper/instrumentation/log_subscriber.rb +24 -5
- data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
- data/lib/flipper/instrumentation/subscriber.rb +8 -1
- data/lib/flipper/metadata.rb +5 -1
- data/lib/flipper/middleware/memoizer.rb +30 -14
- data/lib/flipper/poller.rb +117 -0
- data/lib/flipper/serializers/gzip.rb +24 -0
- data/lib/flipper/serializers/json.rb +19 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +95 -54
- data/lib/flipper/test/shared_adapter_test.rb +91 -48
- data/lib/flipper/typecast.rb +56 -15
- data/lib/flipper/types/actor.rb +13 -13
- data/lib/flipper/types/group.rb +4 -4
- data/lib/flipper/types/percentage.rb +1 -1
- data/lib/flipper/version.rb +1 -1
- data/lib/flipper.rb +47 -10
- data/spec/fixtures/flipper_pstore_1679087600.json +46 -0
- data/spec/flipper/adapter_builder_spec.rb +73 -0
- data/spec/flipper/adapter_spec.rb +30 -2
- data/spec/flipper/adapters/dual_write_spec.rb +2 -2
- data/spec/flipper/adapters/http_spec.rb +64 -8
- data/spec/flipper/adapters/instrumented_spec.rb +29 -11
- data/spec/flipper/adapters/memoizable_spec.rb +51 -31
- data/spec/flipper/adapters/memory_spec.rb +14 -3
- data/spec/flipper/adapters/operation_logger_spec.rb +31 -12
- data/spec/flipper/adapters/read_only_spec.rb +32 -17
- data/spec/flipper/adapters/strict_spec.rb +62 -0
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
- data/spec/flipper/cloud/configuration_spec.rb +252 -0
- data/spec/flipper/cloud/dsl_spec.rb +82 -0
- data/spec/flipper/cloud/message_verifier_spec.rb +104 -0
- data/spec/flipper/cloud/middleware_spec.rb +289 -0
- data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +108 -0
- data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
- data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
- data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
- data/spec/flipper/cloud/telemetry_spec.rb +156 -0
- data/spec/flipper/cloud_spec.rb +180 -0
- data/spec/flipper/configuration_spec.rb +17 -0
- data/spec/flipper/dsl_spec.rb +54 -73
- data/spec/flipper/engine_spec.rb +291 -0
- data/spec/flipper/export_spec.rb +13 -0
- data/spec/flipper/exporter_spec.rb +16 -0
- data/spec/flipper/exporters/json/export_spec.rb +60 -0
- data/spec/flipper/exporters/json/v1_spec.rb +33 -0
- data/spec/flipper/expression/builder_spec.rb +248 -0
- data/spec/flipper/expression_spec.rb +188 -0
- data/spec/flipper/expressions/all_spec.rb +15 -0
- data/spec/flipper/expressions/any_spec.rb +15 -0
- data/spec/flipper/expressions/boolean_spec.rb +15 -0
- data/spec/flipper/expressions/duration_spec.rb +43 -0
- data/spec/flipper/expressions/equal_spec.rb +24 -0
- data/spec/flipper/expressions/greater_than_or_equal_to_spec.rb +28 -0
- data/spec/flipper/expressions/greater_than_spec.rb +28 -0
- data/spec/flipper/expressions/less_than_or_equal_to_spec.rb +28 -0
- data/spec/flipper/expressions/less_than_spec.rb +32 -0
- data/spec/flipper/expressions/not_equal_spec.rb +15 -0
- data/spec/flipper/expressions/now_spec.rb +11 -0
- data/spec/flipper/expressions/number_spec.rb +21 -0
- data/spec/flipper/expressions/percentage_of_actors_spec.rb +20 -0
- data/spec/flipper/expressions/percentage_spec.rb +15 -0
- data/spec/flipper/expressions/property_spec.rb +13 -0
- data/spec/flipper/expressions/random_spec.rb +9 -0
- data/spec/flipper/expressions/string_spec.rb +11 -0
- data/spec/flipper/expressions/time_spec.rb +13 -0
- data/spec/flipper/feature_check_context_spec.rb +17 -17
- data/spec/flipper/feature_spec.rb +436 -33
- data/spec/flipper/gate_values_spec.rb +2 -33
- data/spec/flipper/gates/boolean_spec.rb +1 -1
- data/spec/flipper/gates/expression_spec.rb +108 -0
- data/spec/flipper/gates/group_spec.rb +2 -3
- data/spec/flipper/gates/percentage_of_actors_spec.rb +61 -5
- data/spec/flipper/gates/percentage_of_time_spec.rb +2 -2
- data/spec/flipper/identifier_spec.rb +4 -5
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +15 -5
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +25 -1
- data/spec/flipper/middleware/memoizer_spec.rb +67 -0
- data/spec/flipper/poller_spec.rb +47 -0
- data/spec/flipper/serializers/gzip_spec.rb +13 -0
- data/spec/flipper/serializers/json_spec.rb +13 -0
- data/spec/flipper/typecast_spec.rb +121 -6
- data/spec/flipper/types/actor_spec.rb +63 -46
- data/spec/flipper/types/group_spec.rb +2 -2
- data/spec/flipper_integration_spec.rb +168 -58
- data/spec/flipper_spec.rb +92 -28
- data/spec/spec_helper.rb +6 -13
- data/spec/support/actor_names.yml +1 -0
- data/spec/support/climate_control.rb +7 -0
- data/spec/support/fake_backoff_policy.rb +15 -0
- data/spec/support/skippable.rb +18 -0
- data/spec/support/spec_helpers.rb +11 -3
- metadata +166 -13
- data/.github/workflows/release.yml +0 -44
- data/.tool-versions +0 -1
- data/lib/flipper/railtie.rb +0 -47
- data/spec/flipper/railtie_spec.rb +0 -109
@@ -32,6 +32,52 @@ RSpec.describe Flipper::Feature do
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
+
describe "#enabled?" do
|
36
|
+
context "for an actor" do
|
37
|
+
let(:actor) { Flipper::Actor.new("User;1") }
|
38
|
+
|
39
|
+
it 'returns true if feature is enabled' do
|
40
|
+
subject.enable
|
41
|
+
expect(subject.enabled?(actor)).to be(true)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns false if feature is disabled' do
|
45
|
+
subject.disable
|
46
|
+
expect(subject.enabled?(actor)).to be(false)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "for multiple actors" do
|
51
|
+
let(:actors) {
|
52
|
+
[
|
53
|
+
Flipper::Actor.new("User;1"),
|
54
|
+
Flipper::Actor.new("User;2"),
|
55
|
+
Flipper::Actor.new("User;3"),
|
56
|
+
]
|
57
|
+
}
|
58
|
+
|
59
|
+
it 'returns true if feature is enabled' do
|
60
|
+
subject.enable
|
61
|
+
expect(subject.enabled?(actors)).to be(true)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'returns true if feature is enabled for any actor' do
|
65
|
+
subject.enable_actor actors.first
|
66
|
+
expect(subject.enabled?(actors)).to be(true)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'returns true if feature is enabled for any actor with multiple arguments' do
|
70
|
+
subject.enable_actor actors.last
|
71
|
+
expect(subject.enabled?(*actors)).to be(true)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'returns false if feature is disabled for all actors' do
|
75
|
+
subject.disable
|
76
|
+
expect(subject.enabled?(actors)).to be(false)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
35
81
|
describe '#to_s' do
|
36
82
|
it 'returns name as string' do
|
37
83
|
feature = described_class.new(:search, adapter)
|
@@ -63,7 +109,7 @@ RSpec.describe Flipper::Feature do
|
|
63
109
|
instance.gates.each do |gate|
|
64
110
|
expect(gate).to be_a(Flipper::Gate)
|
65
111
|
end
|
66
|
-
expect(instance.gates.size).to be(
|
112
|
+
expect(instance.gates.size).to be(6)
|
67
113
|
end
|
68
114
|
end
|
69
115
|
|
@@ -148,29 +194,29 @@ RSpec.describe Flipper::Feature do
|
|
148
194
|
end
|
149
195
|
|
150
196
|
it 'is recorded for enable' do
|
151
|
-
|
152
|
-
gate = subject.gate_for(
|
197
|
+
actor = Flipper::Types::Actor.new(Flipper::Actor.new('1'))
|
198
|
+
gate = subject.gate_for(actor)
|
153
199
|
|
154
|
-
subject.enable(
|
200
|
+
subject.enable(actor)
|
155
201
|
|
156
202
|
event = instrumenter.events.last
|
157
203
|
expect(event).not_to be_nil
|
158
204
|
expect(event.name).to eq('feature_operation.flipper')
|
159
205
|
expect(event.payload[:feature_name]).to eq(:search)
|
160
206
|
expect(event.payload[:operation]).to eq(:enable)
|
161
|
-
expect(event.payload[:thing]).to eq(
|
207
|
+
expect(event.payload[:thing]).to eq(actor)
|
162
208
|
expect(event.payload[:result]).not_to be_nil
|
163
209
|
end
|
164
210
|
|
165
211
|
it 'always instruments flipper type instance for enable' do
|
166
|
-
|
167
|
-
gate = subject.gate_for(
|
212
|
+
actor = Flipper::Actor.new('1')
|
213
|
+
gate = subject.gate_for(actor)
|
168
214
|
|
169
|
-
subject.enable(
|
215
|
+
subject.enable(actor)
|
170
216
|
|
171
217
|
event = instrumenter.events.last
|
172
218
|
expect(event).not_to be_nil
|
173
|
-
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(
|
219
|
+
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(actor))
|
174
220
|
end
|
175
221
|
|
176
222
|
it 'is recorded for disable' do
|
@@ -219,15 +265,15 @@ RSpec.describe Flipper::Feature do
|
|
219
265
|
end
|
220
266
|
|
221
267
|
it 'always instruments flipper type instance for disable' do
|
222
|
-
|
223
|
-
gate = subject.gate_for(
|
268
|
+
actor = Flipper::Actor.new('1')
|
269
|
+
gate = subject.gate_for(actor)
|
224
270
|
|
225
|
-
subject.disable(
|
271
|
+
subject.disable(actor)
|
226
272
|
|
227
273
|
event = instrumenter.events.last
|
228
274
|
expect(event).not_to be_nil
|
229
275
|
expect(event.payload[:operation]).to eq(:disable)
|
230
|
-
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(
|
276
|
+
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(actor))
|
231
277
|
end
|
232
278
|
|
233
279
|
it 'is recorded for add' do
|
@@ -275,17 +321,15 @@ RSpec.describe Flipper::Feature do
|
|
275
321
|
end
|
276
322
|
|
277
323
|
it 'is recorded for enabled?' do
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
subject.enabled?(thing)
|
324
|
+
actor = Flipper::Types::Actor.new(Flipper::Actor.new('1'))
|
325
|
+
subject.enabled?(actor)
|
282
326
|
|
283
327
|
event = instrumenter.events.last
|
284
328
|
expect(event).not_to be_nil
|
285
329
|
expect(event.name).to eq('feature_operation.flipper')
|
286
330
|
expect(event.payload[:feature_name]).to eq(:search)
|
287
331
|
expect(event.payload[:operation]).to eq(:enabled?)
|
288
|
-
expect(event.payload[:
|
332
|
+
expect(event.payload[:actors]).to eq([actor])
|
289
333
|
expect(event.payload[:result]).to eq(false)
|
290
334
|
end
|
291
335
|
|
@@ -293,8 +337,8 @@ RSpec.describe Flipper::Feature do
|
|
293
337
|
actor = Flipper::Types::Actor.new(user)
|
294
338
|
{
|
295
339
|
nil => nil,
|
296
|
-
user => actor,
|
297
|
-
actor => actor,
|
340
|
+
user => [actor],
|
341
|
+
actor => [actor],
|
298
342
|
}.each do |thing, wrapped_thing|
|
299
343
|
it "always instruments #{thing.inspect} as #{wrapped_thing.class} for enabled?" do
|
300
344
|
subject.enabled?(thing)
|
@@ -302,7 +346,7 @@ RSpec.describe Flipper::Feature do
|
|
302
346
|
event = instrumenter.events.last
|
303
347
|
expect(event).not_to be_nil
|
304
348
|
expect(event.payload[:operation]).to eq(:enabled?)
|
305
|
-
expect(event.payload[:
|
349
|
+
expect(event.payload[:actors]).to eq(wrapped_thing)
|
306
350
|
end
|
307
351
|
end
|
308
352
|
end
|
@@ -428,10 +472,10 @@ RSpec.describe Flipper::Feature do
|
|
428
472
|
|
429
473
|
context 'when one or more groups enabled' do
|
430
474
|
before do
|
431
|
-
@staff = Flipper.register(:staff) { |
|
432
|
-
@preview_features = Flipper.register(:preview_features) { |
|
433
|
-
@not_enabled = Flipper.register(:not_enabled) { |
|
434
|
-
@disabled = Flipper.register(:disabled) { |
|
475
|
+
@staff = Flipper.register(:staff) { |actor| true }
|
476
|
+
@preview_features = Flipper.register(:preview_features) { |actor| true }
|
477
|
+
@not_enabled = Flipper.register(:not_enabled) { |actor| true }
|
478
|
+
@disabled = Flipper.register(:disabled) { |actor| true }
|
435
479
|
subject.enable @staff
|
436
480
|
subject.enable @preview_features
|
437
481
|
subject.disable @disabled
|
@@ -467,10 +511,10 @@ RSpec.describe Flipper::Feature do
|
|
467
511
|
|
468
512
|
context 'when one or more groups enabled' do
|
469
513
|
before do
|
470
|
-
@staff = Flipper.register(:staff) { |
|
471
|
-
@preview_features = Flipper.register(:preview_features) { |
|
472
|
-
@not_enabled = Flipper.register(:not_enabled) { |
|
473
|
-
@disabled = Flipper.register(:disabled) { |
|
514
|
+
@staff = Flipper.register(:staff) { |actor| true }
|
515
|
+
@preview_features = Flipper.register(:preview_features) { |actor| true }
|
516
|
+
@not_enabled = Flipper.register(:not_enabled) { |actor| true }
|
517
|
+
@disabled = Flipper.register(:disabled) { |actor| true }
|
474
518
|
subject.enable @staff
|
475
519
|
subject.enable @preview_features
|
476
520
|
subject.disable @disabled
|
@@ -494,10 +538,10 @@ RSpec.describe Flipper::Feature do
|
|
494
538
|
|
495
539
|
context 'when one or more groups enabled' do
|
496
540
|
before do
|
497
|
-
@staff = Flipper.register(:staff) { |
|
498
|
-
@preview_features = Flipper.register(:preview_features) { |
|
499
|
-
@not_enabled = Flipper.register(:not_enabled) { |
|
500
|
-
@disabled = Flipper.register(:disabled) { |
|
541
|
+
@staff = Flipper.register(:staff) { |actor| true }
|
542
|
+
@preview_features = Flipper.register(:preview_features) { |actor| true }
|
543
|
+
@not_enabled = Flipper.register(:not_enabled) { |actor| true }
|
544
|
+
@disabled = Flipper.register(:disabled) { |actor| true }
|
501
545
|
subject.enable @staff
|
502
546
|
subject.enable @preview_features
|
503
547
|
subject.disable @disabled
|
@@ -649,6 +693,363 @@ RSpec.describe Flipper::Feature do
|
|
649
693
|
end
|
650
694
|
end
|
651
695
|
|
696
|
+
describe '#expression' do
|
697
|
+
it "returns nil if feature has no expression" do
|
698
|
+
expect(subject.expression).to be(nil)
|
699
|
+
end
|
700
|
+
|
701
|
+
it "returns expression if feature has expression" do
|
702
|
+
expression = Flipper.property(:plan).eq("basic")
|
703
|
+
subject.enable_expression expression
|
704
|
+
expect(subject.expression).to eq(expression)
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
describe '#enable_expression/disable_expression' do
|
709
|
+
context "with expression instance" do
|
710
|
+
it "updates gate values to equal expression or clears expression" do
|
711
|
+
expression = Flipper.property(:plan).eq("basic")
|
712
|
+
other_expression = Flipper.property(:age).gte(21)
|
713
|
+
expect(subject.gate_values.expression).to be(nil)
|
714
|
+
subject.enable_expression(expression)
|
715
|
+
expect(subject.gate_values.expression).to eq(expression.value)
|
716
|
+
subject.disable_expression
|
717
|
+
expect(subject.gate_values.expression).to be(nil)
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
context "with Hash" do
|
722
|
+
it "updates gate values to equal expression or clears expression" do
|
723
|
+
expression = Flipper.property(:plan).eq("basic")
|
724
|
+
other_expression = Flipper.property(:age).gte(21)
|
725
|
+
expect(subject.gate_values.expression).to be(nil)
|
726
|
+
subject.enable_expression(expression.value)
|
727
|
+
expect(subject.gate_values.expression).to eq(expression.value)
|
728
|
+
subject.disable_expression
|
729
|
+
expect(subject.gate_values.expression).to be(nil)
|
730
|
+
end
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
describe "#add_expression" do
|
735
|
+
context "when nothing enabled" do
|
736
|
+
context "with Expression instance" do
|
737
|
+
it "sets expression to Expression" do
|
738
|
+
expression = Flipper.property(:plan).eq("basic")
|
739
|
+
subject.add_expression(expression)
|
740
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
741
|
+
expect(subject.expression).to eq(expression)
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
context "with Any instance" do
|
746
|
+
it "sets expression to Any" do
|
747
|
+
expression = Flipper.any(Flipper.property(:plan).eq("basic"))
|
748
|
+
subject.add_expression(expression)
|
749
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
750
|
+
expect(subject.expression).to eq(expression)
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
context "with All instance" do
|
755
|
+
it "sets expression to All" do
|
756
|
+
expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
757
|
+
subject.add_expression(expression)
|
758
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
759
|
+
expect(subject.expression).to eq(expression)
|
760
|
+
end
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
764
|
+
context "when Expression enabled" do
|
765
|
+
let(:expression) { Flipper.property(:plan).eq("basic") }
|
766
|
+
|
767
|
+
before do
|
768
|
+
subject.enable_expression expression
|
769
|
+
end
|
770
|
+
|
771
|
+
context "with Expression instance" do
|
772
|
+
it "changes expression to Any and adds new Expression" do
|
773
|
+
new_expression = Flipper.property(:age).gte(21)
|
774
|
+
subject.add_expression(new_expression)
|
775
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
776
|
+
expect(subject.expression.args).to include(expression)
|
777
|
+
expect(subject.expression.args).to include(new_expression)
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
context "with Any instance" do
|
782
|
+
it "changes expression to Any and adds new Any" do
|
783
|
+
new_expression = Flipper.any(Flipper.property(:age).eq(21))
|
784
|
+
subject.add_expression new_expression
|
785
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
786
|
+
expect(subject.expression.args).to include(expression)
|
787
|
+
expect(subject.expression.args).to include(new_expression)
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
context "with All instance" do
|
792
|
+
it "changes expression to Any and adds new All" do
|
793
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
794
|
+
subject.add_expression new_expression
|
795
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
796
|
+
expect(subject.expression.args).to include(expression)
|
797
|
+
expect(subject.expression.args).to include(new_expression)
|
798
|
+
end
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
context "when Any enabled" do
|
803
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
804
|
+
let(:expression) { Flipper.any(condition) }
|
805
|
+
|
806
|
+
before do
|
807
|
+
subject.enable_expression expression
|
808
|
+
end
|
809
|
+
|
810
|
+
context "with Expression instance" do
|
811
|
+
it "adds Expression to Any" do
|
812
|
+
new_expression = Flipper.property(:age).gte(21)
|
813
|
+
subject.add_expression(new_expression)
|
814
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
815
|
+
expect(subject.expression.args).to include(condition)
|
816
|
+
expect(subject.expression.args).to include(new_expression)
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
context "with Any instance" do
|
821
|
+
it "adds Any to Any" do
|
822
|
+
new_expression = Flipper.any(Flipper.property(:age).gte(21))
|
823
|
+
subject.add_expression(new_expression)
|
824
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
825
|
+
expect(subject.expression.args).to include(condition)
|
826
|
+
expect(subject.expression.args).to include(new_expression)
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
context "with All instance" do
|
831
|
+
it "adds All to Any" do
|
832
|
+
new_expression = Flipper.all(Flipper.property(:age).gte(21))
|
833
|
+
subject.add_expression(new_expression)
|
834
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
835
|
+
expect(subject.expression.args).to include(condition)
|
836
|
+
expect(subject.expression.args).to include(new_expression)
|
837
|
+
end
|
838
|
+
end
|
839
|
+
end
|
840
|
+
|
841
|
+
context "when All enabled" do
|
842
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
843
|
+
let(:expression) { Flipper.all(condition) }
|
844
|
+
|
845
|
+
before do
|
846
|
+
subject.enable_expression expression
|
847
|
+
end
|
848
|
+
|
849
|
+
context "with Expression instance" do
|
850
|
+
it "adds Expression to All" do
|
851
|
+
new_expression = Flipper.property(:age).gte(21)
|
852
|
+
subject.add_expression(new_expression)
|
853
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
854
|
+
expect(subject.expression.args).to include(condition)
|
855
|
+
expect(subject.expression.args).to include(new_expression)
|
856
|
+
end
|
857
|
+
end
|
858
|
+
|
859
|
+
context "with Any instance" do
|
860
|
+
it "adds Any to All" do
|
861
|
+
new_expression = Flipper.any(Flipper.property(:age).gte(21))
|
862
|
+
subject.add_expression(new_expression)
|
863
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
864
|
+
expect(subject.expression.args).to include(condition)
|
865
|
+
expect(subject.expression.args).to include(new_expression)
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
context "with All instance" do
|
870
|
+
it "adds All to All" do
|
871
|
+
new_expression = Flipper.all(Flipper.property(:age).gte(21))
|
872
|
+
subject.add_expression(new_expression)
|
873
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
874
|
+
expect(subject.expression.args).to include(condition)
|
875
|
+
expect(subject.expression.args).to include(new_expression)
|
876
|
+
end
|
877
|
+
end
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
881
|
+
describe '#remove_expression' do
|
882
|
+
context "when nothing enabled" do
|
883
|
+
context "with Expression instance" do
|
884
|
+
it "does nothing" do
|
885
|
+
expression = Flipper.property(:plan).eq("basic")
|
886
|
+
subject.remove_expression(expression)
|
887
|
+
expect(subject.expression).to be(nil)
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
context "with Any instance" do
|
892
|
+
it "does nothing" do
|
893
|
+
expression = Flipper.any(Flipper.property(:plan).eq("basic"))
|
894
|
+
subject.remove_expression expression
|
895
|
+
expect(subject.expression).to be(nil)
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
context "with All instance" do
|
900
|
+
it "does nothing" do
|
901
|
+
expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
902
|
+
subject.remove_expression expression
|
903
|
+
expect(subject.expression).to be(nil)
|
904
|
+
end
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
context "when Expression enabled" do
|
909
|
+
let(:expression) { Flipper.property(:plan).eq("basic") }
|
910
|
+
|
911
|
+
before do
|
912
|
+
subject.enable_expression expression
|
913
|
+
end
|
914
|
+
|
915
|
+
context "with Expression instance" do
|
916
|
+
it "changes expression to Any and removes Expression if it matches" do
|
917
|
+
new_expression = Flipper.property(:plan).eq("basic")
|
918
|
+
subject.remove_expression new_expression
|
919
|
+
expect(subject.expression).to eq(Flipper.any)
|
920
|
+
end
|
921
|
+
|
922
|
+
it "changes expression to Any if Expression doesn't match" do
|
923
|
+
new_expression = Flipper.property(:plan).eq("premium")
|
924
|
+
subject.remove_expression new_expression
|
925
|
+
expect(subject.expression).to eq(Flipper.any(expression))
|
926
|
+
end
|
927
|
+
end
|
928
|
+
|
929
|
+
context "with Any instance" do
|
930
|
+
it "changes expression to Any and does nothing" do
|
931
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("basic"))
|
932
|
+
subject.remove_expression new_expression
|
933
|
+
expect(subject.expression).to eq(Flipper.any(expression))
|
934
|
+
end
|
935
|
+
end
|
936
|
+
|
937
|
+
context "with All instance" do
|
938
|
+
it "changes expression to Any and does nothing" do
|
939
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
940
|
+
subject.remove_expression new_expression
|
941
|
+
expect(subject.expression).to eq(Flipper.any(expression))
|
942
|
+
end
|
943
|
+
end
|
944
|
+
end
|
945
|
+
|
946
|
+
context "when Any enabled" do
|
947
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
948
|
+
let(:expression) { Flipper.any condition }
|
949
|
+
|
950
|
+
before do
|
951
|
+
subject.enable_expression expression
|
952
|
+
end
|
953
|
+
|
954
|
+
context "with Expression instance" do
|
955
|
+
it "removes Expression if it matches" do
|
956
|
+
subject.remove_expression condition
|
957
|
+
expect(subject.expression).to eq(Flipper.any)
|
958
|
+
end
|
959
|
+
|
960
|
+
it "does nothing if Expression does not match" do
|
961
|
+
subject.remove_expression Flipper.property(:plan).eq("premium")
|
962
|
+
expect(subject.expression).to eq(expression)
|
963
|
+
end
|
964
|
+
end
|
965
|
+
|
966
|
+
context "with Any instance" do
|
967
|
+
it "removes Any if it matches" do
|
968
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("premium"))
|
969
|
+
subject.add_expression new_expression
|
970
|
+
expect(subject.expression.args.size).to be(2)
|
971
|
+
subject.remove_expression new_expression
|
972
|
+
expect(subject.expression).to eq(expression)
|
973
|
+
end
|
974
|
+
|
975
|
+
it "does nothing if Any does not match" do
|
976
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("premium"))
|
977
|
+
subject.remove_expression new_expression
|
978
|
+
expect(subject.expression).to eq(expression)
|
979
|
+
end
|
980
|
+
end
|
981
|
+
|
982
|
+
context "with All instance" do
|
983
|
+
it "removes All if it matches" do
|
984
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
985
|
+
subject.add_expression new_expression
|
986
|
+
expect(subject.expression.args.size).to be(2)
|
987
|
+
subject.remove_expression new_expression
|
988
|
+
expect(subject.expression).to eq(expression)
|
989
|
+
end
|
990
|
+
|
991
|
+
it "does nothing if All does not match" do
|
992
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
993
|
+
subject.remove_expression new_expression
|
994
|
+
expect(subject.expression).to eq(expression)
|
995
|
+
end
|
996
|
+
end
|
997
|
+
end
|
998
|
+
|
999
|
+
context "when All enabled" do
|
1000
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
1001
|
+
let(:expression) { Flipper.all condition }
|
1002
|
+
|
1003
|
+
before do
|
1004
|
+
subject.enable_expression expression
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
context "with Expression instance" do
|
1008
|
+
it "removes Expression if it matches" do
|
1009
|
+
subject.remove_expression condition
|
1010
|
+
expect(subject.expression).to eq(Flipper.all)
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
it "does nothing if Expression does not match" do
|
1014
|
+
subject.remove_expression Flipper.property(:plan).eq("premium")
|
1015
|
+
expect(subject.expression).to eq(expression)
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
context "with Any instance" do
|
1020
|
+
it "removes Any if it matches" do
|
1021
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("premium"))
|
1022
|
+
subject.add_expression new_expression
|
1023
|
+
expect(subject.expression.args.size).to be(2)
|
1024
|
+
subject.remove_expression new_expression
|
1025
|
+
expect(subject.expression).to eq(expression)
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
it "does nothing if Any does not match" do
|
1029
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("premium"))
|
1030
|
+
subject.remove_expression new_expression
|
1031
|
+
expect(subject.expression).to eq(expression)
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
context "with All instance" do
|
1036
|
+
it "removes All if it matches" do
|
1037
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
1038
|
+
subject.add_expression new_expression
|
1039
|
+
expect(subject.expression.args.size).to be(2)
|
1040
|
+
subject.remove_expression new_expression
|
1041
|
+
expect(subject.expression).to eq(expression)
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
it "does nothing if All does not match" do
|
1045
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
1046
|
+
subject.remove_expression new_expression
|
1047
|
+
expect(subject.expression).to eq(expression)
|
1048
|
+
end
|
1049
|
+
end
|
1050
|
+
end
|
1051
|
+
end
|
1052
|
+
|
652
1053
|
describe '#enable_actor/disable_actor' do
|
653
1054
|
context 'with object that responds to flipper_id' do
|
654
1055
|
it 'updates the gate values to include the actor' do
|
@@ -801,12 +1202,14 @@ RSpec.describe Flipper::Feature do
|
|
801
1202
|
:actor,
|
802
1203
|
:boolean,
|
803
1204
|
:group,
|
1205
|
+
:expression,
|
804
1206
|
])
|
805
1207
|
|
806
1208
|
expect(subject.disabled_gate_names.to_set).to eq(Set[
|
807
1209
|
:actor,
|
808
1210
|
:boolean,
|
809
1211
|
:group,
|
1212
|
+
:expression,
|
810
1213
|
])
|
811
1214
|
end
|
812
1215
|
end
|
@@ -80,13 +80,13 @@ RSpec.describe Flipper::GateValues do
|
|
80
80
|
it 'raises argument error for percentage of time value that cannot be converted to an integer' do
|
81
81
|
expect do
|
82
82
|
described_class.new(percentage_of_time: ['asdf'])
|
83
|
-
end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to
|
83
|
+
end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to a number))
|
84
84
|
end
|
85
85
|
|
86
86
|
it 'raises argument error for percentage of actors value that cannot be converted to an int' do
|
87
87
|
expect do
|
88
88
|
described_class.new(percentage_of_actors: ['asdf'])
|
89
|
-
end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to
|
89
|
+
end.to raise_error(ArgumentError, %(["asdf"] cannot be converted to a number))
|
90
90
|
end
|
91
91
|
|
92
92
|
it 'raises argument error for actors value that cannot be converted to a set' do
|
@@ -100,35 +100,4 @@ RSpec.describe Flipper::GateValues do
|
|
100
100
|
described_class.new(groups: 'asdf')
|
101
101
|
end.to raise_error(ArgumentError, %("asdf" cannot be converted to a set))
|
102
102
|
end
|
103
|
-
|
104
|
-
describe '#[]' do
|
105
|
-
it 'can read the boolean value' do
|
106
|
-
expect(described_class.new(boolean: true)[:boolean]).to be(true)
|
107
|
-
expect(described_class.new(boolean: true)['boolean']).to be(true)
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'can read the actors value' do
|
111
|
-
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
|
-
end
|
114
|
-
|
115
|
-
it 'can read the groups value' do
|
116
|
-
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
|
-
end
|
119
|
-
|
120
|
-
it 'can read the percentage of time value' do
|
121
|
-
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
|
-
end
|
124
|
-
|
125
|
-
it 'can read the percentage of actors value' do
|
126
|
-
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
|
-
end
|
129
|
-
|
130
|
-
it 'returns nil for value that is not present' do
|
131
|
-
expect(described_class.new({})['not legit']).to be(nil)
|
132
|
-
end
|
133
|
-
end
|
134
103
|
end
|
@@ -9,7 +9,7 @@ RSpec.describe Flipper::Gates::Boolean do
|
|
9
9
|
Flipper::FeatureCheckContext.new(
|
10
10
|
feature_name: feature_name,
|
11
11
|
values: Flipper::GateValues.new(boolean: bool),
|
12
|
-
|
12
|
+
actors: [Flipper::Types::Actor.new(Flipper::Actor.new('1'))]
|
13
13
|
)
|
14
14
|
end
|
15
15
|
|