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
@@ -0,0 +1,248 @@
|
|
1
|
+
RSpec.describe Flipper::Expression::Builder do
|
2
|
+
def build(object)
|
3
|
+
Flipper::Expression.build(object)
|
4
|
+
end
|
5
|
+
|
6
|
+
describe "#add" do
|
7
|
+
it "converts to Any and adds new expressions" do
|
8
|
+
expression = build("something")
|
9
|
+
first = Flipper.boolean(true).eq(true)
|
10
|
+
second = Flipper.boolean(false).eq(false)
|
11
|
+
new_expression = expression.add(first, second)
|
12
|
+
expect(new_expression).to eq(build({ Any: ["something", first, second] }))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#remove" do
|
17
|
+
it "converts to Any and removes any expressions that match" do
|
18
|
+
expression = build("something")
|
19
|
+
first = Flipper.boolean(true).eq(true)
|
20
|
+
second = Flipper.boolean(false).eq(false)
|
21
|
+
new_expression = expression.remove(build("something"), first, second)
|
22
|
+
expect(new_expression).to eq(build(Any: []))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "can convert to Any" do
|
27
|
+
expression = build("something")
|
28
|
+
converted = expression.any
|
29
|
+
expect(converted).to be_instance_of(Flipper::Expression)
|
30
|
+
expect(converted.function).to be(Flipper::Expressions::Any)
|
31
|
+
expect(converted.args).to eq([expression])
|
32
|
+
end
|
33
|
+
|
34
|
+
it "can convert to All" do
|
35
|
+
expression = build("something")
|
36
|
+
converted = expression.all
|
37
|
+
expect(converted).to eq(build(All: ["something"]))
|
38
|
+
end
|
39
|
+
|
40
|
+
context "Any" do
|
41
|
+
describe "#any" do
|
42
|
+
it "returns self" do
|
43
|
+
expression = build(Any: [
|
44
|
+
Flipper.boolean(true),
|
45
|
+
Flipper.string("yep").eq("yep"),
|
46
|
+
])
|
47
|
+
expect(expression.any).to be(expression)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "#add" do
|
52
|
+
it "returns new instance with expression added" do
|
53
|
+
expression = Flipper.boolean(true)
|
54
|
+
other = Flipper.string("yep").eq("yep")
|
55
|
+
|
56
|
+
result = expression.add(other)
|
57
|
+
expect(result.args).to eq([
|
58
|
+
Flipper.boolean(true),
|
59
|
+
Flipper.string("yep").eq("yep"),
|
60
|
+
])
|
61
|
+
end
|
62
|
+
|
63
|
+
it "returns new instance with many expressions added" do
|
64
|
+
expression = Flipper.boolean(true)
|
65
|
+
second = Flipper.string("yep").eq("yep")
|
66
|
+
third = Flipper.number(1).lte(20)
|
67
|
+
|
68
|
+
result = expression.add(second, third)
|
69
|
+
expect(result.args).to eq([
|
70
|
+
Flipper.boolean(true),
|
71
|
+
Flipper.string("yep").eq("yep"),
|
72
|
+
Flipper.number(1).lte(20),
|
73
|
+
])
|
74
|
+
end
|
75
|
+
|
76
|
+
it "returns new instance with array of expressions added" do
|
77
|
+
expression = Flipper.boolean(true)
|
78
|
+
second = Flipper.string("yep").eq("yep")
|
79
|
+
third = Flipper.number(1).lte(20)
|
80
|
+
|
81
|
+
result = expression.add([second, third])
|
82
|
+
expect(result.args).to eq([
|
83
|
+
Flipper.boolean(true),
|
84
|
+
Flipper.string("yep").eq("yep"),
|
85
|
+
Flipper.number(1).lte(20),
|
86
|
+
])
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#remove" do
|
91
|
+
it "returns new instance with expression removed" do
|
92
|
+
first = Flipper.boolean(true)
|
93
|
+
second = Flipper.string("yep").eq("yep")
|
94
|
+
third = Flipper.number(1).lte(20)
|
95
|
+
expression = Flipper.any([first, second, third])
|
96
|
+
|
97
|
+
result = expression.remove(second)
|
98
|
+
expect(expression.args).to eq([first, second, third])
|
99
|
+
expect(result.args).to eq([first, third])
|
100
|
+
end
|
101
|
+
|
102
|
+
it "returns new instance with many expressions removed" do
|
103
|
+
first = Flipper.boolean(true)
|
104
|
+
second = Flipper.string("yep").eq("yep")
|
105
|
+
third = Flipper.number(1).lte(20)
|
106
|
+
expression = Flipper.any([first, second, third])
|
107
|
+
|
108
|
+
result = expression.remove(second, third)
|
109
|
+
expect(expression.args).to eq([first, second, third])
|
110
|
+
expect(result.args).to eq([first])
|
111
|
+
end
|
112
|
+
|
113
|
+
it "returns new instance with array of expressions removed" do
|
114
|
+
first = Flipper.boolean(true)
|
115
|
+
second = Flipper.string("yep").eq("yep")
|
116
|
+
third = Flipper.number(1).lte(20)
|
117
|
+
expression = Flipper.any([first, second, third])
|
118
|
+
|
119
|
+
result = expression.remove([second, third])
|
120
|
+
expect(expression.args).to eq([first, second, third])
|
121
|
+
expect(result.args).to eq([first])
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
[
|
127
|
+
[2, 3, "equal", "eq", :Equal],
|
128
|
+
[2, 3, "not_equal", "neq", :NotEqual],
|
129
|
+
[2, 3, "greater_than", "gt", :GreaterThan],
|
130
|
+
[2, 3, "greater_than_or_equal_to", "gte", :GreaterThanOrEqualTo],
|
131
|
+
[2, 3, "greater_than_or_equal_to", "greater_than_or_equal", :GreaterThanOrEqualTo],
|
132
|
+
[2, 3, "less_than", "lt", :LessThan],
|
133
|
+
[2, 3, "less_than_or_equal_to", "lte", :LessThanOrEqualTo],
|
134
|
+
[2, 3, "less_than_or_equal_to", "less_than_or_equal", :LessThanOrEqualTo],
|
135
|
+
].each do |(left, right, method_name, shortcut_name, function)|
|
136
|
+
it "can convert to #{function}" do
|
137
|
+
expression = build(left)
|
138
|
+
other = build(right)
|
139
|
+
converted = expression.send(method_name, other)
|
140
|
+
expect(converted).to eq(build({ function => [ left, right] }))
|
141
|
+
end
|
142
|
+
|
143
|
+
it "can convert to #{function} using #{shortcut_name}" do
|
144
|
+
expression = build(left)
|
145
|
+
other = build(right)
|
146
|
+
converted = expression.send(shortcut_name, other)
|
147
|
+
expect(converted).to eq(build({ function => [ left, right] }))
|
148
|
+
end
|
149
|
+
|
150
|
+
it "builds args into expressions when converting to #{function}" do
|
151
|
+
expression = build(left)
|
152
|
+
other = Flipper.property(:age)
|
153
|
+
converted = expression.send(method_name, other.value)
|
154
|
+
expect(converted).to eq(build({ function => [ left, other.value] }))
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
it "can convert to PercentageOfActors" do
|
159
|
+
expression = Flipper.constant("User;1").percentage_of_actors(40)
|
160
|
+
expect(expression).to eq(build({ PercentageOfActors: [ "User;1", 40 ] }))
|
161
|
+
end
|
162
|
+
|
163
|
+
context "All" do
|
164
|
+
describe "#all" do
|
165
|
+
it "returns self" do
|
166
|
+
expression = Flipper.all([
|
167
|
+
Flipper.boolean(true),
|
168
|
+
Flipper.string("yep").eq("yep"),
|
169
|
+
])
|
170
|
+
expect(expression.all).to be(expression)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
describe "#add" do
|
175
|
+
it "returns new instance with expression added" do
|
176
|
+
expression = Flipper.all([Flipper.boolean(true)])
|
177
|
+
other = Flipper.string("yep").eq("yep")
|
178
|
+
|
179
|
+
result = expression.add(other)
|
180
|
+
expect(result.args).to eq([
|
181
|
+
Flipper.boolean(true),
|
182
|
+
Flipper.string("yep").eq("yep"),
|
183
|
+
])
|
184
|
+
end
|
185
|
+
|
186
|
+
it "returns new instance with many expressions added" do
|
187
|
+
expression = Flipper.all([Flipper.boolean(true)])
|
188
|
+
second = Flipper.string("yep").eq("yep")
|
189
|
+
third = Flipper.number(1).lte(20)
|
190
|
+
|
191
|
+
result = expression.add(second, third)
|
192
|
+
expect(result.args).to eq([
|
193
|
+
Flipper.boolean(true),
|
194
|
+
Flipper.string("yep").eq("yep"),
|
195
|
+
Flipper.number(1).lte(20),
|
196
|
+
])
|
197
|
+
end
|
198
|
+
|
199
|
+
it "returns new instance with array of expressions added" do
|
200
|
+
expression = Flipper.all([Flipper.boolean(true)])
|
201
|
+
second = Flipper.string("yep").eq("yep")
|
202
|
+
third = Flipper.number(1).lte(20)
|
203
|
+
|
204
|
+
result = expression.add([second, third])
|
205
|
+
expect(result.args).to eq([
|
206
|
+
Flipper.boolean(true),
|
207
|
+
Flipper.string("yep").eq("yep"),
|
208
|
+
Flipper.number(1).lte(20),
|
209
|
+
])
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "#remove" do
|
214
|
+
it "returns new instance with expression removed" do
|
215
|
+
first = Flipper.boolean(true)
|
216
|
+
second = Flipper.string("yep").eq("yep")
|
217
|
+
third = Flipper.number(1).lte(20)
|
218
|
+
expression = Flipper.all([first, second, third])
|
219
|
+
|
220
|
+
result = expression.remove(second)
|
221
|
+
expect(expression.args).to eq([first, second, third])
|
222
|
+
expect(result.args).to eq([first, third])
|
223
|
+
end
|
224
|
+
|
225
|
+
it "returns new instance with many expressions removed" do
|
226
|
+
first = Flipper.boolean(true)
|
227
|
+
second = Flipper.string("yep").eq("yep")
|
228
|
+
third = Flipper.number(1).lte(20)
|
229
|
+
expression = Flipper.all([first, second, third])
|
230
|
+
|
231
|
+
result = expression.remove(second, third)
|
232
|
+
expect(expression.args).to eq([first, second, third])
|
233
|
+
expect(result.args).to eq([first])
|
234
|
+
end
|
235
|
+
|
236
|
+
it "returns new instance with array of expressions removed" do
|
237
|
+
first = Flipper.boolean(true)
|
238
|
+
second = Flipper.string("yep").eq("yep")
|
239
|
+
third = Flipper.number(1).lte(20)
|
240
|
+
expression = Flipper.all([first, second, third])
|
241
|
+
|
242
|
+
result = expression.remove([second, third])
|
243
|
+
expect(expression.args).to eq([first, second, third])
|
244
|
+
expect(result.args).to eq([first])
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'flipper/expression'
|
2
|
+
|
3
|
+
RSpec.describe Flipper::Expression do
|
4
|
+
describe "#build" do
|
5
|
+
it "can build Equal" do
|
6
|
+
expression = described_class.build({
|
7
|
+
"Equal" => [
|
8
|
+
"basic",
|
9
|
+
"basic",
|
10
|
+
]
|
11
|
+
})
|
12
|
+
|
13
|
+
expect(expression).to be_instance_of(Flipper::Expression)
|
14
|
+
expect(expression.function).to be(Flipper::Expressions::Equal)
|
15
|
+
expect(expression.args).to eq([
|
16
|
+
Flipper.constant("basic"),
|
17
|
+
Flipper.constant("basic"),
|
18
|
+
])
|
19
|
+
end
|
20
|
+
|
21
|
+
it "can build GreaterThanOrEqualTo" do
|
22
|
+
expression = described_class.build({
|
23
|
+
"GreaterThanOrEqualTo" => [
|
24
|
+
2,
|
25
|
+
1,
|
26
|
+
]
|
27
|
+
})
|
28
|
+
|
29
|
+
expect(expression).to be_instance_of(Flipper::Expression)
|
30
|
+
expect(expression.function).to be(Flipper::Expressions::GreaterThanOrEqualTo)
|
31
|
+
expect(expression.args).to eq([
|
32
|
+
Flipper.constant(2),
|
33
|
+
Flipper.constant(1),
|
34
|
+
])
|
35
|
+
end
|
36
|
+
|
37
|
+
it "can build GreaterThan" do
|
38
|
+
expression = described_class.build({
|
39
|
+
"GreaterThan" => [
|
40
|
+
2,
|
41
|
+
1,
|
42
|
+
]
|
43
|
+
})
|
44
|
+
|
45
|
+
expect(expression).to be_instance_of(Flipper::Expression)
|
46
|
+
expect(expression.function).to be(Flipper::Expressions::GreaterThan)
|
47
|
+
expect(expression.args).to eq([
|
48
|
+
Flipper.constant(2),
|
49
|
+
Flipper.constant(1),
|
50
|
+
])
|
51
|
+
end
|
52
|
+
|
53
|
+
it "can build LessThanOrEqualTo" do
|
54
|
+
expression = described_class.build({
|
55
|
+
"LessThanOrEqualTo" => [
|
56
|
+
2,
|
57
|
+
1,
|
58
|
+
]
|
59
|
+
})
|
60
|
+
|
61
|
+
expect(expression).to be_instance_of(Flipper::Expression)
|
62
|
+
expect(expression.function).to be(Flipper::Expressions::LessThanOrEqualTo)
|
63
|
+
expect(expression.args).to eq([
|
64
|
+
Flipper.constant(2),
|
65
|
+
Flipper.constant(1),
|
66
|
+
])
|
67
|
+
end
|
68
|
+
|
69
|
+
it "can build LessThan" do
|
70
|
+
expression = described_class.build({
|
71
|
+
"LessThan" => [2, 1]
|
72
|
+
})
|
73
|
+
|
74
|
+
expect(expression).to be_instance_of(Flipper::Expression)
|
75
|
+
expect(expression.function).to be(Flipper::Expressions::LessThan)
|
76
|
+
expect(expression.args).to eq([
|
77
|
+
Flipper.constant(2),
|
78
|
+
Flipper.constant(1),
|
79
|
+
])
|
80
|
+
end
|
81
|
+
|
82
|
+
it "can build NotEqual" do
|
83
|
+
expression = described_class.build({
|
84
|
+
"NotEqual" => [
|
85
|
+
"basic",
|
86
|
+
"plus",
|
87
|
+
]
|
88
|
+
})
|
89
|
+
|
90
|
+
expect(expression).to be_instance_of(Flipper::Expression)
|
91
|
+
expect(expression.function).to be(Flipper::Expressions::NotEqual)
|
92
|
+
expect(expression.args).to eq([
|
93
|
+
Flipper.constant("basic"),
|
94
|
+
Flipper.constant("plus"),
|
95
|
+
])
|
96
|
+
end
|
97
|
+
|
98
|
+
it "can build Number" do
|
99
|
+
expression = described_class.build(1)
|
100
|
+
|
101
|
+
expect(expression).to be_instance_of(Flipper::Expression::Constant)
|
102
|
+
expect(expression.value).to eq(1)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "can build Percentage" do
|
106
|
+
expression = described_class.build({
|
107
|
+
"Percentage" => [1]
|
108
|
+
})
|
109
|
+
|
110
|
+
expect(expression).to be_instance_of(Flipper::Expression)
|
111
|
+
expect(expression.function).to be(Flipper::Expressions::Percentage)
|
112
|
+
expect(expression.args).to eq([Flipper.constant(1)])
|
113
|
+
end
|
114
|
+
|
115
|
+
it "can build PercentageOfActors" do
|
116
|
+
expression = described_class.build({
|
117
|
+
"PercentageOfActors" => [
|
118
|
+
"User;1",
|
119
|
+
40,
|
120
|
+
]
|
121
|
+
})
|
122
|
+
|
123
|
+
expect(expression).to be_instance_of(Flipper::Expression)
|
124
|
+
expect(expression.function).to be(Flipper::Expressions::PercentageOfActors)
|
125
|
+
expect(expression.args).to eq([
|
126
|
+
Flipper.constant("User;1"),
|
127
|
+
Flipper.constant(40),
|
128
|
+
])
|
129
|
+
end
|
130
|
+
|
131
|
+
it "can build String" do
|
132
|
+
expression = described_class.build("basic")
|
133
|
+
|
134
|
+
expect(expression).to be_instance_of(Flipper::Expression::Constant)
|
135
|
+
expect(expression.value).to eq("basic")
|
136
|
+
end
|
137
|
+
|
138
|
+
it "can build Property" do
|
139
|
+
expression = described_class.build({
|
140
|
+
"Property" => ["flipper_id"]
|
141
|
+
})
|
142
|
+
|
143
|
+
expect(expression).to be_instance_of(Flipper::Expression)
|
144
|
+
expect(expression.function).to be(Flipper::Expressions::Property)
|
145
|
+
expect(expression.args).to eq([Flipper.constant("flipper_id")])
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "#eql?" do
|
150
|
+
it "returns true for same class and args" do
|
151
|
+
expression = described_class.build("foo")
|
152
|
+
other = described_class.build("foo")
|
153
|
+
expect(expression.eql?(other)).to be(true)
|
154
|
+
end
|
155
|
+
|
156
|
+
it "returns false for different class" do
|
157
|
+
expression = described_class.build("foo")
|
158
|
+
other = Object.new
|
159
|
+
expect(expression.eql?(other)).to be(false)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "returns false for different args" do
|
163
|
+
expression = described_class.build("foo")
|
164
|
+
other = described_class.build("bar")
|
165
|
+
expect(expression.eql?(other)).to be(false)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "#==" do
|
170
|
+
it "returns true for same class and args" do
|
171
|
+
expression = described_class.build("foo")
|
172
|
+
other = described_class.build("foo")
|
173
|
+
expect(expression == other).to be(true)
|
174
|
+
end
|
175
|
+
|
176
|
+
it "returns false for different class" do
|
177
|
+
expression = described_class.build("foo")
|
178
|
+
other = Object.new
|
179
|
+
expect(expression == other).to be(false)
|
180
|
+
end
|
181
|
+
|
182
|
+
it "returns false for different args" do
|
183
|
+
expression = described_class.build("foo")
|
184
|
+
other = described_class.build("bar")
|
185
|
+
expect(expression == other).to be(false)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::All do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns true if all args evaluate as true" do
|
4
|
+
expect(described_class.call(true, true)).to be(true)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns false if any args evaluate as false" do
|
8
|
+
expect(described_class.call(false, true)).to be(false)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns true with empty args" do
|
12
|
+
expect(described_class.call).to be(true)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::Any do
|
2
|
+
describe "#call" do
|
3
|
+
it "returns true if any args evaluate as true" do
|
4
|
+
expect(described_class.call(true, false)).to be(true)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "returns false if all args evaluate as false" do
|
8
|
+
expect(described_class.call(false, false)).to be(false)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns false with empty args" do
|
12
|
+
expect(described_class.call).to be(false)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::Boolean do
|
2
|
+
describe "#call" do
|
3
|
+
[true, 'true', 1, '1'].each do |value|
|
4
|
+
it "returns a true for #{value.inspect}" do
|
5
|
+
expect(described_class.call(value)).to be(true)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
[false, 'false', 0, '0', nil].each do |value|
|
10
|
+
it "returns a true for #{value.inspect}" do
|
11
|
+
expect(described_class.call(value)).to be(false)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
RSpec.describe Flipper::Expressions::Duration do
|
2
|
+
describe "#call" do
|
3
|
+
it "raises error with invalid value" do
|
4
|
+
expect { described_class.call(false, 'minute') }.to raise_error(ArgumentError)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "raises error with invalid unit" do
|
8
|
+
expect { described_class.call(4, 'score') }.to raise_error(ArgumentError)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'defaults unit to seconds' do
|
12
|
+
expect(described_class.call(10)).to eq(10)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "evaluates seconds" do
|
16
|
+
expect(described_class.call(10, 'seconds')).to eq(10)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "evaluates minutes" do
|
20
|
+
expect(described_class.call(2, 'minutes')).to eq(120)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "evaluates hours" do
|
24
|
+
expect(described_class.call(2, 'hours')).to eq(7200)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "evaluates days" do
|
28
|
+
expect(described_class.call(2, 'days')).to eq(172_800)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "evaluates weeks" do
|
32
|
+
expect(described_class.call(2, 'weeks')).to eq(1_209_600)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "evaluates months" do
|
36
|
+
expect(described_class.call(2, 'months')).to eq(5_259_492)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "evaluates years" do
|
40
|
+
expect(described_class.call(2, 'years')).to eq(63_113_904)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -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
|