flipper 0.26.0 → 1.3.6
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 +61 -16
- data/.github/workflows/examples.yml +55 -18
- data/CLAUDE.md +74 -0
- data/Changelog.md +1 -486
- data/Gemfile +23 -11
- data/README.md +31 -27
- data/Rakefile +2 -2
- 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/banner.jpg +0 -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/exe/flipper +5 -0
- data/flipper-cloud.gemspec +19 -0
- data/flipper.gemspec +8 -6
- 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/actor_limit.rb +28 -0
- data/lib/flipper/adapters/cache_base.rb +143 -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 +40 -12
- data/lib/flipper/adapters/http/error.rb +2 -2
- data/lib/flipper/adapters/http.rb +30 -17
- 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 +17 -78
- data/lib/flipper/adapters/poll/poller.rb +2 -125
- data/lib/flipper/adapters/poll.rb +20 -3
- data/lib/flipper/adapters/pstore.rb +17 -11
- data/lib/flipper/adapters/read_only.rb +8 -41
- data/lib/flipper/adapters/strict.rb +45 -0
- data/lib/flipper/adapters/sync/feature_synchronizer.rb +10 -1
- data/lib/flipper/adapters/sync.rb +0 -4
- data/lib/flipper/adapters/wrapper.rb +54 -0
- data/lib/flipper/cli.rb +263 -0
- data/lib/flipper/cloud/configuration.rb +266 -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 +96 -0
- data/lib/flipper/cloud/telemetry/instrumenter.rb +22 -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 +100 -0
- data/lib/flipper/cloud/telemetry.rb +191 -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 +102 -0
- data/lib/flipper/errors.rb +3 -3
- data/lib/flipper/export.rb +24 -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 +9 -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 +94 -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 +35 -8
- data/lib/flipper/instrumentation/statsd.rb +4 -2
- data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
- data/lib/flipper/instrumentation/subscriber.rb +8 -5
- data/lib/flipper/metadata.rb +8 -1
- data/lib/flipper/middleware/memoizer.rb +30 -14
- data/lib/flipper/model/active_record.rb +23 -0
- data/lib/flipper/poller.rb +118 -0
- data/lib/flipper/serializers/gzip.rb +22 -0
- data/lib/flipper/serializers/json.rb +17 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +105 -63
- data/lib/flipper/test/shared_adapter_test.rb +101 -58
- data/lib/flipper/test_help.rb +43 -0
- data/lib/flipper/typecast.rb +59 -18
- 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 +11 -1
- data/lib/flipper.rb +50 -11
- data/lib/generators/flipper/setup_generator.rb +68 -0
- data/lib/generators/flipper/templates/initializer.rb +45 -0
- data/lib/generators/flipper/templates/update/migrations/01_create_flipper_tables.rb.erb +22 -0
- data/lib/generators/flipper/templates/update/migrations/02_change_flipper_gates_value_to_text.rb.erb +18 -0
- data/lib/generators/flipper/update_generator.rb +35 -0
- data/package-lock.json +41 -0
- data/package.json +10 -0
- data/spec/fixtures/environment.rb +1 -0
- data/spec/fixtures/flipper_pstore_1679087600.json +46 -0
- data/spec/flipper/adapter_builder_spec.rb +72 -0
- data/spec/flipper/adapter_spec.rb +30 -2
- data/spec/flipper/adapters/actor_limit_spec.rb +20 -0
- data/spec/flipper/adapters/dual_write_spec.rb +2 -2
- data/spec/flipper/adapters/http/client_spec.rb +61 -0
- data/spec/flipper/adapters/http_spec.rb +138 -55
- 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/poll_spec.rb +41 -0
- data/spec/flipper/adapters/read_only_spec.rb +32 -17
- data/spec/flipper/adapters/strict_spec.rb +64 -0
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
- data/spec/flipper/cli_spec.rb +166 -0
- data/spec/flipper/cloud/configuration_spec.rb +251 -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 +107 -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 +208 -0
- data/spec/flipper/cloud_spec.rb +186 -0
- data/spec/flipper/configuration_spec.rb +17 -0
- data/spec/flipper/dsl_spec.rb +54 -76
- data/spec/flipper/engine_spec.rb +374 -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 +453 -39
- 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 +24 -6
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +26 -2
- data/spec/flipper/middleware/memoizer_spec.rb +79 -10
- data/spec/flipper/model/active_record_spec.rb +72 -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 +94 -30
- data/spec/spec_helper.rb +18 -18
- data/spec/support/actor_names.yml +1 -0
- data/spec/support/fail_on_output.rb +8 -0
- data/spec/support/fake_backoff_policy.rb +15 -0
- data/spec/support/skippable.rb +18 -0
- data/spec/support/spec_helpers.rb +34 -8
- data/test/adapters/actor_limit_test.rb +20 -0
- data/test_rails/generators/flipper/setup_generator_test.rb +69 -0
- data/test_rails/generators/flipper/update_generator_test.rb +96 -0
- data/test_rails/helper.rb +22 -2
- data/test_rails/system/test_help_test.rb +52 -0
- metadata +203 -20
- 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,72 @@ 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
|
+
|
80
|
+
context "for an object that implements .nil? == true" do
|
81
|
+
let(:actor) { Flipper::Actor.new("User;1") }
|
82
|
+
|
83
|
+
before do
|
84
|
+
def actor.nil?
|
85
|
+
true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'returns true if feature is enabled' do
|
90
|
+
subject.enable
|
91
|
+
expect(subject.enabled?(actor)).to be(true)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'returns false if feature is disabled' do
|
95
|
+
subject.disable
|
96
|
+
expect(subject.enabled?(actor)).to be(false)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
35
101
|
describe '#to_s' do
|
36
102
|
it 'returns name as string' do
|
37
103
|
feature = described_class.new(:search, adapter)
|
@@ -63,7 +129,7 @@ RSpec.describe Flipper::Feature do
|
|
63
129
|
instance.gates.each do |gate|
|
64
130
|
expect(gate).to be_a(Flipper::Gate)
|
65
131
|
end
|
66
|
-
expect(instance.gates.size).to be(
|
132
|
+
expect(instance.gates.size).to be(6)
|
67
133
|
end
|
68
134
|
end
|
69
135
|
|
@@ -148,34 +214,33 @@ RSpec.describe Flipper::Feature do
|
|
148
214
|
end
|
149
215
|
|
150
216
|
it 'is recorded for enable' do
|
151
|
-
|
152
|
-
|
217
|
+
actor = Flipper::Types::Actor.new(Flipper::Actor.new('1'))
|
218
|
+
subject.gate_for(actor)
|
153
219
|
|
154
|
-
subject.enable(
|
220
|
+
subject.enable(actor)
|
155
221
|
|
156
222
|
event = instrumenter.events.last
|
157
223
|
expect(event).not_to be_nil
|
158
224
|
expect(event.name).to eq('feature_operation.flipper')
|
159
225
|
expect(event.payload[:feature_name]).to eq(:search)
|
160
226
|
expect(event.payload[:operation]).to eq(:enable)
|
161
|
-
expect(event.payload[:thing]).to eq(
|
227
|
+
expect(event.payload[:thing]).to eq(actor)
|
162
228
|
expect(event.payload[:result]).not_to be_nil
|
163
229
|
end
|
164
230
|
|
165
231
|
it 'always instruments flipper type instance for enable' do
|
166
|
-
|
167
|
-
|
232
|
+
actor = Flipper::Actor.new('1')
|
233
|
+
subject.gate_for(actor)
|
168
234
|
|
169
|
-
subject.enable(
|
235
|
+
subject.enable(actor)
|
170
236
|
|
171
237
|
event = instrumenter.events.last
|
172
238
|
expect(event).not_to be_nil
|
173
|
-
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(
|
239
|
+
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(actor))
|
174
240
|
end
|
175
241
|
|
176
242
|
it 'is recorded for disable' do
|
177
243
|
thing = Flipper::Types::Boolean.new
|
178
|
-
gate = subject.gate_for(thing)
|
179
244
|
|
180
245
|
subject.disable(thing)
|
181
246
|
|
@@ -219,15 +284,14 @@ RSpec.describe Flipper::Feature do
|
|
219
284
|
end
|
220
285
|
|
221
286
|
it 'always instruments flipper type instance for disable' do
|
222
|
-
|
223
|
-
gate = subject.gate_for(thing)
|
287
|
+
actor = Flipper::Actor.new('1')
|
224
288
|
|
225
|
-
subject.disable(
|
289
|
+
subject.disable(actor)
|
226
290
|
|
227
291
|
event = instrumenter.events.last
|
228
292
|
expect(event).not_to be_nil
|
229
293
|
expect(event.payload[:operation]).to eq(:disable)
|
230
|
-
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(
|
294
|
+
expect(event.payload[:thing]).to eq(Flipper::Types::Actor.new(actor))
|
231
295
|
end
|
232
296
|
|
233
297
|
it 'is recorded for add' do
|
@@ -275,17 +339,15 @@ RSpec.describe Flipper::Feature do
|
|
275
339
|
end
|
276
340
|
|
277
341
|
it 'is recorded for enabled?' do
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
subject.enabled?(thing)
|
342
|
+
actor = Flipper::Types::Actor.new(Flipper::Actor.new('1'))
|
343
|
+
subject.enabled?(actor)
|
282
344
|
|
283
345
|
event = instrumenter.events.last
|
284
346
|
expect(event).not_to be_nil
|
285
347
|
expect(event.name).to eq('feature_operation.flipper')
|
286
348
|
expect(event.payload[:feature_name]).to eq(:search)
|
287
349
|
expect(event.payload[:operation]).to eq(:enabled?)
|
288
|
-
expect(event.payload[:
|
350
|
+
expect(event.payload[:actors]).to eq([actor])
|
289
351
|
expect(event.payload[:result]).to eq(false)
|
290
352
|
end
|
291
353
|
|
@@ -293,8 +355,8 @@ RSpec.describe Flipper::Feature do
|
|
293
355
|
actor = Flipper::Types::Actor.new(user)
|
294
356
|
{
|
295
357
|
nil => nil,
|
296
|
-
user => actor,
|
297
|
-
actor => actor,
|
358
|
+
user => [actor],
|
359
|
+
actor => [actor],
|
298
360
|
}.each do |thing, wrapped_thing|
|
299
361
|
it "always instruments #{thing.inspect} as #{wrapped_thing.class} for enabled?" do
|
300
362
|
subject.enabled?(thing)
|
@@ -302,7 +364,7 @@ RSpec.describe Flipper::Feature do
|
|
302
364
|
event = instrumenter.events.last
|
303
365
|
expect(event).not_to be_nil
|
304
366
|
expect(event.payload[:operation]).to eq(:enabled?)
|
305
|
-
expect(event.payload[:
|
367
|
+
expect(event.payload[:actors]).to eq(wrapped_thing)
|
306
368
|
end
|
307
369
|
end
|
308
370
|
end
|
@@ -428,10 +490,10 @@ RSpec.describe Flipper::Feature do
|
|
428
490
|
|
429
491
|
context 'when one or more groups enabled' do
|
430
492
|
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) { |
|
493
|
+
@staff = Flipper.register(:staff) { |actor| true }
|
494
|
+
@preview_features = Flipper.register(:preview_features) { |actor| true }
|
495
|
+
@not_enabled = Flipper.register(:not_enabled) { |actor| true }
|
496
|
+
@disabled = Flipper.register(:disabled) { |actor| true }
|
435
497
|
subject.enable @staff
|
436
498
|
subject.enable @preview_features
|
437
499
|
subject.disable @disabled
|
@@ -467,10 +529,10 @@ RSpec.describe Flipper::Feature do
|
|
467
529
|
|
468
530
|
context 'when one or more groups enabled' do
|
469
531
|
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) { |
|
532
|
+
@staff = Flipper.register(:staff) { |actor| true }
|
533
|
+
@preview_features = Flipper.register(:preview_features) { |actor| true }
|
534
|
+
@not_enabled = Flipper.register(:not_enabled) { |actor| true }
|
535
|
+
@disabled = Flipper.register(:disabled) { |actor| true }
|
474
536
|
subject.enable @staff
|
475
537
|
subject.enable @preview_features
|
476
538
|
subject.disable @disabled
|
@@ -494,10 +556,10 @@ RSpec.describe Flipper::Feature do
|
|
494
556
|
|
495
557
|
context 'when one or more groups enabled' do
|
496
558
|
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) { |
|
559
|
+
@staff = Flipper.register(:staff) { |actor| true }
|
560
|
+
@preview_features = Flipper.register(:preview_features) { |actor| true }
|
561
|
+
@not_enabled = Flipper.register(:not_enabled) { |actor| true }
|
562
|
+
@disabled = Flipper.register(:disabled) { |actor| true }
|
501
563
|
subject.enable @staff
|
502
564
|
subject.enable @preview_features
|
503
565
|
subject.disable @disabled
|
@@ -649,6 +711,361 @@ RSpec.describe Flipper::Feature do
|
|
649
711
|
end
|
650
712
|
end
|
651
713
|
|
714
|
+
describe '#expression' do
|
715
|
+
it "returns nil if feature has no expression" do
|
716
|
+
expect(subject.expression).to be(nil)
|
717
|
+
end
|
718
|
+
|
719
|
+
it "returns expression if feature has expression" do
|
720
|
+
expression = Flipper.property(:plan).eq("basic")
|
721
|
+
subject.enable_expression expression
|
722
|
+
expect(subject.expression).to eq(expression)
|
723
|
+
end
|
724
|
+
end
|
725
|
+
|
726
|
+
describe '#enable_expression/disable_expression' do
|
727
|
+
context "with expression instance" do
|
728
|
+
it "updates gate values to equal expression or clears expression" do
|
729
|
+
expression = Flipper.property(:plan).eq("basic")
|
730
|
+
expect(subject.gate_values.expression).to be(nil)
|
731
|
+
subject.enable_expression(expression)
|
732
|
+
expect(subject.gate_values.expression).to eq(expression.value)
|
733
|
+
subject.disable_expression
|
734
|
+
expect(subject.gate_values.expression).to be(nil)
|
735
|
+
end
|
736
|
+
end
|
737
|
+
|
738
|
+
context "with Hash" do
|
739
|
+
it "updates gate values to equal expression or clears expression" do
|
740
|
+
expression = Flipper.property(:plan).eq("basic")
|
741
|
+
expect(subject.gate_values.expression).to be(nil)
|
742
|
+
subject.enable_expression(expression.value)
|
743
|
+
expect(subject.gate_values.expression).to eq(expression.value)
|
744
|
+
subject.disable_expression
|
745
|
+
expect(subject.gate_values.expression).to be(nil)
|
746
|
+
end
|
747
|
+
end
|
748
|
+
end
|
749
|
+
|
750
|
+
describe "#add_expression" do
|
751
|
+
context "when nothing enabled" do
|
752
|
+
context "with Expression instance" do
|
753
|
+
it "sets expression to Expression" do
|
754
|
+
expression = Flipper.property(:plan).eq("basic")
|
755
|
+
subject.add_expression(expression)
|
756
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
757
|
+
expect(subject.expression).to eq(expression)
|
758
|
+
end
|
759
|
+
end
|
760
|
+
|
761
|
+
context "with Any instance" do
|
762
|
+
it "sets expression to Any" do
|
763
|
+
expression = Flipper.any(Flipper.property(:plan).eq("basic"))
|
764
|
+
subject.add_expression(expression)
|
765
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
766
|
+
expect(subject.expression).to eq(expression)
|
767
|
+
end
|
768
|
+
end
|
769
|
+
|
770
|
+
context "with All instance" do
|
771
|
+
it "sets expression to All" do
|
772
|
+
expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
773
|
+
subject.add_expression(expression)
|
774
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
775
|
+
expect(subject.expression).to eq(expression)
|
776
|
+
end
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
context "when Expression enabled" do
|
781
|
+
let(:expression) { Flipper.property(:plan).eq("basic") }
|
782
|
+
|
783
|
+
before do
|
784
|
+
subject.enable_expression expression
|
785
|
+
end
|
786
|
+
|
787
|
+
context "with Expression instance" do
|
788
|
+
it "changes expression to Any and adds new Expression" do
|
789
|
+
new_expression = Flipper.property(:age).gte(21)
|
790
|
+
subject.add_expression(new_expression)
|
791
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
792
|
+
expect(subject.expression.args).to include(expression)
|
793
|
+
expect(subject.expression.args).to include(new_expression)
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
context "with Any instance" do
|
798
|
+
it "changes expression to Any and adds new Any" do
|
799
|
+
new_expression = Flipper.any(Flipper.property(:age).eq(21))
|
800
|
+
subject.add_expression new_expression
|
801
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
802
|
+
expect(subject.expression.args).to include(expression)
|
803
|
+
expect(subject.expression.args).to include(new_expression)
|
804
|
+
end
|
805
|
+
end
|
806
|
+
|
807
|
+
context "with All instance" do
|
808
|
+
it "changes expression to Any and adds new All" do
|
809
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
810
|
+
subject.add_expression new_expression
|
811
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
812
|
+
expect(subject.expression.args).to include(expression)
|
813
|
+
expect(subject.expression.args).to include(new_expression)
|
814
|
+
end
|
815
|
+
end
|
816
|
+
end
|
817
|
+
|
818
|
+
context "when Any enabled" do
|
819
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
820
|
+
let(:expression) { Flipper.any(condition) }
|
821
|
+
|
822
|
+
before do
|
823
|
+
subject.enable_expression expression
|
824
|
+
end
|
825
|
+
|
826
|
+
context "with Expression instance" do
|
827
|
+
it "adds Expression to Any" do
|
828
|
+
new_expression = Flipper.property(:age).gte(21)
|
829
|
+
subject.add_expression(new_expression)
|
830
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
831
|
+
expect(subject.expression.args).to include(condition)
|
832
|
+
expect(subject.expression.args).to include(new_expression)
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
context "with Any instance" do
|
837
|
+
it "adds Any to Any" do
|
838
|
+
new_expression = Flipper.any(Flipper.property(:age).gte(21))
|
839
|
+
subject.add_expression(new_expression)
|
840
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
841
|
+
expect(subject.expression.args).to include(condition)
|
842
|
+
expect(subject.expression.args).to include(new_expression)
|
843
|
+
end
|
844
|
+
end
|
845
|
+
|
846
|
+
context "with All instance" do
|
847
|
+
it "adds All to Any" do
|
848
|
+
new_expression = Flipper.all(Flipper.property(:age).gte(21))
|
849
|
+
subject.add_expression(new_expression)
|
850
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
851
|
+
expect(subject.expression.args).to include(condition)
|
852
|
+
expect(subject.expression.args).to include(new_expression)
|
853
|
+
end
|
854
|
+
end
|
855
|
+
end
|
856
|
+
|
857
|
+
context "when All enabled" do
|
858
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
859
|
+
let(:expression) { Flipper.all(condition) }
|
860
|
+
|
861
|
+
before do
|
862
|
+
subject.enable_expression expression
|
863
|
+
end
|
864
|
+
|
865
|
+
context "with Expression instance" do
|
866
|
+
it "adds Expression to All" do
|
867
|
+
new_expression = Flipper.property(:age).gte(21)
|
868
|
+
subject.add_expression(new_expression)
|
869
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
870
|
+
expect(subject.expression.args).to include(condition)
|
871
|
+
expect(subject.expression.args).to include(new_expression)
|
872
|
+
end
|
873
|
+
end
|
874
|
+
|
875
|
+
context "with Any instance" do
|
876
|
+
it "adds Any to All" do
|
877
|
+
new_expression = Flipper.any(Flipper.property(:age).gte(21))
|
878
|
+
subject.add_expression(new_expression)
|
879
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
880
|
+
expect(subject.expression.args).to include(condition)
|
881
|
+
expect(subject.expression.args).to include(new_expression)
|
882
|
+
end
|
883
|
+
end
|
884
|
+
|
885
|
+
context "with All instance" do
|
886
|
+
it "adds All to All" do
|
887
|
+
new_expression = Flipper.all(Flipper.property(:age).gte(21))
|
888
|
+
subject.add_expression(new_expression)
|
889
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
890
|
+
expect(subject.expression.args).to include(condition)
|
891
|
+
expect(subject.expression.args).to include(new_expression)
|
892
|
+
end
|
893
|
+
end
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
describe '#remove_expression' do
|
898
|
+
context "when nothing enabled" do
|
899
|
+
context "with Expression instance" do
|
900
|
+
it "does nothing" do
|
901
|
+
expression = Flipper.property(:plan).eq("basic")
|
902
|
+
subject.remove_expression(expression)
|
903
|
+
expect(subject.expression).to be(nil)
|
904
|
+
end
|
905
|
+
end
|
906
|
+
|
907
|
+
context "with Any instance" do
|
908
|
+
it "does nothing" do
|
909
|
+
expression = Flipper.any(Flipper.property(:plan).eq("basic"))
|
910
|
+
subject.remove_expression expression
|
911
|
+
expect(subject.expression).to be(nil)
|
912
|
+
end
|
913
|
+
end
|
914
|
+
|
915
|
+
context "with All instance" do
|
916
|
+
it "does nothing" do
|
917
|
+
expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
918
|
+
subject.remove_expression expression
|
919
|
+
expect(subject.expression).to be(nil)
|
920
|
+
end
|
921
|
+
end
|
922
|
+
end
|
923
|
+
|
924
|
+
context "when Expression enabled" do
|
925
|
+
let(:expression) { Flipper.property(:plan).eq("basic") }
|
926
|
+
|
927
|
+
before do
|
928
|
+
subject.enable_expression expression
|
929
|
+
end
|
930
|
+
|
931
|
+
context "with Expression instance" do
|
932
|
+
it "changes expression to Any and removes Expression if it matches" do
|
933
|
+
new_expression = Flipper.property(:plan).eq("basic")
|
934
|
+
subject.remove_expression new_expression
|
935
|
+
expect(subject.expression).to eq(Flipper.any)
|
936
|
+
end
|
937
|
+
|
938
|
+
it "changes expression to Any if Expression doesn't match" do
|
939
|
+
new_expression = Flipper.property(:plan).eq("premium")
|
940
|
+
subject.remove_expression new_expression
|
941
|
+
expect(subject.expression).to eq(Flipper.any(expression))
|
942
|
+
end
|
943
|
+
end
|
944
|
+
|
945
|
+
context "with Any instance" do
|
946
|
+
it "changes expression to Any and does nothing" do
|
947
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("basic"))
|
948
|
+
subject.remove_expression new_expression
|
949
|
+
expect(subject.expression).to eq(Flipper.any(expression))
|
950
|
+
end
|
951
|
+
end
|
952
|
+
|
953
|
+
context "with All instance" do
|
954
|
+
it "changes expression to Any and does nothing" do
|
955
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
956
|
+
subject.remove_expression new_expression
|
957
|
+
expect(subject.expression).to eq(Flipper.any(expression))
|
958
|
+
end
|
959
|
+
end
|
960
|
+
end
|
961
|
+
|
962
|
+
context "when Any enabled" do
|
963
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
964
|
+
let(:expression) { Flipper.any condition }
|
965
|
+
|
966
|
+
before do
|
967
|
+
subject.enable_expression expression
|
968
|
+
end
|
969
|
+
|
970
|
+
context "with Expression instance" do
|
971
|
+
it "removes Expression if it matches" do
|
972
|
+
subject.remove_expression condition
|
973
|
+
expect(subject.expression).to eq(Flipper.any)
|
974
|
+
end
|
975
|
+
|
976
|
+
it "does nothing if Expression does not match" do
|
977
|
+
subject.remove_expression Flipper.property(:plan).eq("premium")
|
978
|
+
expect(subject.expression).to eq(expression)
|
979
|
+
end
|
980
|
+
end
|
981
|
+
|
982
|
+
context "with Any instance" do
|
983
|
+
it "removes Any if it matches" do
|
984
|
+
new_expression = Flipper.any(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 Any does not match" do
|
992
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("premium"))
|
993
|
+
subject.remove_expression new_expression
|
994
|
+
expect(subject.expression).to eq(expression)
|
995
|
+
end
|
996
|
+
end
|
997
|
+
|
998
|
+
context "with All instance" do
|
999
|
+
it "removes All if it matches" do
|
1000
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
1001
|
+
subject.add_expression new_expression
|
1002
|
+
expect(subject.expression.args.size).to be(2)
|
1003
|
+
subject.remove_expression new_expression
|
1004
|
+
expect(subject.expression).to eq(expression)
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
it "does nothing if All does not match" do
|
1008
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
1009
|
+
subject.remove_expression new_expression
|
1010
|
+
expect(subject.expression).to eq(expression)
|
1011
|
+
end
|
1012
|
+
end
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
context "when All enabled" do
|
1016
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
1017
|
+
let(:expression) { Flipper.all condition }
|
1018
|
+
|
1019
|
+
before do
|
1020
|
+
subject.enable_expression expression
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
context "with Expression instance" do
|
1024
|
+
it "removes Expression if it matches" do
|
1025
|
+
subject.remove_expression condition
|
1026
|
+
expect(subject.expression).to eq(Flipper.all)
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
it "does nothing if Expression does not match" do
|
1030
|
+
subject.remove_expression Flipper.property(:plan).eq("premium")
|
1031
|
+
expect(subject.expression).to eq(expression)
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
context "with Any instance" do
|
1036
|
+
it "removes Any if it matches" do
|
1037
|
+
new_expression = Flipper.any(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 Any does not match" do
|
1045
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("premium"))
|
1046
|
+
subject.remove_expression new_expression
|
1047
|
+
expect(subject.expression).to eq(expression)
|
1048
|
+
end
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
context "with All instance" do
|
1052
|
+
it "removes All if it matches" do
|
1053
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
1054
|
+
subject.add_expression new_expression
|
1055
|
+
expect(subject.expression.args.size).to be(2)
|
1056
|
+
subject.remove_expression new_expression
|
1057
|
+
expect(subject.expression).to eq(expression)
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
it "does nothing if All does not match" do
|
1061
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
1062
|
+
subject.remove_expression new_expression
|
1063
|
+
expect(subject.expression).to eq(expression)
|
1064
|
+
end
|
1065
|
+
end
|
1066
|
+
end
|
1067
|
+
end
|
1068
|
+
|
652
1069
|
describe '#enable_actor/disable_actor' do
|
653
1070
|
context 'with object that responds to flipper_id' do
|
654
1071
|
it 'updates the gate values to include the actor' do
|
@@ -677,8 +1094,6 @@ RSpec.describe Flipper::Feature do
|
|
677
1094
|
describe '#enable_group/disable_group' do
|
678
1095
|
context 'with symbol group name' do
|
679
1096
|
it 'updates the gate values to include the group' do
|
680
|
-
actor = Flipper::Actor.new(5)
|
681
|
-
group = Flipper.register(:five_only) { |actor| actor.flipper_id == 5 }
|
682
1097
|
expect(subject.gate_values.groups).to be_empty
|
683
1098
|
subject.enable_group(:five_only)
|
684
1099
|
expect(subject.gate_values.groups).to eq(Set['five_only'])
|
@@ -689,8 +1104,6 @@ RSpec.describe Flipper::Feature do
|
|
689
1104
|
|
690
1105
|
context 'with string group name' do
|
691
1106
|
it 'updates the gate values to include the group' do
|
692
|
-
actor = Flipper::Actor.new(5)
|
693
|
-
group = Flipper.register(:five_only) { |actor| actor.flipper_id == 5 }
|
694
1107
|
expect(subject.gate_values.groups).to be_empty
|
695
1108
|
subject.enable_group('five_only')
|
696
1109
|
expect(subject.gate_values.groups).to eq(Set['five_only'])
|
@@ -701,7 +1114,6 @@ RSpec.describe Flipper::Feature do
|
|
701
1114
|
|
702
1115
|
context 'with group instance' do
|
703
1116
|
it 'updates the gate values for the group' do
|
704
|
-
actor = Flipper::Actor.new(5)
|
705
1117
|
group = Flipper.register(:five_only) { |actor| actor.flipper_id == 5 }
|
706
1118
|
expect(subject.gate_values.groups).to be_empty
|
707
1119
|
subject.enable_group(group)
|
@@ -801,12 +1213,14 @@ RSpec.describe Flipper::Feature do
|
|
801
1213
|
:actor,
|
802
1214
|
:boolean,
|
803
1215
|
:group,
|
1216
|
+
:expression,
|
804
1217
|
])
|
805
1218
|
|
806
1219
|
expect(subject.disabled_gate_names.to_set).to eq(Set[
|
807
1220
|
:actor,
|
808
1221
|
:boolean,
|
809
1222
|
:group,
|
1223
|
+
:expression,
|
810
1224
|
])
|
811
1225
|
end
|
812
1226
|
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
|
|