flipper 0.26.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 +19 -13
- data/.github/workflows/examples.yml +32 -15
- data/Changelog.md +294 -154
- data/Gemfile +15 -10
- data/README.md +13 -11
- 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/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/flipper-cloud.gemspec +19 -0
- data/flipper.gemspec +3 -5
- 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/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 +29 -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 +16 -7
- data/lib/flipper/adapters/poll/poller.rb +2 -125
- data/lib/flipper/adapters/poll.rb +5 -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 +258 -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 +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/cloud.rb +53 -0
- data/lib/flipper/configuration.rb +25 -4
- data/lib/flipper/dsl.rb +46 -45
- data/lib/flipper/engine.rb +88 -0
- data/lib/flipper/errors.rb +3 -3
- 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 +24 -5
- data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
- data/lib/flipper/instrumentation/subscriber.rb +8 -1
- data/lib/flipper/metadata.rb +5 -1
- data/lib/flipper/middleware/memoizer.rb +30 -14
- data/lib/flipper/poller.rb +117 -0
- data/lib/flipper/serializers/gzip.rb +24 -0
- data/lib/flipper/serializers/json.rb +19 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +95 -54
- data/lib/flipper/test/shared_adapter_test.rb +91 -48
- data/lib/flipper/typecast.rb +56 -15
- 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 +1 -1
- data/lib/flipper.rb +47 -10
- data/spec/fixtures/flipper_pstore_1679087600.json +46 -0
- data/spec/flipper/adapter_builder_spec.rb +73 -0
- data/spec/flipper/adapter_spec.rb +30 -2
- data/spec/flipper/adapters/dual_write_spec.rb +2 -2
- data/spec/flipper/adapters/http_spec.rb +64 -8
- 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 +62 -0
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +27 -0
- data/spec/flipper/cloud/configuration_spec.rb +252 -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 +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 +180 -0
- data/spec/flipper/configuration_spec.rb +17 -0
- data/spec/flipper/dsl_spec.rb +54 -73
- data/spec/flipper/engine_spec.rb +291 -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 +15 -5
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +25 -1
- data/spec/flipper/middleware/memoizer_spec.rb +67 -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 +92 -28
- data/spec/spec_helper.rb +6 -13
- data/spec/support/actor_names.yml +1 -0
- data/spec/support/climate_control.rb +7 -0
- data/spec/support/fake_backoff_policy.rb +15 -0
- data/spec/support/skippable.rb +18 -0
- data/spec/support/spec_helpers.rb +11 -3
- metadata +166 -13
- data/.github/workflows/release.yml +0 -44
- data/.tool-versions +0 -1
- data/lib/flipper/railtie.rb +0 -47
- data/spec/flipper/railtie_spec.rb +0 -109
@@ -7,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, @adapter.enable(@feature, @actor_gate,
|
80
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate,
|
107
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor22))
|
108
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor_asdf))
|
81
109
|
|
82
110
|
result = @adapter.get(@feature)
|
83
111
|
assert_equal Set['22', 'asdf'], result[:actors]
|
84
112
|
|
85
|
-
assert true, @adapter.disable(@feature, @actor_gate,
|
113
|
+
assert true, @adapter.disable(@feature, @actor_gate, Flipper::Types::Actor.new(actor22))
|
86
114
|
result = @adapter.get(@feature)
|
87
115
|
assert_equal Set['asdf'], result[:actors]
|
88
116
|
|
89
|
-
assert_equal true, @adapter.disable(@feature, @actor_gate,
|
117
|
+
assert_equal true, @adapter.disable(@feature, @actor_gate, Flipper::Types::Actor.new(actor_asdf))
|
90
118
|
result = @adapter.get(@feature)
|
91
119
|
assert_equal Set.new, result[:actors]
|
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,21 +165,21 @@ 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
181
|
assert_equal true,
|
154
|
-
@adapter.enable(@feature, @actor_gate,
|
182
|
+
@adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(Flipper::Actor.new(22)))
|
155
183
|
result = @adapter.get(@feature)
|
156
184
|
assert_equal Set['22'], result[:actors]
|
157
185
|
end
|
@@ -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,8 +292,8 @@ 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, @adapter.enable(@feature, @actor_gate,
|
268
|
-
assert_equal true, @adapter.enable(@feature, @actor_gate,
|
295
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor))
|
296
|
+
assert_equal true, @adapter.enable(@feature, @actor_gate, Flipper::Types::Actor.new(actor))
|
269
297
|
assert_equal Set['Flipper::Actor;22'], @adapter.get(@feature).fetch(:actors)
|
270
298
|
end
|
271
299
|
|
@@ -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
|
data/lib/flipper/typecast.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'set'
|
2
|
+
require "flipper/serializers/json"
|
3
|
+
require "flipper/serializers/gzip"
|
2
4
|
|
3
5
|
module Flipper
|
4
6
|
module Typecast
|
@@ -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
data/lib/flipper.rb
CHANGED
@@ -56,28 +56,60 @@ module Flipper
|
|
56
56
|
# Public: All the methods delegated to instance. These should match the
|
57
57
|
# interface of Flipper::DSL.
|
58
58
|
def_delegators :instance,
|
59
|
-
:enabled?, :enable, :disable,
|
60
|
-
:
|
59
|
+
:enabled?, :enable, :disable,
|
60
|
+
:enable_expression, :disable_expression,
|
61
|
+
:expression, :add_expression, :remove_expression,
|
62
|
+
:enable_actor, :disable_actor,
|
61
63
|
:enable_group, :disable_group,
|
62
64
|
:enable_percentage_of_actors, :disable_percentage_of_actors,
|
63
|
-
:actors, :percentage_of_actors,
|
64
65
|
:enable_percentage_of_time, :disable_percentage_of_time,
|
65
|
-
:time, :percentage_of_time,
|
66
66
|
:features, :feature, :[], :preload, :preload_all,
|
67
|
-
:adapter, :add, :exist?, :remove, :import,
|
68
|
-
:memoize=, :memoizing?,
|
67
|
+
:adapter, :add, :exist?, :remove, :import, :export,
|
68
|
+
:memoize=, :memoizing?, :read_only?,
|
69
69
|
:sync, :sync_secret # For Flipper::Cloud. Will error for OSS Flipper.
|
70
70
|
|
71
|
+
def any(*args)
|
72
|
+
Expression.build({ Any: args.flatten })
|
73
|
+
end
|
74
|
+
|
75
|
+
def all(*args)
|
76
|
+
Expression.build({ All: args.flatten })
|
77
|
+
end
|
78
|
+
|
79
|
+
def constant(value)
|
80
|
+
Expression.build(value)
|
81
|
+
end
|
82
|
+
|
83
|
+
def property(name)
|
84
|
+
Expression.build({ Property: name })
|
85
|
+
end
|
86
|
+
|
87
|
+
def string(value)
|
88
|
+
Expression.build({ String: value })
|
89
|
+
end
|
90
|
+
|
91
|
+
def number(value)
|
92
|
+
Expression.build({ Number: value })
|
93
|
+
end
|
94
|
+
|
95
|
+
def boolean(value)
|
96
|
+
Expression.build({ Boolean: value })
|
97
|
+
end
|
98
|
+
|
99
|
+
def random(max)
|
100
|
+
Expression.build({ Random: max })
|
101
|
+
end
|
102
|
+
|
71
103
|
# Public: Use this to register a group by name.
|
72
104
|
#
|
73
105
|
# name - The Symbol name of the group.
|
74
106
|
# block - The block that should be used to determine if the group matches a
|
75
|
-
# given
|
107
|
+
# given actor.
|
76
108
|
#
|
77
109
|
# Examples
|
78
110
|
#
|
79
|
-
# Flipper.register(:admins) { |
|
80
|
-
#
|
111
|
+
# Flipper.register(:admins) { |actor|
|
112
|
+
# actor.respond_to?(:admin?) && actor.admin?
|
81
113
|
# }
|
82
114
|
#
|
83
115
|
# Returns a Flipper::Group.
|
@@ -144,7 +176,9 @@ require 'flipper/actor'
|
|
144
176
|
require 'flipper/adapter'
|
145
177
|
require 'flipper/adapters/memoizable'
|
146
178
|
require 'flipper/adapters/memory'
|
179
|
+
require 'flipper/adapters/strict'
|
147
180
|
require 'flipper/adapters/instrumented'
|
181
|
+
require 'flipper/adapter_builder'
|
148
182
|
require 'flipper/configuration'
|
149
183
|
require 'flipper/dsl'
|
150
184
|
require 'flipper/errors'
|
@@ -155,7 +189,9 @@ require 'flipper/instrumenters/noop'
|
|
155
189
|
require 'flipper/identifier'
|
156
190
|
require 'flipper/middleware/memoizer'
|
157
191
|
require 'flipper/middleware/setup_env'
|
192
|
+
require 'flipper/poller'
|
158
193
|
require 'flipper/registry'
|
194
|
+
require 'flipper/expression'
|
159
195
|
require 'flipper/type'
|
160
196
|
require 'flipper/types/actor'
|
161
197
|
require 'flipper/types/boolean'
|
@@ -164,5 +200,6 @@ require 'flipper/types/percentage'
|
|
164
200
|
require 'flipper/types/percentage_of_actors'
|
165
201
|
require 'flipper/types/percentage_of_time'
|
166
202
|
require 'flipper/typecast'
|
203
|
+
require 'flipper/version'
|
167
204
|
|
168
|
-
require "flipper/
|
205
|
+
require "flipper/engine" if defined?(Rails)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
{
|
2
|
+
"version": 1,
|
3
|
+
"features": {
|
4
|
+
"search": {
|
5
|
+
"boolean": null,
|
6
|
+
"actors": [
|
7
|
+
"john",
|
8
|
+
"another",
|
9
|
+
"testing"
|
10
|
+
],
|
11
|
+
"percentage_of_actors": null,
|
12
|
+
"percentage_of_time": null,
|
13
|
+
"groups": [
|
14
|
+
"admins"
|
15
|
+
]
|
16
|
+
},
|
17
|
+
"new_pricing": {
|
18
|
+
"boolean": "true",
|
19
|
+
"actors": [],
|
20
|
+
"percentage_of_actors": null,
|
21
|
+
"percentage_of_time": null,
|
22
|
+
"groups": []
|
23
|
+
},
|
24
|
+
"google_analytics_tag": {
|
25
|
+
"boolean": null,
|
26
|
+
"actors": [],
|
27
|
+
"percentage_of_actors": "100",
|
28
|
+
"percentage_of_time": null,
|
29
|
+
"groups": []
|
30
|
+
},
|
31
|
+
"help_scout_tag": {
|
32
|
+
"boolean": null,
|
33
|
+
"actors": [],
|
34
|
+
"percentage_of_actors": null,
|
35
|
+
"percentage_of_time": "50",
|
36
|
+
"groups": []
|
37
|
+
},
|
38
|
+
"nope": {
|
39
|
+
"boolean": null,
|
40
|
+
"actors": [],
|
41
|
+
"percentage_of_actors": null,
|
42
|
+
"percentage_of_time": null,
|
43
|
+
"groups": []
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|