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.
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.21.0.rc1'.freeze
2
+ VERSION = '0.22.1'.freeze
3
3
  end
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.default { ... }
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 adapter are wrapped in Memoizable
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 preload_all' do
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, preload_all: true
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, preload_all: true)
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, preload_all: true)
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.default do
296
+ config.adapter do
263
297
  memory = Flipper::Adapters::Memory.new
264
- logged_adapter = Flipper::Adapters::OperationLogger.new(memory)
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 preload_all and unless option' do
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, preload_all: true,
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
- it 'does NOT preload_all if request matches unless block' do
331
- expect(flipper).to receive(:preload_all).never
332
- get '/assets/foo.png', {}, 'flipper' => flipper
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
- it 'does preload_all if request does NOT match unless block' do
336
- expect(flipper).to receive(:preload_all).once
337
- get '/some/other/path', {}, 'flipper' => flipper
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 preload_all and caching adapter' do
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
- app = lambda do |_env|
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
- middleware.call('flipper' => flipper)
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
- middleware.call('flipper' => flipper)
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-ui'
18
- require 'flipper-api'
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.21.0.rc1
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-05-01 00:00:00.000000000 Z
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: 1.3.1
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