flipper 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/ci.yml +7 -3
- data/.github/workflows/examples.yml +27 -5
- data/Changelog.md +42 -0
- data/Gemfile +4 -4
- data/README.md +13 -11
- data/benchmark/typecast_ips.rb +8 -0
- data/docs/images/flipper_cloud.png +0 -0
- data/examples/cloud/backoff_policy.rb +13 -0
- data/examples/cloud/cloud_setup.rb +16 -0
- data/examples/cloud/forked.rb +7 -2
- data/examples/cloud/threaded.rb +15 -18
- data/examples/expressions.rb +213 -0
- data/examples/strict.rb +18 -0
- data/flipper.gemspec +1 -2
- data/lib/flipper/actor.rb +6 -3
- data/lib/flipper/adapter.rb +10 -0
- data/lib/flipper/adapter_builder.rb +44 -0
- data/lib/flipper/adapters/dual_write.rb +1 -3
- data/lib/flipper/adapters/failover.rb +0 -4
- data/lib/flipper/adapters/failsafe.rb +0 -4
- data/lib/flipper/adapters/http/client.rb +26 -7
- data/lib/flipper/adapters/http/error.rb +1 -1
- data/lib/flipper/adapters/http.rb +18 -13
- data/lib/flipper/adapters/instrumented.rb +0 -4
- data/lib/flipper/adapters/memoizable.rb +14 -19
- data/lib/flipper/adapters/memory.rb +4 -6
- data/lib/flipper/adapters/operation_logger.rb +0 -4
- data/lib/flipper/adapters/poll.rb +1 -3
- data/lib/flipper/adapters/pstore.rb +17 -11
- data/lib/flipper/adapters/read_only.rb +4 -4
- data/lib/flipper/adapters/strict.rb +47 -0
- data/lib/flipper/adapters/sync/feature_synchronizer.rb +10 -1
- data/lib/flipper/adapters/sync.rb +0 -4
- data/lib/flipper/cloud/configuration.rb +121 -52
- data/lib/flipper/cloud/telemetry/backoff_policy.rb +93 -0
- data/lib/flipper/cloud/telemetry/instrumenter.rb +26 -0
- data/lib/flipper/cloud/telemetry/metric.rb +39 -0
- data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
- data/lib/flipper/cloud/telemetry/submitter.rb +98 -0
- data/lib/flipper/cloud/telemetry.rb +183 -0
- data/lib/flipper/configuration.rb +25 -4
- data/lib/flipper/dsl.rb +51 -0
- data/lib/flipper/engine.rb +28 -3
- data/lib/flipper/exporters/json/export.rb +1 -1
- data/lib/flipper/exporters/json/v1.rb +1 -1
- data/lib/flipper/expression/builder.rb +73 -0
- data/lib/flipper/expression/constant.rb +25 -0
- data/lib/flipper/expression.rb +71 -0
- data/lib/flipper/expressions/all.rb +11 -0
- data/lib/flipper/expressions/any.rb +9 -0
- data/lib/flipper/expressions/boolean.rb +9 -0
- data/lib/flipper/expressions/comparable.rb +13 -0
- data/lib/flipper/expressions/duration.rb +28 -0
- data/lib/flipper/expressions/equal.rb +9 -0
- data/lib/flipper/expressions/greater_than.rb +9 -0
- data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/less_than.rb +9 -0
- data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/not_equal.rb +9 -0
- data/lib/flipper/expressions/now.rb +9 -0
- data/lib/flipper/expressions/number.rb +9 -0
- data/lib/flipper/expressions/percentage.rb +9 -0
- data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
- data/lib/flipper/expressions/property.rb +9 -0
- data/lib/flipper/expressions/random.rb +9 -0
- data/lib/flipper/expressions/string.rb +9 -0
- data/lib/flipper/expressions/time.rb +9 -0
- data/lib/flipper/feature.rb +55 -0
- data/lib/flipper/gate.rb +1 -0
- data/lib/flipper/gate_values.rb +5 -2
- data/lib/flipper/gates/expression.rb +75 -0
- data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
- data/lib/flipper/middleware/memoizer.rb +29 -13
- data/lib/flipper/poller.rb +1 -1
- data/lib/flipper/serializers/gzip.rb +24 -0
- data/lib/flipper/serializers/json.rb +19 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +29 -11
- data/lib/flipper/test/shared_adapter_test.rb +24 -5
- data/lib/flipper/typecast.rb +34 -6
- data/lib/flipper/types/percentage.rb +1 -1
- data/lib/flipper/version.rb +1 -1
- data/lib/flipper.rb +38 -1
- data/spec/flipper/adapter_builder_spec.rb +73 -0
- data/spec/flipper/adapter_spec.rb +1 -0
- data/spec/flipper/adapters/http_spec.rb +39 -5
- data/spec/flipper/adapters/memoizable_spec.rb +15 -15
- data/spec/flipper/adapters/read_only_spec.rb +26 -11
- data/spec/flipper/adapters/strict_spec.rb +62 -0
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
- data/spec/flipper/cloud/configuration_spec.rb +6 -23
- data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +108 -0
- data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
- data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
- data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
- data/spec/flipper/cloud/telemetry_spec.rb +156 -0
- data/spec/flipper/cloud_spec.rb +12 -12
- data/spec/flipper/configuration_spec.rb +17 -0
- data/spec/flipper/dsl_spec.rb +39 -0
- data/spec/flipper/engine_spec.rb +108 -7
- data/spec/flipper/exporters/json/v1_spec.rb +3 -3
- 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_spec.rb +360 -1
- data/spec/flipper/gate_values_spec.rb +2 -2
- data/spec/flipper/gates/expression_spec.rb +108 -0
- data/spec/flipper/identifier_spec.rb +4 -5
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +15 -1
- data/spec/flipper/middleware/memoizer_spec.rb +67 -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 +43 -7
- data/spec/flipper/types/actor_spec.rb +18 -1
- data/spec/flipper_integration_spec.rb +102 -4
- data/spec/flipper_spec.rb +89 -1
- data/spec/spec_helper.rb +5 -0
- data/spec/support/actor_names.yml +1 -0
- data/spec/support/fake_backoff_policy.rb +15 -0
- data/spec/support/spec_helpers.rb +11 -3
- metadata +104 -18
- data/lib/flipper/cloud/instrumenter.rb +0 -48
@@ -0,0 +1,24 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::Equal do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns true when equal" do
|
4
|
+
expect(described_class.call("basic", "basic")).to be(true)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns false when not equal" do
|
8
|
+
expect(described_class.call("basic", "plus")).to be(false)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns false when value evaluates to nil" do
|
12
|
+
expect(described_class.call(nil, 1)).to be(false)
|
13
|
+
expect(described_class.call(1, nil)).to be(false)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "raises ArgumentError with no arguments" do
|
17
|
+
expect { described_class.call }.to raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "raises ArgumentError with one argument" do
|
21
|
+
expect { described_class.call(10) }.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::GreaterThanOrEqualTo do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns true when equal" do
|
4
|
+
expect(described_class.call(2, 2)).to be(true)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns true when greater" do
|
8
|
+
expect(described_class.call(2, 1)).to be(true)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns false when less" do
|
12
|
+
expect(described_class.call(1, 2)).to be(false)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns false when value evaluates to nil" do
|
16
|
+
expect(described_class.call(nil, 1)).to be(false)
|
17
|
+
expect(described_class.call(1, nil)).to be(false)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "raises ArgumentError with no arguments" do
|
21
|
+
expect { described_class.call }.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises ArgumentError with one argument" do
|
25
|
+
expect { described_class.call(10) }.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::GreaterThan do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns false when equal" do
|
4
|
+
expect(described_class.call(2, 2)).to be(false)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns true when greater" do
|
8
|
+
expect(described_class.call(2, 1)).to be(true)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns false when less" do
|
12
|
+
expect(described_class.call(1, 2)).to be(false)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns false when value evaluates to nil" do
|
16
|
+
expect(described_class.call(nil, 1)).to be(false)
|
17
|
+
expect(described_class.call(1, nil)).to be(false)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "raises ArgumentError with no arguments" do
|
21
|
+
expect { described_class.call }.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises ArgumentError with one argument" do
|
25
|
+
expect { described_class.call(10) }.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::LessThanOrEqualTo do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns true when equal" do
|
4
|
+
expect(described_class.call(2, 2)).to be(true)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns true when less" do
|
8
|
+
expect(described_class.call(1, 2)).to be(true)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns false when greater" do
|
12
|
+
expect(described_class.call(2, 1)).to be(false)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns false when value evaluates to nil" do
|
16
|
+
expect(described_class.call(nil, 1)).to be(false)
|
17
|
+
expect(described_class.call(1, nil)).to be(false)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "raises ArgumentError with no arguments" do
|
21
|
+
expect { described_class.call }.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises ArgumentError with one argument" do
|
25
|
+
expect { described_class.call(10) }.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::LessThan do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns false when equal" do
|
4
|
+
expect(described_class.call(2, 2)).to be(false)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns true when less" do
|
8
|
+
expect(described_class.call(1, 2)).to be(true)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns true when less with args that need evaluation" do
|
12
|
+
expect(described_class.call(1, 2)).to be(true)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns false when greater" do
|
16
|
+
expect(described_class.call(2, 1)).to be(false)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "returns false when value evaluates to nil" do
|
20
|
+
expect(described_class.call(nil, 1)).to be(false)
|
21
|
+
expect(described_class.call(1, nil)).to be(false)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises ArgumentError with no arguments" do
|
25
|
+
expect { described_class.call }.to raise_error(ArgumentError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "raises ArgumentError with one argument" do
|
29
|
+
expect { described_class.call(10) }.to raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::NotEqual do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns true when not equal" do
|
4
|
+
expect(described_class.call("basic", "plus")).to be(true)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns false when equal" do
|
8
|
+
expect(described_class.call("basic", "basic")).to be(false)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "raises ArgumentError for more arguments" do
|
12
|
+
expect { described_class.call(20, 10, 20).evaluate }.to raise_error(ArgumentError)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::Now do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns current time" do
|
4
|
+
expect(described_class.call).to be_within(2).of(Time.now.utc)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "defaults to UTC" do
|
8
|
+
expect(described_class.call.zone).to eq("UTC")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::Number do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns Integer for Integer" do
|
4
|
+
expect(described_class.call(10)).to be(10)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns Float for Float" do
|
8
|
+
expect(described_class.call(10.1)).to be(10.1)
|
9
|
+
expect(described_class.call(10.0)).to be(10.0)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "returns Integer for String" do
|
13
|
+
expect(described_class.call('10')).to be(10)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns Float for String" do
|
17
|
+
expect(described_class.call('10.0')).to be(10.0)
|
18
|
+
expect(described_class.call('10.1')).to be(10.1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::PercentageOfActors do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns true when string in percentage enabled" do
|
4
|
+
expect(described_class.call("User;1", 42)).to be(true)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns true when string in fractional percentage enabled" do
|
8
|
+
expect(described_class.call("User;1", 41.687)).to be(true)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns false when string in percentage enabled" do
|
12
|
+
expect(described_class.call("User;1", 0)).to be(false)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "changes value based on feature_name so not all actors get all features first" do
|
16
|
+
expect(described_class.call("User;1", 70, context: {feature_name: "a"})).to be(true)
|
17
|
+
expect(described_class.call("User;1", 70, context: {feature_name: "b"})).to be(false)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::Percentage do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns numeric" do
|
4
|
+
expect(described_class.call(10)).to be(10.0)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns 0 if less than 0" do
|
8
|
+
expect(described_class.call(-1)).to be(0)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns 100 if greater than 100" do
|
12
|
+
expect(described_class.call(101)).to be(100)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::Property do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns value for property key" do
|
4
|
+
context = { properties: { "flipper_id" => "User;1" } }
|
5
|
+
expect(described_class.call("flipper_id", context: context)).to eq("User;1")
|
6
|
+
end
|
7
|
+
|
8
|
+
it "returns nil if key not found in properties" do
|
9
|
+
context = { properties: { } }
|
10
|
+
expect(described_class.call("flipper_id", context: context)).to be(nil)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::Time do
|
2
|
+
let(:time) { Time.now.round }
|
3
|
+
|
4
|
+
describe "#call" do
|
5
|
+
it "returns time for #to_s format" do
|
6
|
+
expect(described_class.call(time.to_s)).to eq(time)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "returns time for #iso8601 format" do
|
10
|
+
expect(described_class.call(time.iso8601)).to eq(time)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -109,7 +109,7 @@ RSpec.describe Flipper::Feature do
|
|
109
109
|
instance.gates.each do |gate|
|
110
110
|
expect(gate).to be_a(Flipper::Gate)
|
111
111
|
end
|
112
|
-
expect(instance.gates.size).to be(
|
112
|
+
expect(instance.gates.size).to be(6)
|
113
113
|
end
|
114
114
|
end
|
115
115
|
|
@@ -693,6 +693,363 @@ RSpec.describe Flipper::Feature do
|
|
693
693
|
end
|
694
694
|
end
|
695
695
|
|
696
|
+
describe '#expression' do
|
697
|
+
it "returns nil if feature has no expression" do
|
698
|
+
expect(subject.expression).to be(nil)
|
699
|
+
end
|
700
|
+
|
701
|
+
it "returns expression if feature has expression" do
|
702
|
+
expression = Flipper.property(:plan).eq("basic")
|
703
|
+
subject.enable_expression expression
|
704
|
+
expect(subject.expression).to eq(expression)
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
describe '#enable_expression/disable_expression' do
|
709
|
+
context "with expression instance" do
|
710
|
+
it "updates gate values to equal expression or clears expression" do
|
711
|
+
expression = Flipper.property(:plan).eq("basic")
|
712
|
+
other_expression = Flipper.property(:age).gte(21)
|
713
|
+
expect(subject.gate_values.expression).to be(nil)
|
714
|
+
subject.enable_expression(expression)
|
715
|
+
expect(subject.gate_values.expression).to eq(expression.value)
|
716
|
+
subject.disable_expression
|
717
|
+
expect(subject.gate_values.expression).to be(nil)
|
718
|
+
end
|
719
|
+
end
|
720
|
+
|
721
|
+
context "with Hash" do
|
722
|
+
it "updates gate values to equal expression or clears expression" do
|
723
|
+
expression = Flipper.property(:plan).eq("basic")
|
724
|
+
other_expression = Flipper.property(:age).gte(21)
|
725
|
+
expect(subject.gate_values.expression).to be(nil)
|
726
|
+
subject.enable_expression(expression.value)
|
727
|
+
expect(subject.gate_values.expression).to eq(expression.value)
|
728
|
+
subject.disable_expression
|
729
|
+
expect(subject.gate_values.expression).to be(nil)
|
730
|
+
end
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
describe "#add_expression" do
|
735
|
+
context "when nothing enabled" do
|
736
|
+
context "with Expression instance" do
|
737
|
+
it "sets expression to Expression" do
|
738
|
+
expression = Flipper.property(:plan).eq("basic")
|
739
|
+
subject.add_expression(expression)
|
740
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
741
|
+
expect(subject.expression).to eq(expression)
|
742
|
+
end
|
743
|
+
end
|
744
|
+
|
745
|
+
context "with Any instance" do
|
746
|
+
it "sets expression to Any" do
|
747
|
+
expression = Flipper.any(Flipper.property(:plan).eq("basic"))
|
748
|
+
subject.add_expression(expression)
|
749
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
750
|
+
expect(subject.expression).to eq(expression)
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
context "with All instance" do
|
755
|
+
it "sets expression to All" do
|
756
|
+
expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
757
|
+
subject.add_expression(expression)
|
758
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
759
|
+
expect(subject.expression).to eq(expression)
|
760
|
+
end
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
764
|
+
context "when Expression enabled" do
|
765
|
+
let(:expression) { Flipper.property(:plan).eq("basic") }
|
766
|
+
|
767
|
+
before do
|
768
|
+
subject.enable_expression expression
|
769
|
+
end
|
770
|
+
|
771
|
+
context "with Expression instance" do
|
772
|
+
it "changes expression to Any and adds new Expression" do
|
773
|
+
new_expression = Flipper.property(:age).gte(21)
|
774
|
+
subject.add_expression(new_expression)
|
775
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
776
|
+
expect(subject.expression.args).to include(expression)
|
777
|
+
expect(subject.expression.args).to include(new_expression)
|
778
|
+
end
|
779
|
+
end
|
780
|
+
|
781
|
+
context "with Any instance" do
|
782
|
+
it "changes expression to Any and adds new Any" do
|
783
|
+
new_expression = Flipper.any(Flipper.property(:age).eq(21))
|
784
|
+
subject.add_expression new_expression
|
785
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
786
|
+
expect(subject.expression.args).to include(expression)
|
787
|
+
expect(subject.expression.args).to include(new_expression)
|
788
|
+
end
|
789
|
+
end
|
790
|
+
|
791
|
+
context "with All instance" do
|
792
|
+
it "changes expression to Any and adds new All" do
|
793
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
794
|
+
subject.add_expression new_expression
|
795
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
796
|
+
expect(subject.expression.args).to include(expression)
|
797
|
+
expect(subject.expression.args).to include(new_expression)
|
798
|
+
end
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
context "when Any enabled" do
|
803
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
804
|
+
let(:expression) { Flipper.any(condition) }
|
805
|
+
|
806
|
+
before do
|
807
|
+
subject.enable_expression expression
|
808
|
+
end
|
809
|
+
|
810
|
+
context "with Expression instance" do
|
811
|
+
it "adds Expression to Any" do
|
812
|
+
new_expression = Flipper.property(:age).gte(21)
|
813
|
+
subject.add_expression(new_expression)
|
814
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
815
|
+
expect(subject.expression.args).to include(condition)
|
816
|
+
expect(subject.expression.args).to include(new_expression)
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
context "with Any instance" do
|
821
|
+
it "adds Any to Any" do
|
822
|
+
new_expression = Flipper.any(Flipper.property(:age).gte(21))
|
823
|
+
subject.add_expression(new_expression)
|
824
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
825
|
+
expect(subject.expression.args).to include(condition)
|
826
|
+
expect(subject.expression.args).to include(new_expression)
|
827
|
+
end
|
828
|
+
end
|
829
|
+
|
830
|
+
context "with All instance" do
|
831
|
+
it "adds All to Any" do
|
832
|
+
new_expression = Flipper.all(Flipper.property(:age).gte(21))
|
833
|
+
subject.add_expression(new_expression)
|
834
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
835
|
+
expect(subject.expression.args).to include(condition)
|
836
|
+
expect(subject.expression.args).to include(new_expression)
|
837
|
+
end
|
838
|
+
end
|
839
|
+
end
|
840
|
+
|
841
|
+
context "when All enabled" do
|
842
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
843
|
+
let(:expression) { Flipper.all(condition) }
|
844
|
+
|
845
|
+
before do
|
846
|
+
subject.enable_expression expression
|
847
|
+
end
|
848
|
+
|
849
|
+
context "with Expression instance" do
|
850
|
+
it "adds Expression to All" do
|
851
|
+
new_expression = Flipper.property(:age).gte(21)
|
852
|
+
subject.add_expression(new_expression)
|
853
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
854
|
+
expect(subject.expression.args).to include(condition)
|
855
|
+
expect(subject.expression.args).to include(new_expression)
|
856
|
+
end
|
857
|
+
end
|
858
|
+
|
859
|
+
context "with Any instance" do
|
860
|
+
it "adds Any to All" do
|
861
|
+
new_expression = Flipper.any(Flipper.property(:age).gte(21))
|
862
|
+
subject.add_expression(new_expression)
|
863
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
864
|
+
expect(subject.expression.args).to include(condition)
|
865
|
+
expect(subject.expression.args).to include(new_expression)
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
context "with All instance" do
|
870
|
+
it "adds All to All" do
|
871
|
+
new_expression = Flipper.all(Flipper.property(:age).gte(21))
|
872
|
+
subject.add_expression(new_expression)
|
873
|
+
expect(subject.expression).to be_instance_of(Flipper::Expression)
|
874
|
+
expect(subject.expression.args).to include(condition)
|
875
|
+
expect(subject.expression.args).to include(new_expression)
|
876
|
+
end
|
877
|
+
end
|
878
|
+
end
|
879
|
+
end
|
880
|
+
|
881
|
+
describe '#remove_expression' do
|
882
|
+
context "when nothing enabled" do
|
883
|
+
context "with Expression instance" do
|
884
|
+
it "does nothing" do
|
885
|
+
expression = Flipper.property(:plan).eq("basic")
|
886
|
+
subject.remove_expression(expression)
|
887
|
+
expect(subject.expression).to be(nil)
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
context "with Any instance" do
|
892
|
+
it "does nothing" do
|
893
|
+
expression = Flipper.any(Flipper.property(:plan).eq("basic"))
|
894
|
+
subject.remove_expression expression
|
895
|
+
expect(subject.expression).to be(nil)
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
context "with All instance" do
|
900
|
+
it "does nothing" do
|
901
|
+
expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
902
|
+
subject.remove_expression expression
|
903
|
+
expect(subject.expression).to be(nil)
|
904
|
+
end
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
context "when Expression enabled" do
|
909
|
+
let(:expression) { Flipper.property(:plan).eq("basic") }
|
910
|
+
|
911
|
+
before do
|
912
|
+
subject.enable_expression expression
|
913
|
+
end
|
914
|
+
|
915
|
+
context "with Expression instance" do
|
916
|
+
it "changes expression to Any and removes Expression if it matches" do
|
917
|
+
new_expression = Flipper.property(:plan).eq("basic")
|
918
|
+
subject.remove_expression new_expression
|
919
|
+
expect(subject.expression).to eq(Flipper.any)
|
920
|
+
end
|
921
|
+
|
922
|
+
it "changes expression to Any if Expression doesn't match" do
|
923
|
+
new_expression = Flipper.property(:plan).eq("premium")
|
924
|
+
subject.remove_expression new_expression
|
925
|
+
expect(subject.expression).to eq(Flipper.any(expression))
|
926
|
+
end
|
927
|
+
end
|
928
|
+
|
929
|
+
context "with Any instance" do
|
930
|
+
it "changes expression to Any and does nothing" do
|
931
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("basic"))
|
932
|
+
subject.remove_expression new_expression
|
933
|
+
expect(subject.expression).to eq(Flipper.any(expression))
|
934
|
+
end
|
935
|
+
end
|
936
|
+
|
937
|
+
context "with All instance" do
|
938
|
+
it "changes expression to Any and does nothing" do
|
939
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("basic"))
|
940
|
+
subject.remove_expression new_expression
|
941
|
+
expect(subject.expression).to eq(Flipper.any(expression))
|
942
|
+
end
|
943
|
+
end
|
944
|
+
end
|
945
|
+
|
946
|
+
context "when Any enabled" do
|
947
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
948
|
+
let(:expression) { Flipper.any condition }
|
949
|
+
|
950
|
+
before do
|
951
|
+
subject.enable_expression expression
|
952
|
+
end
|
953
|
+
|
954
|
+
context "with Expression instance" do
|
955
|
+
it "removes Expression if it matches" do
|
956
|
+
subject.remove_expression condition
|
957
|
+
expect(subject.expression).to eq(Flipper.any)
|
958
|
+
end
|
959
|
+
|
960
|
+
it "does nothing if Expression does not match" do
|
961
|
+
subject.remove_expression Flipper.property(:plan).eq("premium")
|
962
|
+
expect(subject.expression).to eq(expression)
|
963
|
+
end
|
964
|
+
end
|
965
|
+
|
966
|
+
context "with Any instance" do
|
967
|
+
it "removes Any if it matches" do
|
968
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("premium"))
|
969
|
+
subject.add_expression new_expression
|
970
|
+
expect(subject.expression.args.size).to be(2)
|
971
|
+
subject.remove_expression new_expression
|
972
|
+
expect(subject.expression).to eq(expression)
|
973
|
+
end
|
974
|
+
|
975
|
+
it "does nothing if Any does not match" do
|
976
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("premium"))
|
977
|
+
subject.remove_expression new_expression
|
978
|
+
expect(subject.expression).to eq(expression)
|
979
|
+
end
|
980
|
+
end
|
981
|
+
|
982
|
+
context "with All instance" do
|
983
|
+
it "removes All if it matches" do
|
984
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
985
|
+
subject.add_expression new_expression
|
986
|
+
expect(subject.expression.args.size).to be(2)
|
987
|
+
subject.remove_expression new_expression
|
988
|
+
expect(subject.expression).to eq(expression)
|
989
|
+
end
|
990
|
+
|
991
|
+
it "does nothing if All does not match" do
|
992
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
993
|
+
subject.remove_expression new_expression
|
994
|
+
expect(subject.expression).to eq(expression)
|
995
|
+
end
|
996
|
+
end
|
997
|
+
end
|
998
|
+
|
999
|
+
context "when All enabled" do
|
1000
|
+
let(:condition) { Flipper.property(:plan).eq("basic") }
|
1001
|
+
let(:expression) { Flipper.all condition }
|
1002
|
+
|
1003
|
+
before do
|
1004
|
+
subject.enable_expression expression
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
context "with Expression instance" do
|
1008
|
+
it "removes Expression if it matches" do
|
1009
|
+
subject.remove_expression condition
|
1010
|
+
expect(subject.expression).to eq(Flipper.all)
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
it "does nothing if Expression does not match" do
|
1014
|
+
subject.remove_expression Flipper.property(:plan).eq("premium")
|
1015
|
+
expect(subject.expression).to eq(expression)
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
context "with Any instance" do
|
1020
|
+
it "removes Any if it matches" do
|
1021
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("premium"))
|
1022
|
+
subject.add_expression new_expression
|
1023
|
+
expect(subject.expression.args.size).to be(2)
|
1024
|
+
subject.remove_expression new_expression
|
1025
|
+
expect(subject.expression).to eq(expression)
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
it "does nothing if Any does not match" do
|
1029
|
+
new_expression = Flipper.any(Flipper.property(:plan).eq("premium"))
|
1030
|
+
subject.remove_expression new_expression
|
1031
|
+
expect(subject.expression).to eq(expression)
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
context "with All instance" do
|
1036
|
+
it "removes All if it matches" do
|
1037
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
1038
|
+
subject.add_expression new_expression
|
1039
|
+
expect(subject.expression.args.size).to be(2)
|
1040
|
+
subject.remove_expression new_expression
|
1041
|
+
expect(subject.expression).to eq(expression)
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
it "does nothing if All does not match" do
|
1045
|
+
new_expression = Flipper.all(Flipper.property(:plan).eq("premium"))
|
1046
|
+
subject.remove_expression new_expression
|
1047
|
+
expect(subject.expression).to eq(expression)
|
1048
|
+
end
|
1049
|
+
end
|
1050
|
+
end
|
1051
|
+
end
|
1052
|
+
|
696
1053
|
describe '#enable_actor/disable_actor' do
|
697
1054
|
context 'with object that responds to flipper_id' do
|
698
1055
|
it 'updates the gate values to include the actor' do
|
@@ -845,12 +1202,14 @@ RSpec.describe Flipper::Feature do
|
|
845
1202
|
:actor,
|
846
1203
|
:boolean,
|
847
1204
|
:group,
|
1205
|
+
:expression,
|
848
1206
|
])
|
849
1207
|
|
850
1208
|
expect(subject.disabled_gate_names.to_set).to eq(Set[
|
851
1209
|
:actor,
|
852
1210
|
:boolean,
|
853
1211
|
:group,
|
1212
|
+
:expression,
|
854
1213
|
])
|
855
1214
|
end
|
856
1215
|
end
|