flipper 0.16.0 → 1.4.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 +5 -5
- data/.codeclimate.yml +1 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +110 -0
- data/.github/workflows/examples.yml +105 -0
- data/.github/workflows/release.yml +54 -0
- data/.rspec +1 -0
- data/CLAUDE.md +87 -0
- data/Changelog.md +2 -215
- data/Dockerfile +1 -1
- data/Gemfile +28 -20
- data/README.md +72 -62
- data/Rakefile +13 -3
- 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/docker-compose.yml +37 -34
- data/docs/DockerCompose.md +0 -1
- data/docs/README.md +1 -0
- data/docs/images/banner.jpg +0 -0
- data/docs/images/flipper_cloud.png +0 -0
- data/examples/api/basic.ru +18 -0
- data/examples/api/custom_memoized.ru +36 -0
- data/examples/api/memoized.ru +42 -0
- data/examples/basic.rb +1 -12
- 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/poll_interval/README.md +111 -0
- data/examples/cloud/poll_interval/client.rb +108 -0
- data/examples/cloud/poll_interval/server.rb +98 -0
- data/examples/cloud/threaded.rb +33 -0
- data/examples/configuring_default.rb +2 -5
- data/examples/dsl.rb +10 -35
- data/examples/enabled_for_actor.rb +10 -15
- data/examples/expressions.rb +237 -0
- data/examples/group.rb +3 -6
- data/examples/group_dynamic_lookup.rb +5 -19
- data/examples/group_with_members.rb +4 -14
- data/examples/importing.rb +1 -1
- data/examples/individual_actor.rb +2 -5
- data/examples/instrumentation.rb +2 -2
- data/examples/instrumentation_last_accessed_at.rb +38 -0
- data/examples/memoizing.rb +35 -0
- data/examples/mirroring.rb +59 -0
- data/examples/percentage_of_actors.rb +6 -16
- data/examples/percentage_of_actors_enabled_check.rb +7 -10
- data/examples/percentage_of_actors_group.rb +5 -18
- data/examples/percentage_of_time.rb +3 -6
- data/examples/strict.rb +18 -0
- data/exe/flipper +5 -0
- data/flipper-cloud.gemspec +19 -0
- data/flipper.gemspec +10 -7
- data/lib/flipper/actor.rb +10 -3
- data/lib/flipper/adapter.rb +50 -8
- data/lib/flipper/adapter_builder.rb +44 -0
- data/lib/flipper/adapters/actor_limit.rb +54 -0
- data/lib/flipper/adapters/cache_base.rb +161 -0
- data/lib/flipper/adapters/dual_write.rb +63 -0
- data/lib/flipper/adapters/failover.rb +85 -0
- data/lib/flipper/adapters/failsafe.rb +72 -0
- data/lib/flipper/adapters/http/client.rb +64 -7
- data/lib/flipper/adapters/http/error.rb +19 -1
- data/lib/flipper/adapters/http.rb +97 -43
- data/lib/flipper/adapters/instrumented.rb +47 -26
- data/lib/flipper/adapters/memoizable.rb +44 -40
- data/lib/flipper/adapters/memory.rb +75 -111
- data/lib/flipper/adapters/operation_logger.rb +22 -78
- data/lib/flipper/adapters/poll/poller.rb +2 -0
- data/lib/flipper/adapters/poll.rb +52 -0
- data/lib/flipper/adapters/pstore.rb +27 -17
- 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 +14 -1
- data/lib/flipper/adapters/sync/interval_synchronizer.rb +2 -7
- data/lib/flipper/adapters/sync/synchronizer.rb +13 -6
- data/lib/flipper/adapters/sync.rb +23 -29
- data/lib/flipper/adapters/wrapper.rb +54 -0
- data/lib/flipper/cli.rb +314 -0
- data/lib/flipper/cloud/configuration.rb +271 -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/migrate.rb +71 -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 +54 -0
- data/lib/flipper/configuration.rb +54 -7
- data/lib/flipper/dsl.rb +58 -47
- data/lib/flipper/engine.rb +102 -0
- data/lib/flipper/errors.rb +3 -21
- 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/equal.rb +9 -0
- data/lib/flipper/expressions/feature_enabled.rb +34 -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 +16 -0
- data/lib/flipper/feature.rb +95 -28
- 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 +17 -0
- 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/instrumenters/memory.rb +6 -2
- data/lib/flipper/metadata.rb +8 -1
- data/lib/flipper/middleware/memoizer.rb +46 -27
- data/lib/flipper/middleware/setup_env.rb +13 -3
- data/lib/flipper/model/active_record.rb +23 -0
- data/lib/flipper/poller.rb +157 -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 +122 -56
- data/lib/flipper/test/shared_adapter_test.rb +120 -52
- data/lib/flipper/test_help.rb +43 -0
- data/lib/flipper/typecast.rb +59 -18
- data/lib/flipper/types/actor.rb +19 -13
- data/lib/flipper/types/group.rb +12 -5
- data/lib/flipper/types/percentage.rb +1 -1
- data/lib/flipper/version.rb +11 -1
- data/lib/flipper.rb +71 -12
- 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/actor_spec.rb +10 -2
- data/spec/flipper/adapter_builder_spec.rb +72 -0
- data/spec/flipper/adapter_spec.rb +52 -6
- data/spec/flipper/adapters/actor_limit_spec.rb +75 -0
- data/spec/flipper/adapters/dual_write_spec.rb +82 -0
- data/spec/flipper/adapters/failover_spec.rb +141 -0
- data/spec/flipper/adapters/failsafe_spec.rb +58 -0
- data/spec/flipper/adapters/http/client_spec.rb +61 -0
- data/spec/flipper/adapters/http_spec.rb +402 -65
- data/spec/flipper/adapters/instrumented_spec.rb +31 -13
- data/spec/flipper/adapters/memoizable_spec.rb +51 -33
- data/spec/flipper/adapters/memory_spec.rb +33 -5
- data/spec/flipper/adapters/operation_logger_spec.rb +38 -12
- data/spec/flipper/adapters/poll_spec.rb +41 -0
- data/spec/flipper/adapters/pstore_spec.rb +0 -2
- data/spec/flipper/adapters/read_only_spec.rb +32 -18
- data/spec/flipper/adapters/strict_spec.rb +64 -0
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +39 -1
- data/spec/flipper/adapters/sync/interval_synchronizer_spec.rb +4 -5
- data/spec/flipper/adapters/sync/synchronizer_spec.rb +87 -1
- data/spec/flipper/adapters/sync_spec.rb +17 -6
- data/spec/flipper/cli_spec.rb +217 -0
- data/spec/flipper/cloud/configuration_spec.rb +257 -0
- data/spec/flipper/cloud/dsl_spec.rb +90 -0
- data/spec/flipper/cloud/message_verifier_spec.rb +104 -0
- data/spec/flipper/cloud/middleware_spec.rb +307 -0
- data/spec/flipper/cloud/migrate_spec.rb +160 -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 +37 -3
- data/spec/flipper/dsl_spec.rb +67 -80
- 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/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 +29 -0
- data/spec/flipper/feature_check_context_spec.rb +18 -20
- data/spec/flipper/feature_spec.rb +461 -48
- data/spec/flipper/gate_spec.rb +0 -2
- data/spec/flipper/gate_values_spec.rb +2 -34
- data/spec/flipper/gates/actor_spec.rb +0 -2
- data/spec/flipper/gates/boolean_spec.rb +1 -3
- data/spec/flipper/gates/expression_spec.rb +190 -0
- data/spec/flipper/gates/group_spec.rb +2 -5
- data/spec/flipper/gates/percentage_of_actors_spec.rb +61 -7
- data/spec/flipper/gates/percentage_of_time_spec.rb +2 -4
- data/spec/flipper/identifier_spec.rb +12 -0
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +24 -7
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +26 -3
- data/spec/flipper/instrumenters/memory_spec.rb +18 -1
- data/spec/flipper/instrumenters/noop_spec.rb +14 -8
- data/spec/flipper/middleware/memoizer_spec.rb +199 -62
- data/spec/flipper/middleware/setup_env_spec.rb +23 -5
- data/spec/flipper/model/active_record_spec.rb +72 -0
- data/spec/flipper/poller_spec.rb +390 -0
- data/spec/flipper/registry_spec.rb +0 -1
- 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 -7
- data/spec/flipper/types/actor_spec.rb +63 -47
- data/spec/flipper/types/boolean_spec.rb +0 -1
- data/spec/flipper/types/group_spec.rb +24 -3
- data/spec/flipper/types/percentage_of_actors_spec.rb +0 -1
- data/spec/flipper/types/percentage_of_time_spec.rb +0 -1
- data/spec/flipper/types/percentage_spec.rb +0 -1
- data/spec/{integration_spec.rb → flipper_integration_spec.rb} +301 -59
- data/spec/flipper_spec.rb +123 -29
- data/spec/{helper.rb → spec_helper.rb} +23 -21
- data/spec/support/actor_names.yml +1 -0
- data/spec/support/descriptions.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 +53 -6
- data/test/adapters/actor_limit_test.rb +20 -0
- data/test/test_helper.rb +2 -1
- 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 +31 -0
- data/test_rails/system/test_help_test.rb +52 -0
- metadata +200 -82
- data/.rubocop.yml +0 -54
- data/.rubocop_todo.yml +0 -199
- data/docs/Adapters.md +0 -124
- data/docs/Caveats.md +0 -4
- data/docs/Gates.md +0 -167
- data/docs/Instrumentation.md +0 -27
- data/docs/Optimization.md +0 -114
- data/docs/api/README.md +0 -849
- data/docs/http/README.md +0 -35
- data/docs/read-only/README.md +0 -21
- data/examples/example_setup.rb +0 -8
- data/test/helper.rb +0 -11
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
require 'helper'
|
|
2
1
|
require 'flipper/feature'
|
|
3
2
|
|
|
4
3
|
RSpec.describe Flipper do
|
|
@@ -8,25 +7,32 @@ RSpec.describe Flipper do
|
|
|
8
7
|
let(:admin_group) { flipper.group(:admins) }
|
|
9
8
|
let(:dev_group) { flipper.group(:devs) }
|
|
10
9
|
|
|
11
|
-
let(:
|
|
12
|
-
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}
|
|
13
12
|
end
|
|
14
|
-
let(:
|
|
15
|
-
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}
|
|
16
15
|
end
|
|
17
16
|
|
|
18
|
-
let(:
|
|
19
|
-
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}
|
|
20
19
|
end
|
|
21
|
-
let(:
|
|
22
|
-
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"}
|
|
23
29
|
end
|
|
24
30
|
|
|
25
31
|
let(:pitt) { Flipper::Actor.new(1) }
|
|
26
32
|
let(:clooney) { Flipper::Actor.new(10) }
|
|
27
33
|
|
|
28
|
-
let(:five_percent_of_actors) {
|
|
29
|
-
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) }
|
|
30
36
|
|
|
31
37
|
before do
|
|
32
38
|
described_class.register(:admins, &:admin?)
|
|
@@ -61,20 +67,22 @@ RSpec.describe Flipper do
|
|
|
61
67
|
expect(@result).to eq(true)
|
|
62
68
|
end
|
|
63
69
|
|
|
64
|
-
it 'enables feature for non flipper
|
|
65
|
-
expect(feature.enabled?(
|
|
70
|
+
it 'enables feature for non flipper actor in group' do
|
|
71
|
+
expect(feature.enabled?(admin_actor)).to eq(true)
|
|
66
72
|
end
|
|
67
73
|
|
|
68
|
-
it 'does not enable feature for non flipper
|
|
69
|
-
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)
|
|
70
76
|
end
|
|
71
77
|
|
|
72
78
|
it 'enables feature for flipper actor in group' do
|
|
73
|
-
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)
|
|
74
81
|
end
|
|
75
82
|
|
|
76
83
|
it 'does not enable for flipper actor not in group' do
|
|
77
|
-
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)
|
|
78
86
|
end
|
|
79
87
|
|
|
80
88
|
it 'does not enable feature for all' do
|
|
@@ -119,8 +127,8 @@ RSpec.describe Flipper do
|
|
|
119
127
|
|
|
120
128
|
it 'enables feature for actor within percentage' do
|
|
121
129
|
enabled = (1..100).select do |i|
|
|
122
|
-
|
|
123
|
-
feature.enabled?(
|
|
130
|
+
actor = Flipper::Actor.new(i)
|
|
131
|
+
feature.enabled?(actor)
|
|
124
132
|
end.size
|
|
125
133
|
|
|
126
134
|
expect(enabled).to be_within(2).of(5)
|
|
@@ -142,8 +150,8 @@ RSpec.describe Flipper do
|
|
|
142
150
|
|
|
143
151
|
it 'enables feature for actor within percentage' do
|
|
144
152
|
enabled = (1..100).select do |i|
|
|
145
|
-
|
|
146
|
-
feature.enabled?(
|
|
153
|
+
actor = Flipper::Actor.new(i)
|
|
154
|
+
feature.enabled?(actor)
|
|
147
155
|
end.size
|
|
148
156
|
|
|
149
157
|
expect(enabled).to be_within(2).of(5)
|
|
@@ -181,10 +189,10 @@ RSpec.describe Flipper do
|
|
|
181
189
|
|
|
182
190
|
context 'with argument that has no gate' do
|
|
183
191
|
it 'raises error' do
|
|
184
|
-
|
|
192
|
+
actor = Object.new
|
|
185
193
|
expect do
|
|
186
|
-
feature.enable(
|
|
187
|
-
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}")
|
|
188
196
|
end
|
|
189
197
|
end
|
|
190
198
|
end
|
|
@@ -216,13 +224,13 @@ RSpec.describe Flipper do
|
|
|
216
224
|
end
|
|
217
225
|
|
|
218
226
|
it 'disables actor in group' do
|
|
219
|
-
expect(feature.enabled?(
|
|
227
|
+
expect(feature.enabled?(admin_actor)).to eq(false)
|
|
220
228
|
end
|
|
221
229
|
|
|
222
230
|
it 'disables actor in percentage of actors' do
|
|
223
231
|
enabled = (1..100).select do |i|
|
|
224
|
-
|
|
225
|
-
feature.enabled?(
|
|
232
|
+
actor = Flipper::Actor.new(i)
|
|
233
|
+
feature.enabled?(actor)
|
|
226
234
|
end.size
|
|
227
235
|
|
|
228
236
|
expect(enabled).to be(0)
|
|
@@ -248,20 +256,22 @@ RSpec.describe Flipper do
|
|
|
248
256
|
expect(@result).to eq(true)
|
|
249
257
|
end
|
|
250
258
|
|
|
251
|
-
it 'disables the feature for non flipper
|
|
252
|
-
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)
|
|
253
261
|
end
|
|
254
262
|
|
|
255
|
-
it 'does not disable feature for non flipper
|
|
256
|
-
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)
|
|
257
265
|
end
|
|
258
266
|
|
|
259
267
|
it 'disables feature for flipper actor in group' do
|
|
260
|
-
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)
|
|
261
270
|
end
|
|
262
271
|
|
|
263
272
|
it 'does not disable feature for flipper actor in other groups' do
|
|
264
|
-
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)
|
|
265
275
|
end
|
|
266
276
|
|
|
267
277
|
it 'adds feature to set of features' do
|
|
@@ -295,7 +305,7 @@ RSpec.describe Flipper do
|
|
|
295
305
|
|
|
296
306
|
context 'with a percentage of actors' do
|
|
297
307
|
before do
|
|
298
|
-
@result = feature.disable(
|
|
308
|
+
@result = feature.disable(Flipper::Types::PercentageOfActors.new(0))
|
|
299
309
|
end
|
|
300
310
|
|
|
301
311
|
it 'returns true' do
|
|
@@ -304,8 +314,8 @@ RSpec.describe Flipper do
|
|
|
304
314
|
|
|
305
315
|
it 'disables feature' do
|
|
306
316
|
enabled = (1..100).select do |i|
|
|
307
|
-
|
|
308
|
-
feature.enabled?(
|
|
317
|
+
actor = Flipper::Actor.new(i)
|
|
318
|
+
feature.enabled?(actor)
|
|
309
319
|
end.size
|
|
310
320
|
|
|
311
321
|
expect(enabled).to be(0)
|
|
@@ -319,7 +329,7 @@ RSpec.describe Flipper do
|
|
|
319
329
|
context 'with a percentage of time' do
|
|
320
330
|
before do
|
|
321
331
|
@gate = feature.gate(:percentage_of_time)
|
|
322
|
-
@result = feature.disable(
|
|
332
|
+
@result = feature.disable(Flipper::Types::PercentageOfTime.new(0))
|
|
323
333
|
end
|
|
324
334
|
|
|
325
335
|
it 'returns true' do
|
|
@@ -343,10 +353,10 @@ RSpec.describe Flipper do
|
|
|
343
353
|
|
|
344
354
|
context 'with argument that has no gate' do
|
|
345
355
|
it 'raises error' do
|
|
346
|
-
|
|
356
|
+
actor = Object.new
|
|
347
357
|
expect do
|
|
348
|
-
feature.disable(
|
|
349
|
-
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}")
|
|
350
360
|
end
|
|
351
361
|
end
|
|
352
362
|
end
|
|
@@ -374,23 +384,29 @@ RSpec.describe Flipper do
|
|
|
374
384
|
end
|
|
375
385
|
|
|
376
386
|
it 'returns true' do
|
|
377
|
-
expect(feature.enabled?(
|
|
378
|
-
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)
|
|
379
389
|
end
|
|
380
390
|
|
|
381
391
|
it 'returns true for truthy block values' do
|
|
382
|
-
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)
|
|
383
398
|
end
|
|
384
399
|
end
|
|
385
400
|
|
|
386
401
|
context 'for actor in disabled group' do
|
|
387
402
|
it 'returns false' do
|
|
388
|
-
expect(feature.enabled?(
|
|
389
|
-
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)
|
|
390
405
|
end
|
|
391
406
|
|
|
392
407
|
it 'returns false for falsey block values' do
|
|
393
|
-
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)
|
|
394
410
|
end
|
|
395
411
|
end
|
|
396
412
|
|
|
@@ -409,6 +425,10 @@ RSpec.describe Flipper do
|
|
|
409
425
|
expect(feature.enabled?(clooney)).to eq(false)
|
|
410
426
|
end
|
|
411
427
|
|
|
428
|
+
it 'returns false if all actors are disabled' do
|
|
429
|
+
expect(feature.enabled?(clooney, pitt)).to be(false)
|
|
430
|
+
end
|
|
431
|
+
|
|
412
432
|
it 'returns true if boolean enabled' do
|
|
413
433
|
feature.enable
|
|
414
434
|
expect(feature.enabled?(clooney)).to eq(true)
|
|
@@ -429,7 +449,7 @@ RSpec.describe Flipper do
|
|
|
429
449
|
expect(feature.enabled?).to eq(true)
|
|
430
450
|
expect(feature.enabled?(nil)).to eq(true)
|
|
431
451
|
expect(feature.enabled?(pitt)).to eq(true)
|
|
432
|
-
expect(feature.enabled?(
|
|
452
|
+
expect(feature.enabled?(admin_actor)).to eq(true)
|
|
433
453
|
end
|
|
434
454
|
end
|
|
435
455
|
|
|
@@ -447,7 +467,7 @@ RSpec.describe Flipper do
|
|
|
447
467
|
expect(feature.enabled?).to eq(true)
|
|
448
468
|
expect(feature.enabled?(nil)).to eq(true)
|
|
449
469
|
expect(feature.enabled?(pitt)).to eq(true)
|
|
450
|
-
expect(feature.enabled?(
|
|
470
|
+
expect(feature.enabled?(admin_actor)).to eq(true)
|
|
451
471
|
end
|
|
452
472
|
end
|
|
453
473
|
|
|
@@ -464,7 +484,7 @@ RSpec.describe Flipper do
|
|
|
464
484
|
expect(feature.enabled?).to eq(false)
|
|
465
485
|
expect(feature.enabled?(nil)).to eq(false)
|
|
466
486
|
expect(feature.enabled?(pitt)).to eq(false)
|
|
467
|
-
expect(feature.enabled?(
|
|
487
|
+
expect(feature.enabled?(admin_actor)).to eq(false)
|
|
468
488
|
end
|
|
469
489
|
|
|
470
490
|
it 'returns true if boolean enabled' do
|
|
@@ -472,7 +492,7 @@ RSpec.describe Flipper do
|
|
|
472
492
|
expect(feature.enabled?).to eq(true)
|
|
473
493
|
expect(feature.enabled?(nil)).to eq(true)
|
|
474
494
|
expect(feature.enabled?(pitt)).to eq(true)
|
|
475
|
-
expect(feature.enabled?(
|
|
495
|
+
expect(feature.enabled?(admin_actor)).to eq(true)
|
|
476
496
|
end
|
|
477
497
|
end
|
|
478
498
|
|
|
@@ -489,7 +509,7 @@ RSpec.describe Flipper do
|
|
|
489
509
|
expect(feature.enabled?).to eq(false)
|
|
490
510
|
expect(feature.enabled?(nil)).to eq(false)
|
|
491
511
|
expect(feature.enabled?(pitt)).to eq(false)
|
|
492
|
-
expect(feature.enabled?(
|
|
512
|
+
expect(feature.enabled?(admin_actor)).to eq(false)
|
|
493
513
|
end
|
|
494
514
|
|
|
495
515
|
it 'returns true if boolean enabled' do
|
|
@@ -497,27 +517,31 @@ RSpec.describe Flipper do
|
|
|
497
517
|
expect(feature.enabled?).to eq(true)
|
|
498
518
|
expect(feature.enabled?(nil)).to eq(true)
|
|
499
519
|
expect(feature.enabled?(pitt)).to eq(true)
|
|
500
|
-
expect(feature.enabled?(
|
|
520
|
+
expect(feature.enabled?(admin_actor)).to eq(true)
|
|
501
521
|
end
|
|
502
522
|
end
|
|
503
523
|
|
|
504
|
-
context 'for a non flipper
|
|
524
|
+
context 'for a non flipper actor' do
|
|
505
525
|
before do
|
|
506
526
|
feature.enable admin_group
|
|
507
527
|
end
|
|
508
528
|
|
|
509
529
|
it 'returns true if in enabled group' do
|
|
510
|
-
expect(feature.enabled?(
|
|
530
|
+
expect(feature.enabled?(admin_actor)).to eq(true)
|
|
511
531
|
end
|
|
512
532
|
|
|
513
533
|
it 'returns false if not in enabled group' do
|
|
514
|
-
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)
|
|
515
539
|
end
|
|
516
540
|
|
|
517
541
|
it 'returns true if boolean enabled' do
|
|
518
542
|
feature.enable
|
|
519
|
-
expect(feature.enabled?(
|
|
520
|
-
expect(feature.enabled?(
|
|
543
|
+
expect(feature.enabled?(admin_actor)).to eq(true)
|
|
544
|
+
expect(feature.enabled?(dev_actor)).to eq(true)
|
|
521
545
|
end
|
|
522
546
|
end
|
|
523
547
|
end
|
|
@@ -531,11 +555,229 @@ RSpec.describe Flipper do
|
|
|
531
555
|
end
|
|
532
556
|
|
|
533
557
|
it 'enables feature for object in enabled group' do
|
|
534
|
-
expect(feature.enabled?(
|
|
558
|
+
expect(feature.enabled?(admin_actor)).to eq(true)
|
|
535
559
|
end
|
|
536
560
|
|
|
537
561
|
it 'does not enable feature for object in not enabled group' do
|
|
538
|
-
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 FeatureEnabled" do
|
|
604
|
+
before do
|
|
605
|
+
@original_instance = Flipper.instance
|
|
606
|
+
Flipper.instance = flipper
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
after do
|
|
610
|
+
Flipper.instance = @original_instance
|
|
611
|
+
Thread.current[:flipper_evaluating_features] = nil
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
it "returns true when referenced feature is enabled" do
|
|
615
|
+
flipper.enable(:search_beta)
|
|
616
|
+
feature.enable Flipper.feature_enabled(:search_beta)
|
|
617
|
+
|
|
618
|
+
expect(feature.enabled?).to be(true)
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
it "returns false when referenced feature is disabled" do
|
|
622
|
+
feature.enable Flipper.feature_enabled(:search_beta)
|
|
623
|
+
|
|
624
|
+
expect(feature.enabled?).to be(false)
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
it "passes actor through to referenced feature" do
|
|
628
|
+
flipper.enable_actor(:search_beta, pitt)
|
|
629
|
+
feature.enable Flipper.feature_enabled(:search_beta)
|
|
630
|
+
|
|
631
|
+
expect(feature.enabled?(pitt)).to be(true)
|
|
632
|
+
expect(feature.enabled?(clooney)).to be(false)
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
it "returns false on circular dependency" do
|
|
636
|
+
feature.enable Flipper.feature_enabled(:other)
|
|
637
|
+
flipper[:other].enable Flipper.feature_enabled(:search)
|
|
638
|
+
|
|
639
|
+
expect(feature.enabled?).to be(false)
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
it "returns false on self-reference" do
|
|
643
|
+
feature.enable Flipper.feature_enabled(:search)
|
|
644
|
+
|
|
645
|
+
expect(feature.enabled?).to be(false)
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
it "cleans up thread-local state after evaluation" do
|
|
649
|
+
flipper.enable(:search_beta)
|
|
650
|
+
feature.enable Flipper.feature_enabled(:search_beta)
|
|
651
|
+
|
|
652
|
+
feature.enabled?
|
|
653
|
+
evaluating = Thread.current[:flipper_evaluating_features]
|
|
654
|
+
expect(evaluating.nil? || evaluating.empty?).to be(true)
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
it "cleans up thread-local state after self-reference" do
|
|
658
|
+
feature.enable Flipper.feature_enabled(:search)
|
|
659
|
+
|
|
660
|
+
feature.enabled?
|
|
661
|
+
evaluating = Thread.current[:flipper_evaluating_features]
|
|
662
|
+
expect(evaluating.nil? || evaluating.empty?).to be(true)
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
it "cleans up thread-local state after circular dependency" do
|
|
666
|
+
feature.enable Flipper.feature_enabled(:other)
|
|
667
|
+
flipper[:other].enable Flipper.feature_enabled(:search)
|
|
668
|
+
|
|
669
|
+
feature.enabled?
|
|
670
|
+
evaluating = Thread.current[:flipper_evaluating_features]
|
|
671
|
+
expect(evaluating.nil? || evaluating.empty?).to be(true)
|
|
672
|
+
end
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
context "for FeatureDisabled" do
|
|
676
|
+
before do
|
|
677
|
+
@original_instance = Flipper.instance
|
|
678
|
+
Flipper.instance = flipper
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
after do
|
|
682
|
+
Flipper.instance = @original_instance
|
|
683
|
+
Thread.current[:flipper_evaluating_features] = nil
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
it "returns true when referenced feature is disabled" do
|
|
687
|
+
feature.enable Flipper.feature_disabled(:old_checkout)
|
|
688
|
+
|
|
689
|
+
expect(feature.enabled?).to be(true)
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
it "returns false when referenced feature is enabled" do
|
|
693
|
+
flipper.enable(:old_checkout)
|
|
694
|
+
feature.enable Flipper.feature_disabled(:old_checkout)
|
|
695
|
+
|
|
696
|
+
expect(feature.enabled?).to be(false)
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
context "for FeatureEnabled composed with other expressions" do
|
|
701
|
+
before do
|
|
702
|
+
@original_instance = Flipper.instance
|
|
703
|
+
Flipper.instance = flipper
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
after do
|
|
707
|
+
Flipper.instance = @original_instance
|
|
708
|
+
Thread.current[:flipper_evaluating_features] = nil
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
it "works with Any" do
|
|
712
|
+
feature.enable Flipper.any(
|
|
713
|
+
Flipper.feature_enabled(:beta_program),
|
|
714
|
+
Flipper.property(:plan).eq("basic")
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
expect(feature.enabled?(basic_plan_actor)).to be(true)
|
|
718
|
+
expect(feature.enabled?(premium_plan_actor)).to be(false)
|
|
719
|
+
|
|
720
|
+
flipper.enable(:beta_program)
|
|
721
|
+
expect(feature.enabled?(premium_plan_actor)).to be(true)
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
it "works with All" do
|
|
725
|
+
flipper.enable(:basic_search)
|
|
726
|
+
feature.enable Flipper.all(
|
|
727
|
+
Flipper.feature_enabled(:basic_search),
|
|
728
|
+
Flipper.property(:plan).eq("basic")
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
expect(feature.enabled?(basic_plan_actor)).to be(true)
|
|
732
|
+
expect(feature.enabled?(premium_plan_actor)).to be(false)
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
context "for All" do
|
|
737
|
+
it "works" do
|
|
738
|
+
true_actor = Flipper::Actor.new("User;1", {
|
|
739
|
+
"plan" => "basic",
|
|
740
|
+
"age" => 21,
|
|
741
|
+
})
|
|
742
|
+
false_actor = Flipper::Actor.new("User;1", {
|
|
743
|
+
"plan" => "basic",
|
|
744
|
+
"age" => 20,
|
|
745
|
+
})
|
|
746
|
+
expression = Flipper.all(
|
|
747
|
+
Flipper.property(:plan).eq("basic"),
|
|
748
|
+
Flipper.property(:age).eq(21)
|
|
749
|
+
)
|
|
750
|
+
feature.enable expression
|
|
751
|
+
|
|
752
|
+
expect(feature.enabled?(true_actor)).to be(true)
|
|
753
|
+
expect(feature.enabled?(false_actor)).to be(false)
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
it "works when nested" do
|
|
757
|
+
admin_actor = Flipper::Actor.new("User;1", {
|
|
758
|
+
"admin" => true,
|
|
759
|
+
})
|
|
760
|
+
true_actor = Flipper::Actor.new("User;1", {
|
|
761
|
+
"plan" => "basic",
|
|
762
|
+
"age" => 21,
|
|
763
|
+
})
|
|
764
|
+
false_actor = Flipper::Actor.new("User;1", {
|
|
765
|
+
"plan" => "basic",
|
|
766
|
+
"age" => 20,
|
|
767
|
+
})
|
|
768
|
+
expression = Flipper.any(
|
|
769
|
+
Flipper.property(:admin).eq(true),
|
|
770
|
+
Flipper.all(
|
|
771
|
+
Flipper.property(:plan).eq("basic"),
|
|
772
|
+
Flipper.property(:age).eq(21)
|
|
773
|
+
)
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
feature.enable expression
|
|
777
|
+
|
|
778
|
+
expect(feature.enabled?(admin_actor)).to be(true)
|
|
779
|
+
expect(feature.enabled?(true_actor)).to be(true)
|
|
780
|
+
expect(feature.enabled?(false_actor)).to be(false)
|
|
539
781
|
end
|
|
540
782
|
end
|
|
541
783
|
end
|