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
@@ -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(:
|
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(:
|
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(:
|
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(:
|
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) {
|
28
|
-
let(:five_percent_of_time) {
|
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
|
64
|
-
expect(feature.enabled?(
|
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
|
68
|
-
expect(feature.enabled?(
|
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?(
|
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?(
|
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
|
-
|
122
|
-
feature.enabled?(
|
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
|
-
|
145
|
-
feature.enabled?(
|
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
|
-
|
192
|
+
actor = Object.new
|
184
193
|
expect do
|
185
|
-
feature.enable(
|
186
|
-
end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{
|
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?(
|
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
|
-
|
224
|
-
feature.enabled?(
|
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
|
251
|
-
expect(feature.enabled?(
|
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
|
255
|
-
expect(feature.enabled?(
|
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?(
|
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?(
|
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(
|
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
|
-
|
307
|
-
feature.enabled?(
|
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(
|
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
|
-
|
356
|
+
actor = Object.new
|
346
357
|
expect do
|
347
|
-
feature.disable(
|
348
|
-
end.to raise_error(Flipper::GateNotFound, "Could not find gate for #{
|
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?(
|
377
|
-
expect(feature.enabled?(
|
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?(
|
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?(
|
388
|
-
expect(feature.enabled?(
|
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?(
|
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?(
|
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?(
|
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?(
|
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?(
|
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?(
|
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?(
|
520
|
+
expect(feature.enabled?(admin_actor)).to eq(true)
|
500
521
|
end
|
501
522
|
end
|
502
523
|
|
503
|
-
context 'for a non flipper
|
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?(
|
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?(
|
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?(
|
519
|
-
expect(feature.enabled?(
|
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?(
|
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?(
|
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
|
data/spec/flipper_spec.rb
CHANGED
@@ -64,7 +64,12 @@ RSpec.describe Flipper do
|
|
64
64
|
|
65
65
|
describe "delegation to instance" do
|
66
66
|
let(:group) { Flipper::Types::Group.new(:admins) }
|
67
|
-
let(:actor) {
|
67
|
+
let(:actor) {
|
68
|
+
Flipper::Actor.new("1", {
|
69
|
+
"plan" => "basic",
|
70
|
+
})
|
71
|
+
}
|
72
|
+
let(:expression) { Flipper.property(:plan).eq("basic") }
|
68
73
|
|
69
74
|
before do
|
70
75
|
described_class.configure do |config|
|
@@ -88,12 +93,35 @@ RSpec.describe Flipper do
|
|
88
93
|
expect(described_class.instance.enabled?(:search)).to be(false)
|
89
94
|
end
|
90
95
|
|
91
|
-
it 'delegates
|
92
|
-
expect(described_class.
|
96
|
+
it 'delegates expression to instance' do
|
97
|
+
expect(described_class.expression(:search)).to be(nil)
|
98
|
+
|
99
|
+
expression = Flipper.property(:plan).eq("basic")
|
100
|
+
Flipper.instance.enable_expression :search, expression
|
101
|
+
|
102
|
+
expect(described_class.expression(:search)).to eq(expression)
|
93
103
|
end
|
94
104
|
|
95
|
-
it 'delegates
|
96
|
-
|
105
|
+
it 'delegates enable_expression to instance' do
|
106
|
+
described_class.enable_expression(:search, expression)
|
107
|
+
expect(described_class.instance.enabled?(:search, actor)).to be(true)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'delegates disable_expression to instance' do
|
111
|
+
described_class.disable_expression(:search)
|
112
|
+
expect(described_class.instance.enabled?(:search, actor)).to be(false)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'delegates add_expression to instance' do
|
116
|
+
described_class.add_expression(:search, expression)
|
117
|
+
expect(described_class.instance.enabled?(:search, actor)).to be(true)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'delegates remove_expression to instance' do
|
121
|
+
described_class.enable_expression(:search, Flipper.any(expression))
|
122
|
+
expect(described_class.instance.enabled?(:search, actor)).to be(true)
|
123
|
+
described_class.remove_expression(:search, expression)
|
124
|
+
expect(described_class.instance.enabled?(:search, actor)).to be(false)
|
97
125
|
end
|
98
126
|
|
99
127
|
it 'delegates enable_actor to instance' do
|
@@ -106,10 +134,6 @@ RSpec.describe Flipper do
|
|
106
134
|
expect(described_class.instance.enabled?(:search, actor)).to be(false)
|
107
135
|
end
|
108
136
|
|
109
|
-
it 'delegates actor to instance' do
|
110
|
-
expect(described_class.actor(actor)).to eq(described_class.instance.actor(actor))
|
111
|
-
end
|
112
|
-
|
113
137
|
it 'delegates enable_group to instance' do
|
114
138
|
described_class.enable_group(:search, group)
|
115
139
|
expect(described_class.instance[:search].enabled_groups).to include(group)
|
@@ -130,15 +154,6 @@ RSpec.describe Flipper do
|
|
130
154
|
expect(described_class.instance[:search].percentage_of_actors_value).to be(0)
|
131
155
|
end
|
132
156
|
|
133
|
-
it 'delegates actors to instance' do
|
134
|
-
expect(described_class.actors(5)).to eq(described_class.instance.actors(5))
|
135
|
-
end
|
136
|
-
|
137
|
-
it 'delegates percentage_of_actors to instance' do
|
138
|
-
expected = described_class.instance.percentage_of_actors(5)
|
139
|
-
expect(described_class.percentage_of_actors(5)).to eq(expected)
|
140
|
-
end
|
141
|
-
|
142
157
|
it 'delegates enable_percentage_of_time to instance' do
|
143
158
|
described_class.enable_percentage_of_time(:search, 5)
|
144
159
|
expect(described_class.instance[:search].percentage_of_time_value).to be(5)
|
@@ -149,15 +164,6 @@ RSpec.describe Flipper do
|
|
149
164
|
expect(described_class.instance[:search].percentage_of_time_value).to be(0)
|
150
165
|
end
|
151
166
|
|
152
|
-
it 'delegates time to instance' do
|
153
|
-
expect(described_class.time(56)).to eq(described_class.instance.time(56))
|
154
|
-
end
|
155
|
-
|
156
|
-
it 'delegates percentage_of_time to instance' do
|
157
|
-
expected = described_class.instance.percentage_of_time(56)
|
158
|
-
expect(described_class.percentage_of_time(56)).to eq(expected)
|
159
|
-
end
|
160
|
-
|
161
167
|
it 'delegates features to instance' do
|
162
168
|
described_class.instance.add(:search)
|
163
169
|
expect(described_class.features).to eq(described_class.instance.features)
|
@@ -202,6 +208,12 @@ RSpec.describe Flipper do
|
|
202
208
|
expect(described_class.enabled?(:search)).to be(true)
|
203
209
|
end
|
204
210
|
|
211
|
+
it 'delegates export to instance' do
|
212
|
+
described_class.enable(:search)
|
213
|
+
expect(described_class.export).to eq(described_class.adapter.export)
|
214
|
+
expect(described_class.export(format: :json)).to eq(described_class.adapter.export(format: :json))
|
215
|
+
end
|
216
|
+
|
205
217
|
it 'delegates adapter to instance' do
|
206
218
|
expect(described_class.adapter).to eq(described_class.instance.adapter)
|
207
219
|
end
|
@@ -216,16 +228,20 @@ RSpec.describe Flipper do
|
|
216
228
|
expect(described_class.memoizing?).to eq(described_class.adapter.memoizing?)
|
217
229
|
end
|
218
230
|
|
231
|
+
it 'delegates read_only? to instance' do
|
232
|
+
expect(described_class.read_only?).to eq(described_class.adapter.read_only?)
|
233
|
+
end
|
234
|
+
|
219
235
|
it 'delegates sync stuff to instance and does nothing' do
|
220
236
|
expect(described_class.sync).to be(nil)
|
221
237
|
expect(described_class.sync_secret).to be(nil)
|
222
238
|
end
|
223
239
|
|
224
240
|
it 'delegates sync stuff to instance for Flipper::Cloud' do
|
225
|
-
stub = stub_request(:get, "https://www.flippercloud.io/adapter/features").
|
241
|
+
stub = stub_request(:get, "https://www.flippercloud.io/adapter/features?exclude_gate_names=true").
|
226
242
|
with({
|
227
243
|
headers: {
|
228
|
-
'
|
244
|
+
'flipper-cloud-token'=>'asdf',
|
229
245
|
},
|
230
246
|
}).to_return(status: 200, body: '{"features": {}}', headers: {})
|
231
247
|
cloud_configuration = Flipper::Cloud::Configuration.new({
|
@@ -297,7 +313,7 @@ RSpec.describe Flipper do
|
|
297
313
|
|
298
314
|
describe '.group_exists' do
|
299
315
|
it 'returns true if the group is already created' do
|
300
|
-
|
316
|
+
described_class.register('admins', &:admin?)
|
301
317
|
expect(described_class.group_exists?(:admins)).to eq(true)
|
302
318
|
end
|
303
319
|
|
@@ -350,4 +366,52 @@ RSpec.describe Flipper do
|
|
350
366
|
expect(described_class.instance_variable_get('@groups_registry')).to eq(registry)
|
351
367
|
end
|
352
368
|
end
|
369
|
+
|
370
|
+
describe ".constant" do
|
371
|
+
it "returns Flipper::Expression::Constant instance" do
|
372
|
+
expect(described_class.constant(false)).to eq(Flipper::Expression::Constant.new(false))
|
373
|
+
expect(described_class.constant("string")).to eq(Flipper::Expression::Constant.new("string"))
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
describe ".property" do
|
378
|
+
it "returns Flipper::Expressions::Property expression" do
|
379
|
+
expect(Flipper.property("name")).to eq(Flipper::Expression.build(Property: "name"))
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
describe ".boolean" do
|
384
|
+
it "returns Flipper::Expressions::Boolean expression" do
|
385
|
+
expect(described_class.boolean(true)).to eq(Flipper::Expression.build(Boolean: true))
|
386
|
+
expect(described_class.boolean(false)).to eq(Flipper::Expression.build(Boolean: false))
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
describe ".random" do
|
391
|
+
it "returns Flipper::Expressions::Random expression" do
|
392
|
+
expect(Flipper.random(100)).to eq(Flipper::Expression.build(Random: 100))
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
describe ".any" do
|
397
|
+
let(:age_expression) { Flipper.property(:age).gte(21) }
|
398
|
+
let(:plan_expression) { Flipper.property(:plan).eq("basic") }
|
399
|
+
|
400
|
+
it "returns Flipper::Expressions::Any instance" do
|
401
|
+
expect(Flipper.any(age_expression, plan_expression)).to eq(
|
402
|
+
Flipper::Expression.build({Any: [age_expression, plan_expression]})
|
403
|
+
)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
describe ".all" do
|
408
|
+
let(:age_expression) { Flipper.property(:age).gte(21) }
|
409
|
+
let(:plan_expression) { Flipper.property(:plan).eq("basic") }
|
410
|
+
|
411
|
+
it "returns Flipper::Expressions::All instance" do
|
412
|
+
expect(Flipper.all(age_expression, plan_expression)).to eq(
|
413
|
+
Flipper::Expression.build({All: [age_expression, plan_expression]})
|
414
|
+
)
|
415
|
+
end
|
416
|
+
end
|
353
417
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,14 +2,17 @@ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
|
|
2
2
|
|
3
3
|
require 'pp'
|
4
4
|
require 'pathname'
|
5
|
-
|
6
|
-
|
7
|
-
require 'rubygems'
|
8
|
-
require 'bundler'
|
5
|
+
require 'bundler/setup'
|
9
6
|
|
10
|
-
|
7
|
+
require 'warning'
|
8
|
+
Warning.ignore(/lib\/statsd/)
|
9
|
+
Warning.ignore(/lib\/debug\//)
|
10
|
+
Warning.ignore(/lib\/ice_age\//)
|
11
|
+
Warning.ignore(/lib\/moneta\//)
|
12
|
+
Warning.ignore(/lib\/mongo\//)
|
11
13
|
|
12
14
|
require 'debug'
|
15
|
+
require 'statsd'
|
13
16
|
require 'webmock/rspec'
|
14
17
|
WebMock.disable_net_connect!(allow_localhost: true)
|
15
18
|
|
@@ -17,12 +20,21 @@ require 'flipper'
|
|
17
20
|
require 'flipper/api'
|
18
21
|
require 'flipper/spec/shared_adapter_specs'
|
19
22
|
require 'flipper/ui'
|
23
|
+
require 'flipper/test_help'
|
20
24
|
|
25
|
+
FlipperRoot = Pathname(__FILE__).dirname.join('..').expand_path
|
21
26
|
Dir[FlipperRoot.join('spec/support/**/*.rb')].sort.each { |f| require f }
|
22
27
|
|
28
|
+
# Disable telemetry logging in specs.
|
29
|
+
ENV["FLIPPER_CLOUD_LOGGING_ENABLED"] = "false"
|
30
|
+
|
23
31
|
RSpec.configure do |config|
|
24
32
|
config.before(:example) do
|
25
|
-
|
33
|
+
# default stub for telemetry
|
34
|
+
stub_request(:post, "https://www.flippercloud.io/adapter/telemetry").
|
35
|
+
to_return(status: 200, body: "", headers: {})
|
36
|
+
Flipper::Cloud::Telemetry.reset if defined?(Flipper::Cloud::Telemetry) && Flipper::Cloud::Telemetry.respond_to?(:reset)
|
37
|
+
Flipper::Poller.reset if defined?(Flipper::Poller)
|
26
38
|
Flipper.unregister_groups
|
27
39
|
Flipper.configuration = nil
|
28
40
|
end
|
@@ -91,15 +103,3 @@ RSpec.shared_examples_for 'a DSL feature' do
|
|
91
103
|
end.to raise_error(ArgumentError, /must be a String or Symbol/)
|
92
104
|
end
|
93
105
|
end
|
94
|
-
|
95
|
-
RSpec.shared_examples_for 'a DSL boolean method' do
|
96
|
-
it 'returns boolean with value set' do
|
97
|
-
result = subject.send(method_name, true)
|
98
|
-
expect(result).to be_instance_of(Flipper::Types::Boolean)
|
99
|
-
expect(result.value).to be(true)
|
100
|
-
|
101
|
-
result = subject.send(method_name, false)
|
102
|
-
expect(result).to be_instance_of(Flipper::Types::Boolean)
|
103
|
-
expect(result.value).to be(false)
|
104
|
-
end
|
105
|
-
end
|
@@ -0,0 +1 @@
|
|
1
|
+
actor_name_1: 'Actor #1'
|