flipper 0.21.0.rc1 → 0.22.1
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/workflows/ci.yml +12 -16
- data/.github/workflows/examples.yml +56 -0
- data/Changelog.md +54 -0
- data/Gemfile +1 -0
- data/README.md +2 -1
- data/docker-compose.yml +37 -34
- data/docs/Adapters.md +9 -10
- data/docs/Caveats.md +2 -2
- data/docs/Optimization.md +70 -47
- data/docs/api/README.md +5 -5
- data/docs/http/README.md +12 -11
- data/docs/read-only/README.md +8 -5
- data/examples/api/basic.ru +19 -0
- data/examples/api/custom_memoized.ru +37 -0
- data/examples/api/memoized.ru +43 -0
- data/examples/configuring_default.rb +1 -3
- data/examples/instrumentation_last_accessed_at.rb +37 -0
- data/examples/memoizing.rb +2 -5
- data/lib/flipper/adapters/pstore.rb +4 -0
- data/lib/flipper/adapters/sync/synchronizer.rb +2 -1
- data/lib/flipper/configuration.rb +29 -3
- data/lib/flipper/middleware/memoizer.rb +30 -15
- data/lib/flipper/railtie.rb +46 -0
- data/lib/flipper/version.rb +1 -1
- data/lib/flipper.rb +3 -1
- data/spec/flipper/configuration_spec.rb +16 -1
- data/spec/flipper/middleware/memoizer_spec.rb +94 -34
- data/spec/flipper/railtie_spec.rb +69 -0
- data/spec/helper.rb +2 -2
- data/spec/support/spec_helpers.rb +17 -0
- metadata +12 -4
data/lib/flipper/version.rb
CHANGED
data/lib/flipper.rb
CHANGED
@@ -16,7 +16,7 @@ module Flipper
|
|
16
16
|
# Public: Configure flipper.
|
17
17
|
#
|
18
18
|
# Flipper.configure do |config|
|
19
|
-
# config.
|
19
|
+
# config.adapter { ... }
|
20
20
|
# end
|
21
21
|
#
|
22
22
|
# Yields Flipper::Configuration instance.
|
@@ -164,3 +164,5 @@ require 'flipper/types/percentage'
|
|
164
164
|
require 'flipper/types/percentage_of_actors'
|
165
165
|
require 'flipper/types/percentage_of_time'
|
166
166
|
require 'flipper/typecast'
|
167
|
+
|
168
|
+
require "flipper/railtie" if defined?(Rails::Railtie)
|
@@ -2,10 +2,25 @@ require 'helper'
|
|
2
2
|
require 'flipper/configuration'
|
3
3
|
|
4
4
|
RSpec.describe Flipper::Configuration do
|
5
|
+
describe '#adapter' do
|
6
|
+
it 'returns instance using Memory adapter' do
|
7
|
+
expect(subject.adapter).to be_a(Flipper::Adapters::Memory)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'can be set' do
|
11
|
+
instance = Flipper::Adapters::Memory.new
|
12
|
+
expect(subject.adapter).not_to be(instance)
|
13
|
+
subject.adapter { instance }
|
14
|
+
expect(subject.adapter).to be(instance)
|
15
|
+
# All adapters are wrapped in Memoizable
|
16
|
+
expect(subject.default.adapter.adapter).to be(instance)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
5
20
|
describe '#default' do
|
6
21
|
it 'returns instance using Memory adapter' do
|
7
22
|
expect(subject.default).to be_a(Flipper::DSL)
|
8
|
-
# All
|
23
|
+
# All adapters are wrapped in Memoizable
|
9
24
|
expect(subject.default.adapter.adapter).to be_a(Flipper::Adapters::Memory)
|
10
25
|
end
|
11
26
|
|
@@ -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)
|
@@ -103,14 +99,14 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
103
99
|
end
|
104
100
|
end
|
105
101
|
|
106
|
-
context 'with
|
102
|
+
context 'with preload: true' do
|
107
103
|
let(:app) do
|
108
104
|
# ensure scoped for builder block, annoying...
|
109
105
|
instance = flipper
|
110
106
|
middleware = described_class
|
111
107
|
|
112
108
|
Rack::Builder.new do
|
113
|
-
use middleware,
|
109
|
+
use middleware, preload: true
|
114
110
|
|
115
111
|
map '/' do
|
116
112
|
run ->(_env) { [200, {}, []] }
|
@@ -139,7 +135,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
139
135
|
[200, {}, []]
|
140
136
|
end
|
141
137
|
|
142
|
-
middleware = described_class.new(app,
|
138
|
+
middleware = described_class.new(app, preload: true)
|
143
139
|
middleware.call(env)
|
144
140
|
|
145
141
|
expect(adapter.operations.size).to be(1)
|
@@ -156,7 +152,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
156
152
|
[200, {}, []]
|
157
153
|
end
|
158
154
|
|
159
|
-
middleware = described_class.new(app,
|
155
|
+
middleware = described_class.new(app, preload: true)
|
160
156
|
middleware.call(env)
|
161
157
|
|
162
158
|
expect(adapter.count(:get)).to be(1)
|
@@ -222,6 +218,44 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
222
218
|
end
|
223
219
|
end
|
224
220
|
|
221
|
+
context 'with multiple instances' do
|
222
|
+
let(:app) do
|
223
|
+
# ensure scoped for builder block, annoying...
|
224
|
+
instance = flipper
|
225
|
+
middleware = described_class
|
226
|
+
|
227
|
+
Rack::Builder.new do
|
228
|
+
use middleware, preload: %i(stats)
|
229
|
+
# Second instance should be a noop
|
230
|
+
use middleware, preload: true
|
231
|
+
|
232
|
+
map '/' do
|
233
|
+
run ->(_env) { [200, {}, []] }
|
234
|
+
end
|
235
|
+
|
236
|
+
map '/fail' do
|
237
|
+
run ->(_env) { raise 'FAIL!' }
|
238
|
+
end
|
239
|
+
end.to_app
|
240
|
+
end
|
241
|
+
|
242
|
+
def get(uri, params = {}, env = {}, &block)
|
243
|
+
silence { super(uri, params, env, &block) }
|
244
|
+
end
|
245
|
+
|
246
|
+
include_examples 'flipper middleware'
|
247
|
+
|
248
|
+
it 'does not call preload in second instance' do
|
249
|
+
expect(flipper).not_to receive(:preload_all)
|
250
|
+
|
251
|
+
output = get '/', {}, 'flipper' => flipper
|
252
|
+
|
253
|
+
expect(output).to match(/Flipper::Middleware::Memoizer appears to be running twice/)
|
254
|
+
expect(adapter.count(:get_multi)).to be(1)
|
255
|
+
expect(adapter.last(:get_multi).args).to eq([[flipper[:stats]]])
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
225
259
|
context 'when an app raises an exception' do
|
226
260
|
it 'resets memoize' do
|
227
261
|
begin
|
@@ -259,10 +293,9 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
259
293
|
context 'with Flipper setup in env' do
|
260
294
|
it 'caches getting a feature for duration of request' do
|
261
295
|
Flipper.configure do |config|
|
262
|
-
config.
|
296
|
+
config.adapter do
|
263
297
|
memory = Flipper::Adapters::Memory.new
|
264
|
-
|
265
|
-
Flipper.new(logged_adapter)
|
298
|
+
Flipper::Adapters::OperationLogger.new(memory)
|
266
299
|
end
|
267
300
|
end
|
268
301
|
Flipper.enable(:stats)
|
@@ -308,14 +341,16 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
308
341
|
end
|
309
342
|
end
|
310
343
|
|
311
|
-
context 'with
|
344
|
+
context 'with preload:true' do
|
345
|
+
let(:options) { {preload: true} }
|
346
|
+
|
312
347
|
let(:app) do
|
313
348
|
# ensure scoped for builder block, annoying...
|
314
349
|
middleware = described_class
|
350
|
+
opts = options
|
315
351
|
|
316
352
|
Rack::Builder.new do
|
317
|
-
use middleware,
|
318
|
-
unless: ->(request) { request.path.start_with?("/assets") }
|
353
|
+
use middleware, opts
|
319
354
|
|
320
355
|
map '/' do
|
321
356
|
run ->(_env) { [200, {}, []] }
|
@@ -327,18 +362,53 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
327
362
|
end.to_app
|
328
363
|
end
|
329
364
|
|
330
|
-
|
331
|
-
|
332
|
-
|
365
|
+
|
366
|
+
context 'and unless option' do
|
367
|
+
before do
|
368
|
+
options[:unless] = ->(request) { request.path.start_with?("/assets") }
|
369
|
+
end
|
370
|
+
|
371
|
+
it 'does NOT preload if request matches unless block' do
|
372
|
+
expect(flipper).to receive(:preload_all).never
|
373
|
+
get '/assets/foo.png', {}, 'flipper' => flipper
|
374
|
+
end
|
375
|
+
|
376
|
+
it 'does preload if request does NOT match unless block' do
|
377
|
+
expect(flipper).to receive(:preload_all).once
|
378
|
+
get '/some/other/path', {}, 'flipper' => flipper
|
379
|
+
end
|
333
380
|
end
|
334
381
|
|
335
|
-
|
336
|
-
|
337
|
-
|
382
|
+
context 'and if option' do
|
383
|
+
before do
|
384
|
+
options[:if] = ->(request) { !request.path.start_with?("/assets") }
|
385
|
+
end
|
386
|
+
|
387
|
+
it 'does NOT preload if request does not match if block' do
|
388
|
+
expect(flipper).to receive(:preload_all).never
|
389
|
+
get '/assets/foo.png', {}, 'flipper' => flipper
|
390
|
+
end
|
391
|
+
|
392
|
+
it 'does preload if request matches if block' do
|
393
|
+
expect(flipper).to receive(:preload_all).once
|
394
|
+
get '/some/other/path', {}, 'flipper' => flipper
|
395
|
+
end
|
338
396
|
end
|
339
397
|
end
|
340
398
|
|
341
|
-
context 'with
|
399
|
+
context 'with preload:true and caching adapter' do
|
400
|
+
let(:app) do
|
401
|
+
app = lambda do |_env|
|
402
|
+
flipper[:stats].enabled?
|
403
|
+
flipper[:stats].enabled?
|
404
|
+
flipper[:shiny].enabled?
|
405
|
+
flipper[:shiny].enabled?
|
406
|
+
[200, {}, []]
|
407
|
+
end
|
408
|
+
|
409
|
+
described_class.new(app, preload: true)
|
410
|
+
end
|
411
|
+
|
342
412
|
it 'eagerly caches known features for duration of request' do
|
343
413
|
memory = Flipper::Adapters::Memory.new
|
344
414
|
logged_memory = Flipper::Adapters::OperationLogger.new(memory)
|
@@ -355,25 +425,15 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
355
425
|
logged_memory.reset
|
356
426
|
logged_cached.reset
|
357
427
|
|
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)
|
428
|
+
get '/', {}, 'flipper' => flipper
|
369
429
|
expect(logged_cached.count(:get_all)).to be(1)
|
370
430
|
expect(logged_memory.count(:get_all)).to be(1)
|
371
431
|
|
372
|
-
|
432
|
+
get '/', {}, 'flipper' => flipper
|
373
433
|
expect(logged_cached.count(:get_all)).to be(2)
|
374
434
|
expect(logged_memory.count(:get_all)).to be(1)
|
375
435
|
|
376
|
-
|
436
|
+
get '/', {}, 'flipper' => flipper
|
377
437
|
expect(logged_cached.count(:get_all)).to be(3)
|
378
438
|
expect(logged_memory.count(:get_all)).to be(1)
|
379
439
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'rails'
|
3
|
+
require 'flipper/railtie'
|
4
|
+
|
5
|
+
RSpec.describe Flipper::Railtie do
|
6
|
+
let(:application) do
|
7
|
+
app = Class.new(Rails::Application).new(
|
8
|
+
railties: [Flipper::Railtie],
|
9
|
+
ordered_railties: [Flipper::Railtie]
|
10
|
+
)
|
11
|
+
app.config.eager_load = false
|
12
|
+
app.config.logger = ActiveSupport::Logger.new($stdout)
|
13
|
+
app.run_load_hooks!
|
14
|
+
end
|
15
|
+
|
16
|
+
before do
|
17
|
+
Rails.application = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
subject do
|
21
|
+
application.initialize!
|
22
|
+
application
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'initializers' do
|
26
|
+
it 'sets defaults' do
|
27
|
+
expect(application.config.flipper.env_key).to eq("flipper")
|
28
|
+
expect(application.config.flipper.memoize).to be(true)
|
29
|
+
expect(application.config.flipper.preload).to be(true)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "configures instrumentor on default instance" do
|
33
|
+
subject
|
34
|
+
|
35
|
+
expect(Flipper.instance.instrumenter).to eq(ActiveSupport::Notifications)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'uses Memoizer middleware if config.memoize = true' do
|
39
|
+
expect(subject.middleware.last).to eq(Flipper::Middleware::Memoizer)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does not use Memoizer middleware if config.memoize = false' do
|
43
|
+
# load but don't initialize
|
44
|
+
application.config.flipper.memoize = false
|
45
|
+
|
46
|
+
expect(subject.middleware.last).not_to eq(Flipper::Middleware::Memoizer)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'passes config to memoizer' do
|
50
|
+
# load but don't initialize
|
51
|
+
application.config.flipper.update(
|
52
|
+
env_key: 'my_flipper',
|
53
|
+
preload: [:stats, :search]
|
54
|
+
)
|
55
|
+
|
56
|
+
expect(Flipper::Middleware::Memoizer).to receive(:new).with(application.routes,
|
57
|
+
env_key: 'my_flipper', preload: [:stats, :search], if: nil
|
58
|
+
)
|
59
|
+
|
60
|
+
subject # initialize
|
61
|
+
end
|
62
|
+
|
63
|
+
it "defines #flipper_id on AR::Base" do
|
64
|
+
subject
|
65
|
+
require 'active_record'
|
66
|
+
expect(ActiveRecord::Base.ancestors).to include(Flipper::Identifier)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/spec/helper.rb
CHANGED
@@ -14,8 +14,8 @@ require 'webmock/rspec'
|
|
14
14
|
WebMock.disable_net_connect!(allow_localhost: true)
|
15
15
|
|
16
16
|
require 'flipper'
|
17
|
-
require 'flipper
|
18
|
-
require 'flipper
|
17
|
+
require 'flipper/ui'
|
18
|
+
require 'flipper/api'
|
19
19
|
|
20
20
|
Dir[FlipperRoot.join('spec/support/**/*.rb')].sort.each { |f| require f }
|
21
21
|
|
@@ -61,6 +61,23 @@ module SpecHelpers
|
|
61
61
|
def with_modified_env(options, &block)
|
62
62
|
ClimateControl.modify(options, &block)
|
63
63
|
end
|
64
|
+
|
65
|
+
def silence
|
66
|
+
# Store the original stderr and stdout in order to restore them later
|
67
|
+
original_stderr = $stderr
|
68
|
+
original_stdout = $stdout
|
69
|
+
|
70
|
+
# Redirect stderr and stdout
|
71
|
+
output = $stderr = $stdout = StringIO.new
|
72
|
+
|
73
|
+
yield
|
74
|
+
|
75
|
+
$stderr = original_stderr
|
76
|
+
$stdout = original_stdout
|
77
|
+
|
78
|
+
# Return output
|
79
|
+
output.string
|
80
|
+
end
|
64
81
|
end
|
65
82
|
|
66
83
|
RSpec.configure do |config|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.22.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -19,6 +19,7 @@ extra_rdoc_files: []
|
|
19
19
|
files:
|
20
20
|
- ".codeclimate.yml"
|
21
21
|
- ".github/workflows/ci.yml"
|
22
|
+
- ".github/workflows/examples.yml"
|
22
23
|
- CODE_OF_CONDUCT.md
|
23
24
|
- Changelog.md
|
24
25
|
- Dockerfile
|
@@ -37,6 +38,9 @@ files:
|
|
37
38
|
- docs/http/README.md
|
38
39
|
- docs/images/banner.jpg
|
39
40
|
- docs/read-only/README.md
|
41
|
+
- examples/api/basic.ru
|
42
|
+
- examples/api/custom_memoized.ru
|
43
|
+
- examples/api/memoized.ru
|
40
44
|
- examples/basic.rb
|
41
45
|
- examples/configuring_default.rb
|
42
46
|
- examples/dsl.rb
|
@@ -47,6 +51,7 @@ files:
|
|
47
51
|
- examples/importing.rb
|
48
52
|
- examples/individual_actor.rb
|
49
53
|
- examples/instrumentation.rb
|
54
|
+
- examples/instrumentation_last_accessed_at.rb
|
50
55
|
- examples/memoizing.rb
|
51
56
|
- examples/percentage_of_actors.rb
|
52
57
|
- examples/percentage_of_actors_enabled_check.rb
|
@@ -92,6 +97,7 @@ files:
|
|
92
97
|
- lib/flipper/metadata.rb
|
93
98
|
- lib/flipper/middleware/memoizer.rb
|
94
99
|
- lib/flipper/middleware/setup_env.rb
|
100
|
+
- lib/flipper/railtie.rb
|
95
101
|
- lib/flipper/registry.rb
|
96
102
|
- lib/flipper/spec/shared_adapter_specs.rb
|
97
103
|
- lib/flipper/test/shared_adapter_test.rb
|
@@ -137,6 +143,7 @@ files:
|
|
137
143
|
- spec/flipper/instrumenters/noop_spec.rb
|
138
144
|
- spec/flipper/middleware/memoizer_spec.rb
|
139
145
|
- spec/flipper/middleware/setup_env_spec.rb
|
146
|
+
- spec/flipper/railtie_spec.rb
|
140
147
|
- spec/flipper/registry_spec.rb
|
141
148
|
- spec/flipper/typecast_spec.rb
|
142
149
|
- spec/flipper/types/actor_spec.rb
|
@@ -171,9 +178,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
171
178
|
version: '0'
|
172
179
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
180
|
requirements:
|
174
|
-
- - "
|
181
|
+
- - ">="
|
175
182
|
- !ruby/object:Gem::Version
|
176
|
-
version:
|
183
|
+
version: '0'
|
177
184
|
requirements: []
|
178
185
|
rubygems_version: 3.0.3
|
179
186
|
signing_key:
|
@@ -213,6 +220,7 @@ test_files:
|
|
213
220
|
- spec/flipper/instrumenters/noop_spec.rb
|
214
221
|
- spec/flipper/middleware/memoizer_spec.rb
|
215
222
|
- spec/flipper/middleware/setup_env_spec.rb
|
223
|
+
- spec/flipper/railtie_spec.rb
|
216
224
|
- spec/flipper/registry_spec.rb
|
217
225
|
- spec/flipper/typecast_spec.rb
|
218
226
|
- spec/flipper/types/actor_spec.rb
|