flipper 0.16.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.codeclimate.yml +1 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ci.yml +110 -0
- data/.github/workflows/examples.yml +105 -0
- data/.github/workflows/release.yml +54 -0
- data/.rspec +1 -0
- data/CLAUDE.md +87 -0
- data/Changelog.md +2 -215
- data/Dockerfile +1 -1
- data/Gemfile +28 -20
- data/README.md +72 -62
- data/Rakefile +13 -3
- data/benchmark/enabled_ips.rb +10 -0
- data/benchmark/enabled_multiple_actors_ips.rb +20 -0
- data/benchmark/enabled_profile.rb +20 -0
- data/benchmark/instrumentation_ips.rb +21 -0
- data/benchmark/typecast_ips.rb +27 -0
- data/docker-compose.yml +37 -34
- data/docs/DockerCompose.md +0 -1
- data/docs/README.md +1 -0
- data/docs/images/banner.jpg +0 -0
- data/docs/images/flipper_cloud.png +0 -0
- data/examples/api/basic.ru +18 -0
- data/examples/api/custom_memoized.ru +36 -0
- data/examples/api/memoized.ru +42 -0
- data/examples/basic.rb +1 -12
- data/examples/cloud/app.ru +12 -0
- data/examples/cloud/backoff_policy.rb +13 -0
- data/examples/cloud/basic.rb +22 -0
- data/examples/cloud/cloud_setup.rb +20 -0
- data/examples/cloud/forked.rb +36 -0
- data/examples/cloud/import.rb +17 -0
- data/examples/cloud/poll_interval/README.md +111 -0
- data/examples/cloud/poll_interval/client.rb +108 -0
- data/examples/cloud/poll_interval/server.rb +98 -0
- data/examples/cloud/threaded.rb +33 -0
- data/examples/configuring_default.rb +2 -5
- data/examples/dsl.rb +10 -35
- data/examples/enabled_for_actor.rb +10 -15
- data/examples/expressions.rb +237 -0
- data/examples/group.rb +3 -6
- data/examples/group_dynamic_lookup.rb +5 -19
- data/examples/group_with_members.rb +4 -14
- data/examples/importing.rb +1 -1
- data/examples/individual_actor.rb +2 -5
- data/examples/instrumentation.rb +2 -2
- data/examples/instrumentation_last_accessed_at.rb +38 -0
- data/examples/memoizing.rb +35 -0
- data/examples/mirroring.rb +59 -0
- data/examples/percentage_of_actors.rb +6 -16
- data/examples/percentage_of_actors_enabled_check.rb +7 -10
- data/examples/percentage_of_actors_group.rb +5 -18
- data/examples/percentage_of_time.rb +3 -6
- data/examples/strict.rb +18 -0
- data/exe/flipper +5 -0
- data/flipper-cloud.gemspec +19 -0
- data/flipper.gemspec +10 -7
- data/lib/flipper/actor.rb +10 -3
- data/lib/flipper/adapter.rb +50 -8
- data/lib/flipper/adapter_builder.rb +44 -0
- data/lib/flipper/adapters/actor_limit.rb +54 -0
- data/lib/flipper/adapters/cache_base.rb +161 -0
- data/lib/flipper/adapters/dual_write.rb +63 -0
- data/lib/flipper/adapters/failover.rb +85 -0
- data/lib/flipper/adapters/failsafe.rb +72 -0
- data/lib/flipper/adapters/http/client.rb +64 -7
- data/lib/flipper/adapters/http/error.rb +19 -1
- data/lib/flipper/adapters/http.rb +97 -43
- data/lib/flipper/adapters/instrumented.rb +47 -26
- data/lib/flipper/adapters/memoizable.rb +44 -40
- data/lib/flipper/adapters/memory.rb +75 -111
- data/lib/flipper/adapters/operation_logger.rb +22 -78
- data/lib/flipper/adapters/poll/poller.rb +2 -0
- data/lib/flipper/adapters/poll.rb +52 -0
- data/lib/flipper/adapters/pstore.rb +27 -17
- data/lib/flipper/adapters/read_only.rb +8 -41
- data/lib/flipper/adapters/strict.rb +45 -0
- data/lib/flipper/adapters/sync/feature_synchronizer.rb +14 -1
- data/lib/flipper/adapters/sync/interval_synchronizer.rb +2 -7
- data/lib/flipper/adapters/sync/synchronizer.rb +13 -6
- data/lib/flipper/adapters/sync.rb +23 -29
- data/lib/flipper/adapters/wrapper.rb +54 -0
- data/lib/flipper/cli.rb +314 -0
- data/lib/flipper/cloud/configuration.rb +271 -0
- data/lib/flipper/cloud/dsl.rb +27 -0
- data/lib/flipper/cloud/message_verifier.rb +95 -0
- data/lib/flipper/cloud/middleware.rb +63 -0
- data/lib/flipper/cloud/migrate.rb +71 -0
- data/lib/flipper/cloud/routes.rb +14 -0
- data/lib/flipper/cloud/telemetry/backoff_policy.rb +96 -0
- data/lib/flipper/cloud/telemetry/instrumenter.rb +22 -0
- data/lib/flipper/cloud/telemetry/metric.rb +39 -0
- data/lib/flipper/cloud/telemetry/metric_storage.rb +30 -0
- data/lib/flipper/cloud/telemetry/submitter.rb +100 -0
- data/lib/flipper/cloud/telemetry.rb +191 -0
- data/lib/flipper/cloud.rb +54 -0
- data/lib/flipper/configuration.rb +54 -7
- data/lib/flipper/dsl.rb +58 -47
- data/lib/flipper/engine.rb +102 -0
- data/lib/flipper/errors.rb +3 -21
- data/lib/flipper/export.rb +24 -0
- data/lib/flipper/exporter.rb +17 -0
- data/lib/flipper/exporters/json/export.rb +32 -0
- data/lib/flipper/exporters/json/v1.rb +33 -0
- data/lib/flipper/expression/builder.rb +73 -0
- data/lib/flipper/expression/constant.rb +25 -0
- data/lib/flipper/expression.rb +71 -0
- data/lib/flipper/expressions/all.rb +9 -0
- data/lib/flipper/expressions/any.rb +9 -0
- data/lib/flipper/expressions/boolean.rb +9 -0
- data/lib/flipper/expressions/comparable.rb +13 -0
- data/lib/flipper/expressions/equal.rb +9 -0
- data/lib/flipper/expressions/feature_enabled.rb +34 -0
- data/lib/flipper/expressions/greater_than.rb +9 -0
- data/lib/flipper/expressions/greater_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/less_than.rb +9 -0
- data/lib/flipper/expressions/less_than_or_equal_to.rb +9 -0
- data/lib/flipper/expressions/not_equal.rb +9 -0
- data/lib/flipper/expressions/now.rb +9 -0
- data/lib/flipper/expressions/number.rb +9 -0
- data/lib/flipper/expressions/percentage.rb +9 -0
- data/lib/flipper/expressions/percentage_of_actors.rb +12 -0
- data/lib/flipper/expressions/property.rb +9 -0
- data/lib/flipper/expressions/random.rb +9 -0
- data/lib/flipper/expressions/string.rb +9 -0
- data/lib/flipper/expressions/time.rb +16 -0
- data/lib/flipper/feature.rb +95 -28
- data/lib/flipper/feature_check_context.rb +10 -6
- data/lib/flipper/gate.rb +13 -11
- data/lib/flipper/gate_values.rb +5 -18
- data/lib/flipper/gates/actor.rb +10 -17
- data/lib/flipper/gates/boolean.rb +1 -1
- data/lib/flipper/gates/expression.rb +75 -0
- data/lib/flipper/gates/group.rb +5 -7
- data/lib/flipper/gates/percentage_of_actors.rb +10 -13
- data/lib/flipper/gates/percentage_of_time.rb +1 -2
- data/lib/flipper/identifier.rb +17 -0
- data/lib/flipper/instrumentation/log_subscriber.rb +35 -8
- data/lib/flipper/instrumentation/statsd.rb +4 -2
- data/lib/flipper/instrumentation/statsd_subscriber.rb +2 -4
- data/lib/flipper/instrumentation/subscriber.rb +8 -5
- data/lib/flipper/instrumenters/memory.rb +6 -2
- data/lib/flipper/metadata.rb +8 -1
- data/lib/flipper/middleware/memoizer.rb +46 -27
- data/lib/flipper/middleware/setup_env.rb +13 -3
- data/lib/flipper/model/active_record.rb +23 -0
- data/lib/flipper/poller.rb +157 -0
- data/lib/flipper/serializers/gzip.rb +22 -0
- data/lib/flipper/serializers/json.rb +17 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +122 -56
- data/lib/flipper/test/shared_adapter_test.rb +120 -52
- data/lib/flipper/test_help.rb +43 -0
- data/lib/flipper/typecast.rb +59 -18
- data/lib/flipper/types/actor.rb +19 -13
- data/lib/flipper/types/group.rb +12 -5
- data/lib/flipper/types/percentage.rb +1 -1
- data/lib/flipper/version.rb +11 -1
- data/lib/flipper.rb +71 -12
- data/lib/generators/flipper/setup_generator.rb +68 -0
- data/lib/generators/flipper/templates/initializer.rb +45 -0
- data/lib/generators/flipper/templates/update/migrations/01_create_flipper_tables.rb.erb +22 -0
- data/lib/generators/flipper/templates/update/migrations/02_change_flipper_gates_value_to_text.rb.erb +18 -0
- data/lib/generators/flipper/update_generator.rb +35 -0
- data/package-lock.json +41 -0
- data/package.json +10 -0
- data/spec/fixtures/environment.rb +1 -0
- data/spec/fixtures/flipper_pstore_1679087600.json +46 -0
- data/spec/flipper/actor_spec.rb +10 -2
- data/spec/flipper/adapter_builder_spec.rb +72 -0
- data/spec/flipper/adapter_spec.rb +52 -6
- data/spec/flipper/adapters/actor_limit_spec.rb +75 -0
- data/spec/flipper/adapters/dual_write_spec.rb +82 -0
- data/spec/flipper/adapters/failover_spec.rb +141 -0
- data/spec/flipper/adapters/failsafe_spec.rb +58 -0
- data/spec/flipper/adapters/http/client_spec.rb +61 -0
- data/spec/flipper/adapters/http_spec.rb +402 -65
- data/spec/flipper/adapters/instrumented_spec.rb +31 -13
- data/spec/flipper/adapters/memoizable_spec.rb +51 -33
- data/spec/flipper/adapters/memory_spec.rb +33 -5
- data/spec/flipper/adapters/operation_logger_spec.rb +38 -12
- data/spec/flipper/adapters/poll_spec.rb +41 -0
- data/spec/flipper/adapters/pstore_spec.rb +0 -2
- data/spec/flipper/adapters/read_only_spec.rb +32 -18
- data/spec/flipper/adapters/strict_spec.rb +64 -0
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +39 -1
- data/spec/flipper/adapters/sync/interval_synchronizer_spec.rb +4 -5
- data/spec/flipper/adapters/sync/synchronizer_spec.rb +87 -1
- data/spec/flipper/adapters/sync_spec.rb +17 -6
- data/spec/flipper/cli_spec.rb +217 -0
- data/spec/flipper/cloud/configuration_spec.rb +257 -0
- data/spec/flipper/cloud/dsl_spec.rb +90 -0
- data/spec/flipper/cloud/message_verifier_spec.rb +104 -0
- data/spec/flipper/cloud/middleware_spec.rb +307 -0
- data/spec/flipper/cloud/migrate_spec.rb +160 -0
- data/spec/flipper/cloud/telemetry/backoff_policy_spec.rb +107 -0
- data/spec/flipper/cloud/telemetry/metric_spec.rb +87 -0
- data/spec/flipper/cloud/telemetry/metric_storage_spec.rb +58 -0
- data/spec/flipper/cloud/telemetry/submitter_spec.rb +145 -0
- data/spec/flipper/cloud/telemetry_spec.rb +208 -0
- data/spec/flipper/cloud_spec.rb +186 -0
- data/spec/flipper/configuration_spec.rb +37 -3
- data/spec/flipper/dsl_spec.rb +67 -80
- data/spec/flipper/engine_spec.rb +374 -0
- data/spec/flipper/export_spec.rb +13 -0
- data/spec/flipper/exporter_spec.rb +16 -0
- data/spec/flipper/exporters/json/export_spec.rb +60 -0
- data/spec/flipper/exporters/json/v1_spec.rb +33 -0
- data/spec/flipper/expression/builder_spec.rb +248 -0
- data/spec/flipper/expression_spec.rb +188 -0
- data/spec/flipper/expressions/all_spec.rb +15 -0
- data/spec/flipper/expressions/any_spec.rb +15 -0
- data/spec/flipper/expressions/boolean_spec.rb +15 -0
- data/spec/flipper/expressions/equal_spec.rb +24 -0
- data/spec/flipper/expressions/greater_than_or_equal_to_spec.rb +28 -0
- data/spec/flipper/expressions/greater_than_spec.rb +28 -0
- data/spec/flipper/expressions/less_than_or_equal_to_spec.rb +28 -0
- data/spec/flipper/expressions/less_than_spec.rb +32 -0
- data/spec/flipper/expressions/not_equal_spec.rb +15 -0
- data/spec/flipper/expressions/now_spec.rb +11 -0
- data/spec/flipper/expressions/number_spec.rb +21 -0
- data/spec/flipper/expressions/percentage_of_actors_spec.rb +20 -0
- data/spec/flipper/expressions/percentage_spec.rb +15 -0
- data/spec/flipper/expressions/property_spec.rb +13 -0
- data/spec/flipper/expressions/random_spec.rb +9 -0
- data/spec/flipper/expressions/string_spec.rb +11 -0
- data/spec/flipper/expressions/time_spec.rb +29 -0
- data/spec/flipper/feature_check_context_spec.rb +18 -20
- data/spec/flipper/feature_spec.rb +461 -48
- data/spec/flipper/gate_spec.rb +0 -2
- data/spec/flipper/gate_values_spec.rb +2 -34
- data/spec/flipper/gates/actor_spec.rb +0 -2
- data/spec/flipper/gates/boolean_spec.rb +1 -3
- data/spec/flipper/gates/expression_spec.rb +190 -0
- data/spec/flipper/gates/group_spec.rb +2 -5
- data/spec/flipper/gates/percentage_of_actors_spec.rb +61 -7
- data/spec/flipper/gates/percentage_of_time_spec.rb +2 -4
- data/spec/flipper/identifier_spec.rb +12 -0
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +24 -7
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +26 -3
- data/spec/flipper/instrumenters/memory_spec.rb +18 -1
- data/spec/flipper/instrumenters/noop_spec.rb +14 -8
- data/spec/flipper/middleware/memoizer_spec.rb +199 -62
- data/spec/flipper/middleware/setup_env_spec.rb +23 -5
- data/spec/flipper/model/active_record_spec.rb +72 -0
- data/spec/flipper/poller_spec.rb +390 -0
- data/spec/flipper/registry_spec.rb +0 -1
- data/spec/flipper/serializers/gzip_spec.rb +13 -0
- data/spec/flipper/serializers/json_spec.rb +13 -0
- data/spec/flipper/typecast_spec.rb +121 -7
- data/spec/flipper/types/actor_spec.rb +63 -47
- data/spec/flipper/types/boolean_spec.rb +0 -1
- data/spec/flipper/types/group_spec.rb +24 -3
- data/spec/flipper/types/percentage_of_actors_spec.rb +0 -1
- data/spec/flipper/types/percentage_of_time_spec.rb +0 -1
- data/spec/flipper/types/percentage_spec.rb +0 -1
- data/spec/{integration_spec.rb → flipper_integration_spec.rb} +301 -59
- data/spec/flipper_spec.rb +123 -29
- data/spec/{helper.rb → spec_helper.rb} +23 -21
- data/spec/support/actor_names.yml +1 -0
- data/spec/support/descriptions.yml +1 -0
- data/spec/support/fail_on_output.rb +8 -0
- data/spec/support/fake_backoff_policy.rb +15 -0
- data/spec/support/skippable.rb +18 -0
- data/spec/support/spec_helpers.rb +53 -6
- data/test/adapters/actor_limit_test.rb +20 -0
- data/test/test_helper.rb +2 -1
- data/test_rails/generators/flipper/setup_generator_test.rb +69 -0
- data/test_rails/generators/flipper/update_generator_test.rb +96 -0
- data/test_rails/helper.rb +31 -0
- data/test_rails/system/test_help_test.rb +52 -0
- metadata +200 -82
- data/.rubocop.yml +0 -54
- data/.rubocop_todo.yml +0 -199
- data/docs/Adapters.md +0 -124
- data/docs/Caveats.md +0 -4
- data/docs/Gates.md +0 -167
- data/docs/Instrumentation.md +0 -27
- data/docs/Optimization.md +0 -114
- data/docs/api/README.md +0 -849
- data/docs/http/README.md +0 -35
- data/docs/read-only/README.md +0 -21
- data/examples/example_setup.rb +0 -8
- data/test/helper.rb +0 -11
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
require 'helper'
|
|
2
1
|
require 'rack/test'
|
|
3
2
|
require 'active_support/cache'
|
|
4
|
-
require 'active_support/cache/dalli_store'
|
|
5
3
|
require 'flipper/adapters/active_support_cache_store'
|
|
6
4
|
require 'flipper/adapters/operation_logger'
|
|
5
|
+
require 'flipper/adapters/actor_limit'
|
|
6
|
+
require 'flipper/adapters/sync'
|
|
7
7
|
|
|
8
8
|
RSpec.describe Flipper::Middleware::Memoizer do
|
|
9
9
|
include Rack::Test::Methods
|
|
@@ -15,10 +15,6 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
15
15
|
let(:flipper) { Flipper.new(adapter) }
|
|
16
16
|
let(:env) { { 'flipper' => flipper } }
|
|
17
17
|
|
|
18
|
-
after do
|
|
19
|
-
flipper.memoize = nil
|
|
20
|
-
end
|
|
21
|
-
|
|
22
18
|
it 'raises if initialized with app and flipper instance' do
|
|
23
19
|
expect do
|
|
24
20
|
described_class.new(app, flipper)
|
|
@@ -44,26 +40,6 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
44
40
|
expect(called).to eq(true)
|
|
45
41
|
end
|
|
46
42
|
|
|
47
|
-
it 'disables local cache after body close' do
|
|
48
|
-
app = ->(_env) { [200, {}, []] }
|
|
49
|
-
middleware = described_class.new(app)
|
|
50
|
-
body = middleware.call(env).last
|
|
51
|
-
|
|
52
|
-
expect(flipper.memoizing?).to eq(true)
|
|
53
|
-
body.close
|
|
54
|
-
expect(flipper.memoizing?).to eq(false)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
it 'clears local cache after body close' do
|
|
58
|
-
app = ->(_env) { [200, {}, []] }
|
|
59
|
-
middleware = described_class.new(app)
|
|
60
|
-
body = middleware.call(env).last
|
|
61
|
-
|
|
62
|
-
flipper.adapter.cache['hello'] = 'world'
|
|
63
|
-
body.close
|
|
64
|
-
expect(flipper.adapter.cache).to be_empty
|
|
65
|
-
end
|
|
66
|
-
|
|
67
43
|
it 'clears the local cache with a successful request' do
|
|
68
44
|
flipper.adapter.cache['hello'] = 'world'
|
|
69
45
|
get '/', {}, 'flipper' => flipper
|
|
@@ -103,14 +79,14 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
103
79
|
end
|
|
104
80
|
end
|
|
105
81
|
|
|
106
|
-
context 'with
|
|
82
|
+
context 'with preload: true' do
|
|
107
83
|
let(:app) do
|
|
108
84
|
# ensure scoped for builder block, annoying...
|
|
109
|
-
|
|
85
|
+
flipper
|
|
110
86
|
middleware = described_class
|
|
111
87
|
|
|
112
88
|
Rack::Builder.new do
|
|
113
|
-
use middleware,
|
|
89
|
+
use middleware, preload: true
|
|
114
90
|
|
|
115
91
|
map '/' do
|
|
116
92
|
run ->(_env) { [200, {}, []] }
|
|
@@ -139,7 +115,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
139
115
|
[200, {}, []]
|
|
140
116
|
end
|
|
141
117
|
|
|
142
|
-
middleware = described_class.new(app,
|
|
118
|
+
middleware = described_class.new(app, preload: true)
|
|
143
119
|
middleware.call(env)
|
|
144
120
|
|
|
145
121
|
expect(adapter.operations.size).to be(1)
|
|
@@ -156,7 +132,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
156
132
|
[200, {}, []]
|
|
157
133
|
end
|
|
158
134
|
|
|
159
|
-
middleware = described_class.new(app,
|
|
135
|
+
middleware = described_class.new(app, preload: true)
|
|
160
136
|
middleware.call(env)
|
|
161
137
|
|
|
162
138
|
expect(adapter.count(:get)).to be(1)
|
|
@@ -167,7 +143,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
167
143
|
context 'with preload specific' do
|
|
168
144
|
let(:app) do
|
|
169
145
|
# ensure scoped for builder block, annoying...
|
|
170
|
-
|
|
146
|
+
flipper
|
|
171
147
|
middleware = described_class
|
|
172
148
|
|
|
173
149
|
Rack::Builder.new do
|
|
@@ -222,6 +198,111 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
222
198
|
end
|
|
223
199
|
end
|
|
224
200
|
|
|
201
|
+
context 'with preload block' do
|
|
202
|
+
let(:app) do
|
|
203
|
+
app = lambda do |_env|
|
|
204
|
+
flipper[:stats].enabled?
|
|
205
|
+
flipper[:stats].enabled?
|
|
206
|
+
flipper[:shiny].enabled?
|
|
207
|
+
flipper[:shiny].enabled?
|
|
208
|
+
[200, {}, []]
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
described_class.new(app, preload: ->(request) {
|
|
212
|
+
case request.path
|
|
213
|
+
when "/true"
|
|
214
|
+
true
|
|
215
|
+
when "/specific"
|
|
216
|
+
[:stats]
|
|
217
|
+
else
|
|
218
|
+
false
|
|
219
|
+
end
|
|
220
|
+
})
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
include_examples 'flipper middleware'
|
|
224
|
+
|
|
225
|
+
it 'eagerly caches known features for duration of request if block returns true' do
|
|
226
|
+
flipper[:stats].enable
|
|
227
|
+
flipper[:shiny].enable
|
|
228
|
+
|
|
229
|
+
# clear the log of operations
|
|
230
|
+
adapter.reset
|
|
231
|
+
|
|
232
|
+
get '/true', {}, 'flipper' => flipper
|
|
233
|
+
|
|
234
|
+
expect(adapter.operations.size).to be(1)
|
|
235
|
+
expect(adapter.count(:get_all)).to be(1)
|
|
236
|
+
expect(adapter.count(:get)).to be(0)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
it 'does not eagerly cache known features if block returns false' do
|
|
240
|
+
flipper[:stats].enable
|
|
241
|
+
flipper[:shiny].enable
|
|
242
|
+
|
|
243
|
+
# clear the log of operations
|
|
244
|
+
adapter.reset
|
|
245
|
+
|
|
246
|
+
get '/false', {}, 'flipper' => flipper
|
|
247
|
+
|
|
248
|
+
expect(adapter.operations.size).to be(2)
|
|
249
|
+
expect(adapter.count(:get_all)).to be(0)
|
|
250
|
+
expect(adapter.count(:get)).to be(2)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
it 'eagerly caches specified features for duration of request if block returns array of specified features' do
|
|
254
|
+
flipper[:stats].enable
|
|
255
|
+
flipper[:shiny].enable
|
|
256
|
+
|
|
257
|
+
# clear the log of operations
|
|
258
|
+
adapter.reset
|
|
259
|
+
|
|
260
|
+
get '/specific', {}, 'flipper' => flipper
|
|
261
|
+
|
|
262
|
+
expect(adapter.operations.size).to be(2)
|
|
263
|
+
expect(adapter.count(:get_multi)).to be(1)
|
|
264
|
+
expect(adapter.count(:get)).to be(1)
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
context 'with multiple instances' do
|
|
269
|
+
let(:app) do
|
|
270
|
+
# ensure scoped for builder block, annoying...
|
|
271
|
+
flipper
|
|
272
|
+
middleware = described_class
|
|
273
|
+
|
|
274
|
+
Rack::Builder.new do
|
|
275
|
+
use middleware, preload: %i(stats)
|
|
276
|
+
# Second instance should be a noop
|
|
277
|
+
use middleware, preload: true
|
|
278
|
+
|
|
279
|
+
map '/' do
|
|
280
|
+
run ->(_env) { [200, {}, []] }
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
map '/fail' do
|
|
284
|
+
run ->(_env) { raise 'FAIL!' }
|
|
285
|
+
end
|
|
286
|
+
end.to_app
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def get(uri, params = {}, env = {}, &block)
|
|
290
|
+
capture_output { super(uri, params, env, &block) }
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
include_examples 'flipper middleware'
|
|
294
|
+
|
|
295
|
+
it 'does not call preload in second instance' do
|
|
296
|
+
expect(flipper).not_to receive(:preload_all)
|
|
297
|
+
|
|
298
|
+
output = get '/', {}, 'flipper' => flipper
|
|
299
|
+
|
|
300
|
+
expect(output).to match(/Flipper::Middleware::Memoizer appears to be running twice/)
|
|
301
|
+
expect(adapter.count(:get_multi)).to be(1)
|
|
302
|
+
expect(adapter.last(:get_multi).args).to eq([[flipper[:stats]]])
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
225
306
|
context 'when an app raises an exception' do
|
|
226
307
|
it 'resets memoize' do
|
|
227
308
|
begin
|
|
@@ -237,7 +318,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
237
318
|
context 'with flipper setup in env' do
|
|
238
319
|
let(:app) do
|
|
239
320
|
# ensure scoped for builder block, annoying...
|
|
240
|
-
|
|
321
|
+
flipper
|
|
241
322
|
middleware = described_class
|
|
242
323
|
|
|
243
324
|
Rack::Builder.new do
|
|
@@ -259,10 +340,9 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
259
340
|
context 'with Flipper setup in env' do
|
|
260
341
|
it 'caches getting a feature for duration of request' do
|
|
261
342
|
Flipper.configure do |config|
|
|
262
|
-
config.
|
|
343
|
+
config.adapter do
|
|
263
344
|
memory = Flipper::Adapters::Memory.new
|
|
264
|
-
|
|
265
|
-
Flipper.new(logged_adapter)
|
|
345
|
+
Flipper::Adapters::OperationLogger.new(memory)
|
|
266
346
|
end
|
|
267
347
|
end
|
|
268
348
|
Flipper.enable(:stats)
|
|
@@ -308,14 +388,16 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
308
388
|
end
|
|
309
389
|
end
|
|
310
390
|
|
|
311
|
-
context 'with
|
|
391
|
+
context 'with preload:true' do
|
|
392
|
+
let(:options) { {preload: true} }
|
|
393
|
+
|
|
312
394
|
let(:app) do
|
|
313
395
|
# ensure scoped for builder block, annoying...
|
|
314
396
|
middleware = described_class
|
|
397
|
+
opts = options
|
|
315
398
|
|
|
316
399
|
Rack::Builder.new do
|
|
317
|
-
use middleware,
|
|
318
|
-
unless: ->(request) { request.path.start_with?("/assets") }
|
|
400
|
+
use middleware, opts
|
|
319
401
|
|
|
320
402
|
map '/' do
|
|
321
403
|
run ->(_env) { [200, {}, []] }
|
|
@@ -327,26 +409,59 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
327
409
|
end.to_app
|
|
328
410
|
end
|
|
329
411
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
412
|
+
context 'and unless option' do
|
|
413
|
+
before do
|
|
414
|
+
options[:unless] = ->(request) { request.path.start_with?("/assets") }
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
it 'does NOT preload if request matches unless block' do
|
|
418
|
+
expect(flipper).to receive(:preload_all).never
|
|
419
|
+
get '/assets/foo.png', {}, 'flipper' => flipper
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
it 'does preload if request does NOT match unless block' do
|
|
423
|
+
expect(flipper).to receive(:preload_all).once
|
|
424
|
+
get '/some/other/path', {}, 'flipper' => flipper
|
|
425
|
+
end
|
|
333
426
|
end
|
|
334
427
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
428
|
+
context 'and if option' do
|
|
429
|
+
before do
|
|
430
|
+
options[:if] = ->(request) { !request.path.start_with?("/assets") }
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
it 'does NOT preload if request does not match if block' do
|
|
434
|
+
expect(flipper).to receive(:preload_all).never
|
|
435
|
+
get '/assets/foo.png', {}, 'flipper' => flipper
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
it 'does preload if request matches if block' do
|
|
439
|
+
expect(flipper).to receive(:preload_all).once
|
|
440
|
+
get '/some/other/path', {}, 'flipper' => flipper
|
|
441
|
+
end
|
|
338
442
|
end
|
|
339
443
|
end
|
|
340
444
|
|
|
341
|
-
context 'with
|
|
445
|
+
context 'with preload:true and caching adapter' do
|
|
446
|
+
let(:app) do
|
|
447
|
+
app = lambda do |_env|
|
|
448
|
+
flipper[:stats].enabled?
|
|
449
|
+
flipper[:stats].enabled?
|
|
450
|
+
flipper[:shiny].enabled?
|
|
451
|
+
flipper[:shiny].enabled?
|
|
452
|
+
[200, {}, []]
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
described_class.new(app, preload: true)
|
|
456
|
+
end
|
|
457
|
+
|
|
342
458
|
it 'eagerly caches known features for duration of request' do
|
|
343
459
|
memory = Flipper::Adapters::Memory.new
|
|
344
460
|
logged_memory = Flipper::Adapters::OperationLogger.new(memory)
|
|
345
|
-
cache = ActiveSupport::Cache::
|
|
461
|
+
cache = ActiveSupport::Cache::MemoryStore.new
|
|
346
462
|
cache.clear
|
|
347
|
-
cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache
|
|
463
|
+
cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache)
|
|
348
464
|
logged_cached = Flipper::Adapters::OperationLogger.new(cached)
|
|
349
|
-
memo = {}
|
|
350
465
|
flipper = Flipper.new(logged_cached)
|
|
351
466
|
flipper[:stats].enable
|
|
352
467
|
flipper[:shiny].enable
|
|
@@ -355,27 +470,49 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
|
355
470
|
logged_memory.reset
|
|
356
471
|
logged_cached.reset
|
|
357
472
|
|
|
358
|
-
|
|
359
|
-
flipper[:stats].enabled?
|
|
360
|
-
flipper[:stats].enabled?
|
|
361
|
-
flipper[:shiny].enabled?
|
|
362
|
-
flipper[:shiny].enabled?
|
|
363
|
-
[200, {}, []]
|
|
364
|
-
end
|
|
365
|
-
|
|
366
|
-
middleware = described_class.new(app, preload_all: true)
|
|
367
|
-
|
|
368
|
-
middleware.call('flipper' => flipper)
|
|
473
|
+
get '/', {}, 'flipper' => flipper
|
|
369
474
|
expect(logged_cached.count(:get_all)).to be(1)
|
|
370
475
|
expect(logged_memory.count(:get_all)).to be(1)
|
|
371
476
|
|
|
372
|
-
|
|
477
|
+
get '/', {}, 'flipper' => flipper
|
|
373
478
|
expect(logged_cached.count(:get_all)).to be(2)
|
|
374
479
|
expect(logged_memory.count(:get_all)).to be(1)
|
|
375
480
|
|
|
376
|
-
|
|
481
|
+
get '/', {}, 'flipper' => flipper
|
|
377
482
|
expect(logged_cached.count(:get_all)).to be(3)
|
|
378
483
|
expect(logged_memory.count(:get_all)).to be(1)
|
|
379
484
|
end
|
|
380
485
|
end
|
|
486
|
+
|
|
487
|
+
context 'with preload:true and Sync adapter wrapped with ActorLimit' do
|
|
488
|
+
it 'preloads even when remote has more actors than local limit' do
|
|
489
|
+
local = Flipper::Adapters::Memory.new
|
|
490
|
+
remote = Flipper::Adapters::Memory.new
|
|
491
|
+
remote_flipper = Flipper.new(remote)
|
|
492
|
+
|
|
493
|
+
# Remote has more actors than limit allows (actor-only enables, not boolean)
|
|
494
|
+
10.times { |i| remote_flipper[:stats].enable_actor Flipper::Actor.new("User;#{i}") }
|
|
495
|
+
|
|
496
|
+
# Sync adapter will sync from remote to local, then ActorLimit wraps it
|
|
497
|
+
# Use interval: 0 to force sync on every call
|
|
498
|
+
sync_adapter = Flipper::Adapters::Sync.new(local, remote, interval: 0)
|
|
499
|
+
limited_adapter = Flipper::Adapters::ActorLimit.new(sync_adapter, 5)
|
|
500
|
+
test_flipper = Flipper.new(limited_adapter)
|
|
501
|
+
|
|
502
|
+
app = lambda do |env|
|
|
503
|
+
f = env['flipper']
|
|
504
|
+
f[:stats].enabled?
|
|
505
|
+
[200, {}, []]
|
|
506
|
+
end
|
|
507
|
+
middleware = described_class.new(app, preload: true)
|
|
508
|
+
|
|
509
|
+
# Preload should work without raising ActorLimit::LimitExceeded
|
|
510
|
+
expect {
|
|
511
|
+
middleware.call('flipper' => test_flipper)
|
|
512
|
+
}.not_to raise_error
|
|
513
|
+
|
|
514
|
+
# Verify actors were synced (all 10, not just 5)
|
|
515
|
+
expect(test_flipper[:stats].actors_value.size).to eq(10)
|
|
516
|
+
end
|
|
517
|
+
end
|
|
381
518
|
end
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
require 'helper'
|
|
2
|
-
|
|
3
1
|
RSpec.describe Flipper::Middleware::SetupEnv do
|
|
4
2
|
context 'with flipper instance' do
|
|
5
3
|
let(:app) do
|
|
@@ -56,21 +54,41 @@ RSpec.describe Flipper::Middleware::SetupEnv do
|
|
|
56
54
|
end
|
|
57
55
|
end
|
|
58
56
|
|
|
59
|
-
context 'when flipper instance is
|
|
57
|
+
context 'when flipper instance or block are nil but env flipper is configured' do
|
|
60
58
|
let(:app) do
|
|
61
59
|
app = lambda do |env|
|
|
62
60
|
[200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]]
|
|
63
61
|
end
|
|
64
62
|
builder = Rack::Builder.new
|
|
65
|
-
builder.use described_class
|
|
63
|
+
builder.use described_class
|
|
66
64
|
builder.run app
|
|
67
65
|
builder
|
|
68
66
|
end
|
|
69
67
|
|
|
70
|
-
it '
|
|
68
|
+
it 'can use env flipper' do
|
|
71
69
|
env_flipper = build_flipper
|
|
72
70
|
get '/', {}, 'flipper' => env_flipper
|
|
73
71
|
expect(last_response.body).to eq(env_flipper.object_id.to_s)
|
|
74
72
|
end
|
|
75
73
|
end
|
|
74
|
+
|
|
75
|
+
context 'when flipper instance or block are nil and default Flipper is configured' do
|
|
76
|
+
let(:app) do
|
|
77
|
+
Flipper.configure do |config|
|
|
78
|
+
config.default { flipper }
|
|
79
|
+
end
|
|
80
|
+
app = lambda do |env|
|
|
81
|
+
[200, { 'Content-Type' => 'text/html' }, [env['flipper'].object_id.to_s]]
|
|
82
|
+
end
|
|
83
|
+
builder = Rack::Builder.new
|
|
84
|
+
builder.use described_class
|
|
85
|
+
builder.run app
|
|
86
|
+
builder
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it 'can use env flipper' do
|
|
90
|
+
get '/', {}, {}
|
|
91
|
+
expect(last_response.body).to eq(Flipper.object_id.to_s)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
76
94
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require 'active_record'
|
|
2
|
+
require 'flipper/model/active_record'
|
|
3
|
+
|
|
4
|
+
# Turn off migration logging for specs
|
|
5
|
+
ActiveRecord::Migration.verbose = false
|
|
6
|
+
|
|
7
|
+
RSpec.describe Flipper::Model::ActiveRecord do
|
|
8
|
+
before(:all) do
|
|
9
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
before(:each) do
|
|
13
|
+
ActiveRecord::Base.connection.execute <<-SQL
|
|
14
|
+
CREATE TABLE users (
|
|
15
|
+
id integer PRIMARY KEY,
|
|
16
|
+
name string NOT NULL,
|
|
17
|
+
age integer,
|
|
18
|
+
is_confirmed boolean,
|
|
19
|
+
created_at datetime NOT NULL,
|
|
20
|
+
updated_at datetime NOT NULL
|
|
21
|
+
)
|
|
22
|
+
SQL
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
after(:each) do
|
|
26
|
+
ActiveRecord::Base.connection.execute("DROP table IF EXISTS `users`")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class User < ActiveRecord::Base
|
|
30
|
+
include Flipper::Model::ActiveRecord
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class DelegatedUser < DelegateClass(User)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Admin < User
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "doesn't warn for to_ary" do
|
|
40
|
+
# looks like we should remove this but you are wrong, we have specs that
|
|
41
|
+
# fail if there are warnings and if this regresses it will print a warning
|
|
42
|
+
# so it is in fact testing something
|
|
43
|
+
user = User.create!(name: "Test")
|
|
44
|
+
Flipper.enabled?(:something, DelegatedUser.new(user))
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe "flipper_id" do
|
|
48
|
+
it "returns class name and id" do
|
|
49
|
+
expect(User.new(id: 1).flipper_id).to eq("User;1")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "uses base class name" do
|
|
53
|
+
expect(Admin.new(id: 2).flipper_id).to eq("User;2")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe "flipper_properties" do
|
|
58
|
+
subject { User.create!(name: "Test", age: 22, is_confirmed: true) }
|
|
59
|
+
|
|
60
|
+
it "includes all attributes" do
|
|
61
|
+
expect(subject.flipper_properties).to eq({
|
|
62
|
+
"type" => "User",
|
|
63
|
+
"id" => subject.id,
|
|
64
|
+
"name" => "Test",
|
|
65
|
+
"age" => 22,
|
|
66
|
+
"is_confirmed" => true,
|
|
67
|
+
"created_at" => subject.created_at,
|
|
68
|
+
"updated_at" => subject.updated_at
|
|
69
|
+
})
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|