flipper 0.24.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +45 -14
- data/.github/workflows/examples.yml +39 -16
- data/Changelog.md +2 -443
- data/Gemfile +19 -11
- data/README.md +31 -27
- data/Rakefile +6 -4
- 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/instrumentation.rb +1 -0
- data/examples/instrumentation_last_accessed_at.rb +1 -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 +10 -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 +72 -0
- data/lib/flipper/adapters/http/client.rb +44 -20
- data/lib/flipper/adapters/http/error.rb +1 -1
- data/lib/flipper/adapters/http.rb +31 -16
- 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 -0
- data/lib/flipper/adapters/poll.rb +37 -0
- 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 +263 -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 +93 -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 +98 -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 -20
- data/lib/flipper/export.rb +26 -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 +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 +87 -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 +34 -6
- data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
- data/lib/flipper/instrumentation/subscriber.rb +8 -1
- data/lib/flipper/metadata.rb +7 -1
- data/lib/flipper/middleware/memoizer.rb +28 -22
- 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 +63 -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/failsafe_spec.rb +58 -0
- data/spec/flipper/adapters/http/client_spec.rb +61 -0
- data/spec/flipper/adapters/http_spec.rb +137 -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/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 +164 -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 +181 -0
- data/spec/flipper/configuration_spec.rb +17 -0
- data/spec/flipper/dsl_spec.rb +54 -73
- data/spec/flipper/engine_spec.rb +373 -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 +436 -33
- 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 +23 -6
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +25 -1
- data/spec/flipper/middleware/memoizer_spec.rb +74 -24
- data/spec/flipper/model/active_record_spec.rb +61 -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 +93 -29
- data/spec/spec_helper.rb +8 -14
- 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 +23 -8
- data/test/adapters/actor_limit_test.rb +20 -0
- data/test_rails/generators/flipper/setup_generator_test.rb +64 -0
- data/test_rails/generators/flipper/update_generator_test.rb +96 -0
- data/test_rails/helper.rb +19 -2
- data/test_rails/system/test_help_test.rb +51 -0
- metadata +223 -19
- data/lib/flipper/railtie.rb +0 -47
- data/spec/flipper/railtie_spec.rb +0 -73
@@ -7,6 +7,7 @@ module Flipper
|
|
7
7
|
@feature = @flipper[:stats]
|
8
8
|
@boolean_gate = @feature.gate(:boolean)
|
9
9
|
@group_gate = @feature.gate(:group)
|
10
|
+
@expression_gate = @feature.gate(:expression)
|
10
11
|
@actor_gate = @feature.gate(:actor)
|
11
12
|
@actors_gate = @feature.gate(:percentage_of_actors)
|
12
13
|
@time_gate = @feature.gate(:percentage_of_time)
|
@@ -34,28 +35,55 @@ module Flipper
|
|
34
35
|
assert_includes @adapter.class.ancestors, Flipper::Adapter
|
35
36
|
end
|
36
37
|
|
38
|
+
def test_knows_how_to_get_adapter_from_source
|
39
|
+
adapter = Flipper::Adapters::Memory.new
|
40
|
+
flipper = Flipper.new(adapter)
|
41
|
+
|
42
|
+
assert_includes adapter.class.from(adapter).class.ancestors, Flipper::Adapter
|
43
|
+
assert_includes adapter.class.from(flipper).class.ancestors, Flipper::Adapter
|
44
|
+
end
|
45
|
+
|
37
46
|
def test_returns_correct_default_values_for_gates_if_none_are_enabled
|
47
|
+
assert_equal @adapter.class.default_config, @adapter.get(@feature)
|
38
48
|
assert_equal @adapter.default_config, @adapter.get(@feature)
|
39
49
|
end
|
40
50
|
|
41
51
|
def test_can_enable_disable_and_get_value_for_boolean_gate
|
42
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate,
|
52
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, Flipper::Types::Boolean.new)
|
43
53
|
assert_equal 'true', @adapter.get(@feature)[:boolean]
|
44
|
-
assert_equal true, @adapter.disable(@feature, @boolean_gate,
|
54
|
+
assert_equal true, @adapter.disable(@feature, @boolean_gate, Flipper::Types::Boolean.new(false))
|
45
55
|
assert_nil @adapter.get(@feature)[:boolean]
|
46
56
|
end
|
47
57
|
|
48
58
|
def test_fully_disables_all_enabled_things_when_boolean_gate_disabled
|
49
59
|
actor22 = Flipper::Actor.new('22')
|
50
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate,
|
60
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, Flipper::Types::Boolean.new)
|
51
61
|
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
52
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate,
|
53
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate,
|
54
|
-
assert_equal true, @adapter.enable(@feature, @time_gate,
|
55
|
-
assert_equal true, @adapter.disable(@feature, @boolean_gate,
|
62
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor22))
|
63
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(25))
|
64
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, Flipper::Types::PercentageOfTime.new(45))
|
65
|
+
assert_equal true, @adapter.disable(@feature, @boolean_gate, Flipper::Types::Boolean.new(false))
|
56
66
|
assert_equal @adapter.default_config, @adapter.get(@feature)
|
57
67
|
end
|
58
68
|
|
69
|
+
def test_can_enable_disable_and_get_value_for_expression_gate
|
70
|
+
basic_expression = Flipper.property(:plan).eq("basic")
|
71
|
+
age_expression = Flipper.property(:age).gte(21)
|
72
|
+
any_expression = Flipper.any(basic_expression, age_expression)
|
73
|
+
|
74
|
+
assert_equal true, @adapter.enable(@feature, @expression_gate, any_expression)
|
75
|
+
result = @adapter.get(@feature)
|
76
|
+
assert_equal any_expression.value, result[:expression]
|
77
|
+
|
78
|
+
assert_equal true, @adapter.enable(@feature, @expression_gate, basic_expression)
|
79
|
+
result = @adapter.get(@feature)
|
80
|
+
assert_equal basic_expression.value, result[:expression]
|
81
|
+
|
82
|
+
assert_equal true, @adapter.disable(@feature, @expression_gate, basic_expression)
|
83
|
+
result = @adapter.get(@feature)
|
84
|
+
assert_nil result[:expression]
|
85
|
+
end
|
86
|
+
|
59
87
|
def test_can_enable_disable_get_value_for_group_gate
|
60
88
|
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
61
89
|
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:early_access))
|
@@ -76,34 +104,34 @@ module Flipper
|
|
76
104
|
actor22 = Flipper::Actor.new('22')
|
77
105
|
actor_asdf = Flipper::Actor.new('asdf')
|
78
106
|
|
79
|
-
assert_equal true, @
|
80
|
-
assert_equal true, @
|
107
|
+
assert_equal true, @feature.enable(actor22)
|
108
|
+
assert_equal true, @feature.enable(actor_asdf)
|
81
109
|
|
82
|
-
|
83
|
-
|
110
|
+
assert @feature.enabled?(actor22)
|
111
|
+
assert @feature.enabled?(actor_asdf)
|
84
112
|
|
85
|
-
|
86
|
-
|
87
|
-
|
113
|
+
assert_equal true, @feature.disable(actor22)
|
114
|
+
refute @feature.enabled?(actor22)
|
115
|
+
assert @feature.enabled?(actor_asdf)
|
88
116
|
|
89
|
-
assert_equal true, @
|
90
|
-
|
91
|
-
|
117
|
+
assert_equal true, @feature.disable(actor_asdf)
|
118
|
+
refute @feature.enabled?(actor22)
|
119
|
+
refute @feature.enabled?(actor_asdf)
|
92
120
|
end
|
93
121
|
|
94
122
|
def test_can_enable_disable_get_value_for_percentage_of_actors_gate
|
95
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate,
|
123
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(15))
|
96
124
|
result = @adapter.get(@feature)
|
97
125
|
assert_equal '15', result[:percentage_of_actors]
|
98
126
|
|
99
|
-
assert_equal true, @adapter.disable(@feature, @actors_gate,
|
127
|
+
assert_equal true, @adapter.disable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(0))
|
100
128
|
result = @adapter.get(@feature)
|
101
129
|
assert_equal '0', result[:percentage_of_actors]
|
102
130
|
end
|
103
131
|
|
104
132
|
def test_can_enable_percentage_of_actors_gate_many_times_and_consistently_return_values
|
105
133
|
(1..100).each do |percentage|
|
106
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate,
|
134
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(percentage))
|
107
135
|
result = @adapter.get(@feature)
|
108
136
|
assert_equal percentage.to_s, result[:percentage_of_actors]
|
109
137
|
end
|
@@ -111,25 +139,25 @@ module Flipper
|
|
111
139
|
|
112
140
|
def test_can_disable_percentage_of_actors_gate_many_times_and_consistently_return_values
|
113
141
|
(1..100).each do |percentage|
|
114
|
-
assert_equal true, @adapter.disable(@feature, @actors_gate,
|
142
|
+
assert_equal true, @adapter.disable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(percentage))
|
115
143
|
result = @adapter.get(@feature)
|
116
144
|
assert_equal percentage.to_s, result[:percentage_of_actors]
|
117
145
|
end
|
118
146
|
end
|
119
147
|
|
120
148
|
def test_can_enable_disable_and_get_value_for_percentage_of_time_gate
|
121
|
-
assert_equal true, @adapter.enable(@feature, @time_gate,
|
149
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, Flipper::Types::PercentageOfTime.new(10))
|
122
150
|
result = @adapter.get(@feature)
|
123
151
|
assert_equal '10', result[:percentage_of_time]
|
124
152
|
|
125
|
-
assert_equal true, @adapter.disable(@feature, @time_gate,
|
153
|
+
assert_equal true, @adapter.disable(@feature, @time_gate, Flipper::Types::PercentageOfTime.new(0))
|
126
154
|
result = @adapter.get(@feature)
|
127
155
|
assert_equal '0', result[:percentage_of_time]
|
128
156
|
end
|
129
157
|
|
130
158
|
def test_can_enable_percentage_of_time_gate_many_times_and_consistently_return_values
|
131
159
|
(1..100).each do |percentage|
|
132
|
-
assert_equal true, @adapter.enable(@feature, @time_gate,
|
160
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, Flipper::Types::PercentageOfTime.new(percentage))
|
133
161
|
result = @adapter.get(@feature)
|
134
162
|
assert_equal percentage.to_s, result[:percentage_of_time]
|
135
163
|
end
|
@@ -137,23 +165,23 @@ module Flipper
|
|
137
165
|
|
138
166
|
def test_can_disable_percentage_of_time_gate_many_times_and_consistently_return_values
|
139
167
|
(1..100).each do |percentage|
|
140
|
-
assert_equal true, @adapter.disable(@feature, @time_gate,
|
168
|
+
assert_equal true, @adapter.disable(@feature, @time_gate, Flipper::Types::PercentageOfTime.new(percentage))
|
141
169
|
result = @adapter.get(@feature)
|
142
170
|
assert_equal percentage.to_s, result[:percentage_of_time]
|
143
171
|
end
|
144
172
|
end
|
145
173
|
|
146
174
|
def test_converts_boolean_value_to_a_string
|
147
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate,
|
175
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, Flipper::Types::Boolean.new)
|
148
176
|
result = @adapter.get(@feature)
|
149
177
|
assert_equal 'true', result[:boolean]
|
150
178
|
end
|
151
179
|
|
152
180
|
def test_converts_the_actor_value_to_a_string
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
181
|
+
actor = Flipper::Actor.new(22)
|
182
|
+
refute @feature.enabled?(actor)
|
183
|
+
@feature.enable_actor actor
|
184
|
+
assert @feature.enabled?(actor)
|
157
185
|
end
|
158
186
|
|
159
187
|
def test_converts_group_value_to_a_string
|
@@ -163,13 +191,13 @@ module Flipper
|
|
163
191
|
end
|
164
192
|
|
165
193
|
def test_converts_percentage_of_time_integer_value_to_a_string
|
166
|
-
assert_equal true, @adapter.enable(@feature, @time_gate,
|
194
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, Flipper::Types::PercentageOfTime.new(10))
|
167
195
|
result = @adapter.get(@feature)
|
168
196
|
assert_equal '10', result[:percentage_of_time]
|
169
197
|
end
|
170
198
|
|
171
199
|
def test_converts_percentage_of_actors_integer_value_to_a_string
|
172
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate,
|
200
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(10))
|
173
201
|
result = @adapter.get(@feature)
|
174
202
|
assert_equal '10', result[:percentage_of_actors]
|
175
203
|
end
|
@@ -192,11 +220,11 @@ module Flipper
|
|
192
220
|
|
193
221
|
def test_clears_all_the_gate_values_for_the_feature_on_remove
|
194
222
|
actor22 = Flipper::Actor.new('22')
|
195
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate,
|
223
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, Flipper::Types::Boolean.new)
|
196
224
|
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
197
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate,
|
198
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate,
|
199
|
-
assert_equal true, @adapter.enable(@feature, @time_gate,
|
225
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor22))
|
226
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(25))
|
227
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, Flipper::Types::PercentageOfTime.new(45))
|
200
228
|
|
201
229
|
assert_equal true, @adapter.remove(@feature)
|
202
230
|
|
@@ -208,11 +236,11 @@ module Flipper
|
|
208
236
|
@adapter.add(@feature)
|
209
237
|
assert_includes @adapter.features, @feature.key
|
210
238
|
|
211
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate,
|
239
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, Flipper::Types::Boolean.new)
|
212
240
|
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
213
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate,
|
214
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate,
|
215
|
-
assert_equal true, @adapter.enable(@feature, @time_gate,
|
241
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor22))
|
242
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(25))
|
243
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, Flipper::Types::PercentageOfTime.new(45))
|
216
244
|
|
217
245
|
assert_equal true, @adapter.clear(@feature)
|
218
246
|
assert_includes @adapter.features, @feature.key
|
@@ -225,7 +253,7 @@ module Flipper
|
|
225
253
|
|
226
254
|
def test_can_get_multiple_features
|
227
255
|
assert @adapter.add(@flipper[:stats])
|
228
|
-
assert @adapter.enable(@flipper[:stats], @boolean_gate,
|
256
|
+
assert @adapter.enable(@flipper[:stats], @boolean_gate, Flipper::Types::Boolean.new)
|
229
257
|
assert @adapter.add(@flipper[:search])
|
230
258
|
|
231
259
|
result = @adapter.get_multi([@flipper[:stats], @flipper[:search], @flipper[:other]])
|
@@ -241,16 +269,16 @@ module Flipper
|
|
241
269
|
|
242
270
|
def test_can_get_all_features
|
243
271
|
assert @adapter.add(@flipper[:stats])
|
244
|
-
assert @adapter.enable(@flipper[:stats], @boolean_gate,
|
272
|
+
assert @adapter.enable(@flipper[:stats], @boolean_gate, Flipper::Types::Boolean.new)
|
245
273
|
assert @adapter.add(@flipper[:search])
|
274
|
+
@flipper.enable :analytics, Flipper.property(:plan).eq("pro")
|
246
275
|
|
247
276
|
result = @adapter.get_all
|
248
|
-
assert_instance_of Hash, result
|
249
277
|
|
250
|
-
|
251
|
-
|
252
|
-
assert_equal @adapter.default_config
|
253
|
-
assert_equal @adapter.default_config,
|
278
|
+
assert_instance_of Hash, result
|
279
|
+
assert_equal @adapter.default_config.merge(boolean: 'true'), result["stats"]
|
280
|
+
assert_equal @adapter.default_config, result["search"]
|
281
|
+
assert_equal @adapter.default_config.merge(expression: {"Equal"=>[{"Property"=>["plan"]}, "pro"]}), result["analytics"]
|
254
282
|
end
|
255
283
|
|
256
284
|
def test_includes_explicitly_disabled_features_when_getting_all_features
|
@@ -264,9 +292,9 @@ module Flipper
|
|
264
292
|
|
265
293
|
def test_can_double_enable_an_actor_without_error
|
266
294
|
actor = Flipper::Actor.new('Flipper::Actor;22')
|
267
|
-
assert_equal true, @
|
268
|
-
assert_equal true, @
|
269
|
-
|
295
|
+
assert_equal true, @feature.enable(actor)
|
296
|
+
assert_equal true, @feature.enable(actor)
|
297
|
+
assert @feature.enabled?(actor)
|
270
298
|
end
|
271
299
|
|
272
300
|
def test_can_double_enable_a_group_without_error
|
@@ -276,13 +304,13 @@ module Flipper
|
|
276
304
|
end
|
277
305
|
|
278
306
|
def test_can_double_enable_percentage_without_error
|
279
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate,
|
280
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate,
|
307
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(25))
|
308
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(25))
|
281
309
|
end
|
282
310
|
|
283
311
|
def test_can_double_enable_without_error
|
284
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate,
|
285
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate,
|
312
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, Flipper::Types::Boolean.new)
|
313
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, Flipper::Types::Boolean.new)
|
286
314
|
end
|
287
315
|
|
288
316
|
def test_can_get_all_features_when_there_are_none
|
@@ -293,13 +321,28 @@ module Flipper
|
|
293
321
|
|
294
322
|
def test_clears_other_gate_values_on_enable
|
295
323
|
actor = Flipper::Actor.new('Flipper::Actor;22')
|
296
|
-
assert_equal true, @adapter.enable(@feature, @actors_gate,
|
297
|
-
assert_equal true, @adapter.enable(@feature, @time_gate,
|
324
|
+
assert_equal true, @adapter.enable(@feature, @actors_gate, Flipper::Types::PercentageOfActors.new(25))
|
325
|
+
assert_equal true, @adapter.enable(@feature, @time_gate, Flipper::Types::PercentageOfTime.new(25))
|
298
326
|
assert_equal true, @adapter.enable(@feature, @group_gate, @flipper.group(:admins))
|
299
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate,
|
300
|
-
assert_equal true, @adapter.enable(@feature, @boolean_gate,
|
327
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor))
|
328
|
+
assert_equal true, @adapter.enable(@feature, @boolean_gate, Flipper::Types::Boolean.new(true))
|
301
329
|
assert_equal @adapter.default_config.merge(boolean: "true"), @adapter.get(@feature)
|
302
330
|
end
|
331
|
+
|
332
|
+
def test_can_import_and_export
|
333
|
+
adapter = Flipper::Adapters::Memory.new
|
334
|
+
source_flipper = Flipper.new(adapter)
|
335
|
+
source_flipper.enable(:stats)
|
336
|
+
export = adapter.export
|
337
|
+
|
338
|
+
# some adapters cannot import so if they return false lets assert it
|
339
|
+
# didn't happen
|
340
|
+
if @adapter.import(export)
|
341
|
+
assert @flipper[:stats].enabled?
|
342
|
+
else
|
343
|
+
refute @flipper[:stats].enabled?
|
344
|
+
end
|
345
|
+
end
|
303
346
|
end
|
304
347
|
end
|
305
348
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Flipper
|
2
|
+
module TestHelp
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def flipper_configure
|
6
|
+
# Use a shared Memory adapter for all tests. This is instantiated outside of the
|
7
|
+
# `configure` block so the same instance is returned in new threads.
|
8
|
+
adapter = Flipper::Adapters::Memory.new
|
9
|
+
|
10
|
+
Flipper.configure do |config|
|
11
|
+
config.adapter { adapter }
|
12
|
+
config.default { Flipper.new(config.adapter) }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def flipper_reset
|
17
|
+
# Remove all features
|
18
|
+
Flipper.features.each(&:remove) rescue nil
|
19
|
+
|
20
|
+
# Reset previous DSL instance
|
21
|
+
Flipper.instance = nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if defined?(RSpec) && RSpec.respond_to?(:configure)
|
27
|
+
RSpec.configure do |config|
|
28
|
+
config.include Flipper::TestHelp
|
29
|
+
config.before(:suite) { Flipper::TestHelp.flipper_configure }
|
30
|
+
config.before(:each) { flipper_reset }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
if defined?(ActiveSupport)
|
34
|
+
ActiveSupport.on_load(:active_support_test_case) do
|
35
|
+
Flipper::TestHelp.flipper_configure
|
36
|
+
|
37
|
+
ActiveSupport::TestCase.class_eval do
|
38
|
+
include Flipper::TestHelp
|
39
|
+
|
40
|
+
setup :flipper_reset
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/flipper/typecast.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'set'
|
2
|
+
require "flipper/serializers/json"
|
3
|
+
require "flipper/serializers/gzip"
|
2
4
|
|
3
5
|
module Flipper
|
4
|
-
|
5
|
-
|
6
|
+
class Typecast
|
7
|
+
TRUTH_MAP = {
|
6
8
|
true => true,
|
7
9
|
1 => true,
|
8
10
|
'true' => true,
|
@@ -13,7 +15,7 @@ module Flipper
|
|
13
15
|
#
|
14
16
|
# Returns true or false.
|
15
17
|
def self.to_boolean(value)
|
16
|
-
!!
|
18
|
+
!!TRUTH_MAP[value]
|
17
19
|
end
|
18
20
|
|
19
21
|
# Internal: Convert value to an integer.
|
@@ -21,11 +23,9 @@ module Flipper
|
|
21
23
|
# Returns an Integer representation of the value.
|
22
24
|
# Raises ArgumentError if conversion is not possible.
|
23
25
|
def self.to_integer(value)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
raise ArgumentError, "#{value.inspect} cannot be converted to an integer"
|
28
|
-
end
|
26
|
+
value.to_i
|
27
|
+
rescue NoMethodError
|
28
|
+
raise ArgumentError, "#{value.inspect} cannot be converted to an integer"
|
29
29
|
end
|
30
30
|
|
31
31
|
# Internal: Convert value to a float.
|
@@ -33,24 +33,30 @@ module Flipper
|
|
33
33
|
# Returns a Float representation of the value.
|
34
34
|
# Raises ArgumentError if conversion is not possible.
|
35
35
|
def self.to_float(value)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
raise ArgumentError, "#{value.inspect} cannot be converted to a float"
|
40
|
-
end
|
36
|
+
value.to_f
|
37
|
+
rescue NoMethodError
|
38
|
+
raise ArgumentError, "#{value.inspect} cannot be converted to a float"
|
41
39
|
end
|
42
40
|
|
43
|
-
# Internal: Convert value to a
|
41
|
+
# Internal: Convert value to a number.
|
44
42
|
#
|
45
43
|
# Returns a Integer or Float representation of the value.
|
46
44
|
# Raises ArgumentError if conversion is not possible.
|
47
|
-
def self.
|
48
|
-
|
49
|
-
|
45
|
+
def self.to_number(value)
|
46
|
+
case value
|
47
|
+
when Numeric
|
48
|
+
value
|
49
|
+
when String
|
50
|
+
value.include?('.') ? to_float(value) : to_integer(value)
|
51
|
+
when NilClass
|
52
|
+
0
|
50
53
|
else
|
51
|
-
|
54
|
+
value.to_f
|
52
55
|
end
|
56
|
+
rescue NoMethodError
|
57
|
+
raise ArgumentError, "#{value.inspect} cannot be converted to a number"
|
53
58
|
end
|
59
|
+
singleton_class.send(:alias_method, :to_percentage, :to_number)
|
54
60
|
|
55
61
|
# Internal: Convert value to a set.
|
56
62
|
#
|
@@ -66,5 +72,40 @@ module Flipper
|
|
66
72
|
raise ArgumentError, "#{value.inspect} cannot be converted to a set"
|
67
73
|
end
|
68
74
|
end
|
75
|
+
|
76
|
+
def self.features_hash(source)
|
77
|
+
normalized_source = {}
|
78
|
+
(source || {}).each do |feature_key, gates|
|
79
|
+
normalized_source[feature_key] ||= {}
|
80
|
+
gates.each do |gate_key, value|
|
81
|
+
normalized_value = case value
|
82
|
+
when Array, Set
|
83
|
+
value.to_set
|
84
|
+
when Hash
|
85
|
+
value
|
86
|
+
else
|
87
|
+
value ? value.to_s : value
|
88
|
+
end
|
89
|
+
normalized_source[feature_key][gate_key.to_sym] = normalized_value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
normalized_source
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.to_json(source)
|
96
|
+
Serializers::Json.serialize(source)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.from_json(source)
|
100
|
+
Serializers::Json.deserialize(source)
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.to_gzip(source)
|
104
|
+
Serializers::Gzip.serialize(source)
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.from_gzip(source)
|
108
|
+
Serializers::Gzip.deserialize(source)
|
109
|
+
end
|
69
110
|
end
|
70
111
|
end
|
data/lib/flipper/types/actor.rb
CHANGED
@@ -1,35 +1,35 @@
|
|
1
1
|
module Flipper
|
2
2
|
module Types
|
3
3
|
class Actor < Type
|
4
|
-
def self.wrappable?(
|
5
|
-
return false if
|
6
|
-
|
4
|
+
def self.wrappable?(actor)
|
5
|
+
return false if actor.nil?
|
6
|
+
actor.respond_to?(:flipper_id)
|
7
7
|
end
|
8
8
|
|
9
|
-
attr_reader :
|
9
|
+
attr_reader :actor
|
10
10
|
|
11
|
-
def initialize(
|
12
|
-
raise ArgumentError, '
|
11
|
+
def initialize(actor)
|
12
|
+
raise ArgumentError, 'actor cannot be nil' if actor.nil?
|
13
13
|
|
14
|
-
unless
|
15
|
-
raise ArgumentError, "#{
|
14
|
+
unless actor.respond_to?(:flipper_id)
|
15
|
+
raise ArgumentError, "#{actor.inspect} must respond to flipper_id, but does not"
|
16
16
|
end
|
17
17
|
|
18
|
-
@
|
19
|
-
@value =
|
18
|
+
@actor = actor
|
19
|
+
@value = actor.flipper_id.to_s
|
20
20
|
end
|
21
21
|
|
22
22
|
def respond_to?(*args)
|
23
|
-
super || @
|
23
|
+
super || @actor.respond_to?(*args)
|
24
24
|
end
|
25
25
|
|
26
26
|
if RUBY_VERSION >= '3.0'
|
27
27
|
def method_missing(name, *args, **kwargs, &block)
|
28
|
-
@
|
28
|
+
@actor.send name, *args, **kwargs, &block
|
29
29
|
end
|
30
30
|
else
|
31
31
|
def method_missing(name, *args, &block)
|
32
|
-
@
|
32
|
+
@actor.send name, *args, &block
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
data/lib/flipper/types/group.rb
CHANGED
@@ -16,16 +16,16 @@ module Flipper
|
|
16
16
|
@block = block
|
17
17
|
@single_argument = call_with_no_context?(@block)
|
18
18
|
else
|
19
|
-
@block = ->(
|
19
|
+
@block = ->(actor, context) { false }
|
20
20
|
@single_argument = false
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
def match?(
|
24
|
+
def match?(actor, context)
|
25
25
|
if @single_argument
|
26
|
-
@block.call(
|
26
|
+
@block.call(actor)
|
27
27
|
else
|
28
|
-
@block.call(
|
28
|
+
@block.call(actor, context)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
data/lib/flipper/version.rb
CHANGED
@@ -1,3 +1,13 @@
|
|
1
1
|
module Flipper
|
2
|
-
VERSION = '
|
2
|
+
VERSION = '1.3.0'.freeze
|
3
|
+
|
4
|
+
REQUIRED_RUBY_VERSION = '2.6'.freeze
|
5
|
+
NEXT_REQUIRED_RUBY_VERSION = '3.0'.freeze
|
6
|
+
|
7
|
+
REQUIRED_RAILS_VERSION = '5.2'.freeze
|
8
|
+
NEXT_REQUIRED_RAILS_VERSION = '6.1.0'.freeze
|
9
|
+
|
10
|
+
def self.deprecated_ruby_version?
|
11
|
+
Gem::Version.new(RUBY_VERSION) < Gem::Version.new(NEXT_REQUIRED_RUBY_VERSION)
|
12
|
+
end
|
3
13
|
end
|