flipper 0.11.0.beta3 → 0.11.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +10 -0
- data/Changelog.md +5 -0
- data/Gemfile +1 -0
- data/docker-compose.yml +3 -3
- data/docs/Adapters.md +1 -0
- data/docs/DockerCompose.md +1 -1
- data/docs/Optimization.md +7 -1
- data/lib/flipper/adapter.rb +8 -0
- data/lib/flipper/adapters/http.rb +19 -0
- data/lib/flipper/adapters/instrumented.rb +24 -1
- data/lib/flipper/adapters/memoizable.rb +23 -0
- data/lib/flipper/adapters/operation_logger.rb +7 -0
- data/lib/flipper/dsl.rb +8 -0
- data/lib/flipper/middleware/memoizer.rb +20 -5
- data/lib/flipper/spec/shared_adapter_specs.rb +18 -2
- data/lib/flipper/test/shared_adapter_test.rb +17 -1
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapter_spec.rb +2 -2
- data/spec/flipper/adapters/http_spec.rb +12 -0
- data/spec/flipper/adapters/instrumented_spec.rb +14 -0
- data/spec/flipper/adapters/memoizable_spec.rb +34 -1
- data/spec/flipper/dsl_spec.rb +36 -0
- data/spec/flipper/middleware/memoizer_spec.rb +31 -3
- data/spec/helper.rb +1 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: baf85e722be59174189c5871316c40cb1109c507
|
4
|
+
data.tar.gz: a05626a7197b77e0ef609a21233d24aeac5d639d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b0f2bbae8bc3a80afa52ee58563cbc5e284a9c0093268e535dd6b7f6cd67b9868690eae86d2b9a93efc01e5f0d4b6f23b2667ecd688f4fc35fcd4d121ab96a2
|
7
|
+
data.tar.gz: 785f3114118eaa592f7c012319fcdacd34d0bb21ca342ca2ba197a622ab32f6de1808df4a3faf9a846b0b47b1b0e881532f6372e5ac1576d77e6742267dc2406
|
data/.rubocop_todo.yml
CHANGED
@@ -186,3 +186,13 @@ Style/IfInsideElse:
|
|
186
186
|
Style/MethodMissing:
|
187
187
|
Exclude:
|
188
188
|
- 'lib/flipper/types/actor.rb'
|
189
|
+
|
190
|
+
Style/AccessorMethodName:
|
191
|
+
Exclude:
|
192
|
+
- 'lib/flipper/adapter.rb'
|
193
|
+
- 'lib/flipper/adapters/http.rb'
|
194
|
+
- 'lib/flipper/adapters/instrumented.rb'
|
195
|
+
- 'lib/flipper/adapters/memoizable.rb'
|
196
|
+
- 'lib/flipper/adapters/operation_logger.rb'
|
197
|
+
- 'lib/flipper/adapters/memory.rb'
|
198
|
+
- 'lib/flipper/adapters/pstore.rb'
|
data/Changelog.md
CHANGED
@@ -13,6 +13,11 @@
|
|
13
13
|
* Finish API and HTTP adapter that speaks to API.
|
14
14
|
* Add flipper cloud adapter (https://github.com/jnunemaker/flipper/pull/249). Nothing to see here yet, but good stuff soon. ;)
|
15
15
|
* Add importing (https://github.com/jnunemaker/flipper/pull/251).
|
16
|
+
* Added Adapter#get_all to allow for more efficient preload_all (https://github.com/jnunemaker/flipper/pull/255).
|
17
|
+
* Added :unless option to Flipper::Middleware::Memoizer to allow skipping memoization and preloading for certain requests.
|
18
|
+
* Made it possible to instrument Flipper::Cloud (https://github.com/jnunemaker/flipper/commit/4b10e4d807772202f63881f5e2c00d11ac58481f).
|
19
|
+
* Made it possible to wrap Http adapter when using Flipper::Cloud (https://github.com/jnunemaker/flipper/commit/4b10e4d807772202f63881f5e2c00d11ac58481f).
|
20
|
+
* Instrument get_multi in instrumented adapter (https://github.com/jnunemaker/flipper/commit/951d25c5ce07d3b56b0b2337adf5f6bcbe4050e7).
|
16
21
|
|
17
22
|
## 0.10.2
|
18
23
|
|
data/Gemfile
CHANGED
data/docker-compose.yml
CHANGED
@@ -24,9 +24,9 @@ app:
|
|
24
24
|
- mongo
|
25
25
|
- memcached
|
26
26
|
environment:
|
27
|
-
-
|
28
|
-
-
|
29
|
-
-
|
27
|
+
- REDIS_URL=redis://redis:6379
|
28
|
+
- MONGODB_HOST=mongo
|
29
|
+
- MEMCACHED_URL=memcached:11211
|
30
30
|
bundle_cache:
|
31
31
|
container_name: flipper_bundle_cache
|
32
32
|
image: busybox
|
data/docs/Adapters.md
CHANGED
@@ -31,6 +31,7 @@ The basic API for an adapter is this:
|
|
31
31
|
* `enable(feature, gate, thing)` - Enable a gate for a thing.
|
32
32
|
* `disable(feature, gate, thing)` - Disable a gate for a thing.
|
33
33
|
* `get_multi(features)` - Get all gate values for several features at once. Implementation is optional. If none provided, default implementation performs N+1 `get` calls where N is the number of elements in the features parameter.
|
34
|
+
* `get_all` - Get all gate values for all features at once. Implementation is optional. If none provided, default implementation performs two calls, one to `features` to get the names of all features and one to `get_multi` with the feature names from the first call.
|
34
35
|
|
35
36
|
If you would like to make your own adapter, there are shared adapter specs (RSpec) and tests (MiniTest) that you can use to verify that you have everything working correctly.
|
36
37
|
|
data/docs/DockerCompose.md
CHANGED
@@ -12,7 +12,7 @@ new contributor could start working on code with a minumum efforts.
|
|
12
12
|
1. Run specs `docker-compose run --rm app bundle exec rspec`
|
13
13
|
1. Run tests `docker-compose run --rm app bundle exec rake test`
|
14
14
|
1. Clear and check files with Rubocop `docker-compose run --rm app bundle exec rubocop -D`
|
15
|
-
1. Optional: log in to container an using a bash for running specs
|
15
|
+
1. Optional: log in to container an using a `bash` shell for running specs
|
16
16
|
```sh
|
17
17
|
docker-compose run --rm app bash
|
18
18
|
bundle exec rspec
|
data/docs/Optimization.md
CHANGED
@@ -48,11 +48,17 @@ The Memoizer middleware also supports a few options. Use either `preload` or `pr
|
|
48
48
|
config.middleware.use Flipper::Middleware::Memoizer,
|
49
49
|
preload: [:stats, :search, :some_feature]
|
50
50
|
```
|
51
|
-
* **`:preload_all`** - A Boolean value (default: false) of whether or not all features should be preloaded. Using this results in a `
|
51
|
+
* **`:preload_all`** - A Boolean value (default: false) of whether or not all features should be preloaded. Using this results in a `preload_all` call with the result of `Adapter#get_all`. Any subsequent feature checks will be memoized and perform no network calls. I wouldn't recommend using this unless you have few features (< 100?) and nearly all of them are used on every request.
|
52
52
|
```ruby
|
53
53
|
config.middleware.use Flipper::Middleware::Memoizer,
|
54
54
|
preload_all: true
|
55
55
|
```
|
56
|
+
* **`:unless`** - A block that prevents preloading and memoization if it evaluates to true.
|
57
|
+
```ruby
|
58
|
+
# skip preloading and memoizing if path starts with /assets
|
59
|
+
config.middleware.use Flipper::Middleware::Memoizer,
|
60
|
+
unless: ->(request) { request.path.start_with?("/assets") }
|
61
|
+
```
|
56
62
|
|
57
63
|
## Cache Adapters
|
58
64
|
|
data/lib/flipper/adapter.rb
CHANGED
@@ -18,6 +18,14 @@ module Flipper
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
+
# Public: Get all features and gate values in one call. Defaults to one call
|
22
|
+
# to features and another to get_multi. Feel free to override per adapter to
|
23
|
+
# make this more efficient.
|
24
|
+
def get_all
|
25
|
+
instances = features.map { |key| Flipper::Feature.new(key, self) }
|
26
|
+
get_multi(instances)
|
27
|
+
end
|
28
|
+
|
21
29
|
# Public: Get multiple features in one call. Defaults to one get per
|
22
30
|
# feature. Feel free to override per adapter to make this more efficient and
|
23
31
|
# reduce network calls.
|
@@ -59,6 +59,25 @@ module Flipper
|
|
59
59
|
result
|
60
60
|
end
|
61
61
|
|
62
|
+
def get_all
|
63
|
+
response = @client.get("/features")
|
64
|
+
raise Error, response unless response.is_a?(Net::HTTPOK)
|
65
|
+
|
66
|
+
parsed_response = JSON.parse(response.body)
|
67
|
+
parsed_features = parsed_response.fetch('features')
|
68
|
+
gates_by_key = parsed_features.each_with_object({}) do |parsed_feature, hash|
|
69
|
+
hash[parsed_feature['key']] = parsed_feature['gates']
|
70
|
+
hash
|
71
|
+
end
|
72
|
+
|
73
|
+
result = {}
|
74
|
+
gates_by_key.keys.each do |key|
|
75
|
+
feature = Feature.new(key, self)
|
76
|
+
result[feature.key] = result_for_feature(feature, gates_by_key[feature.key])
|
77
|
+
end
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
62
81
|
def features
|
63
82
|
response = @client.get('/features')
|
64
83
|
raise Error, response unless response.is_a?(Net::HTTPOK)
|
@@ -4,7 +4,7 @@ require 'flipper/instrumenters/noop'
|
|
4
4
|
module Flipper
|
5
5
|
module Adapters
|
6
6
|
# Internal: Adapter that wraps another adapter and instruments all adapter
|
7
|
-
# operations.
|
7
|
+
# operations.
|
8
8
|
class Instrumented < SimpleDelegator
|
9
9
|
include ::Flipper::Adapter
|
10
10
|
|
@@ -95,6 +95,29 @@ module Flipper
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
+
def get_multi(features)
|
99
|
+
payload = {
|
100
|
+
operation: :get_multi,
|
101
|
+
adapter_name: @adapter.name,
|
102
|
+
feature_names: features.map(&:name),
|
103
|
+
}
|
104
|
+
|
105
|
+
@instrumenter.instrument(InstrumentationName, payload) do |payload|
|
106
|
+
payload[:result] = @adapter.get_multi(features)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_all
|
111
|
+
payload = {
|
112
|
+
operation: :get_all,
|
113
|
+
adapter_name: @adapter.name,
|
114
|
+
}
|
115
|
+
|
116
|
+
@instrumenter.instrument(InstrumentationName, payload) do |payload|
|
117
|
+
payload[:result] = @adapter.get_all
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
98
121
|
# Public
|
99
122
|
def enable(feature, gate, thing)
|
100
123
|
payload = {
|
@@ -26,6 +26,7 @@ module Flipper
|
|
26
26
|
@name = :memoizable
|
27
27
|
@cache = cache || {}
|
28
28
|
@memoize = false
|
29
|
+
@all_fetched = false
|
29
30
|
end
|
30
31
|
|
31
32
|
# Public
|
@@ -90,6 +91,27 @@ module Flipper
|
|
90
91
|
end
|
91
92
|
end
|
92
93
|
|
94
|
+
def get_all
|
95
|
+
if memoizing?
|
96
|
+
unless @all_fetched
|
97
|
+
result = @adapter.get_all
|
98
|
+
result.each do |key, value|
|
99
|
+
cache[key] = value
|
100
|
+
end
|
101
|
+
cache[FeaturesKey] = result.keys.to_set
|
102
|
+
@all_fetched = true
|
103
|
+
end
|
104
|
+
|
105
|
+
result = {}
|
106
|
+
features.each do |key|
|
107
|
+
result[key] = cache[key]
|
108
|
+
end
|
109
|
+
result
|
110
|
+
else
|
111
|
+
@adapter.get_all
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
93
115
|
# Public
|
94
116
|
def enable(feature, gate, thing)
|
95
117
|
result = @adapter.enable(feature, gate, thing)
|
@@ -108,6 +130,7 @@ module Flipper
|
|
108
130
|
#
|
109
131
|
# value - The Boolean that decides if local caching is on.
|
110
132
|
def memoize=(value)
|
133
|
+
@all_fetched = false
|
111
134
|
cache.clear
|
112
135
|
@memoize = value
|
113
136
|
end
|
@@ -17,6 +17,7 @@ module Flipper
|
|
17
17
|
:clear,
|
18
18
|
:get,
|
19
19
|
:get_multi,
|
20
|
+
:get_all,
|
20
21
|
:enable,
|
21
22
|
:disable,
|
22
23
|
].freeze
|
@@ -72,6 +73,12 @@ module Flipper
|
|
72
73
|
@adapter.get_multi(features)
|
73
74
|
end
|
74
75
|
|
76
|
+
# Public
|
77
|
+
def get_all
|
78
|
+
@operations << Operation.new(:get_all, [])
|
79
|
+
@adapter.get_all
|
80
|
+
end
|
81
|
+
|
75
82
|
# Public
|
76
83
|
def enable(feature, gate, thing)
|
77
84
|
@operations << Operation.new(:enable, [feature, gate, thing])
|
data/lib/flipper/dsl.rb
CHANGED
@@ -181,6 +181,14 @@ module Flipper
|
|
181
181
|
features
|
182
182
|
end
|
183
183
|
|
184
|
+
# Public: Preload all the adapters features.
|
185
|
+
#
|
186
|
+
# Returns an Array of Flipper::Feature.
|
187
|
+
def preload_all
|
188
|
+
keys = @adapter.get_all.keys
|
189
|
+
keys.map { |key| feature(key) }
|
190
|
+
end
|
191
|
+
|
184
192
|
# Public: Shortcut access to a feature instance by name.
|
185
193
|
#
|
186
194
|
# name - The String or Symbol name of the feature.
|
@@ -32,16 +32,31 @@ module Flipper
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def call(env)
|
35
|
+
request = Rack::Request.new(env)
|
36
|
+
|
37
|
+
if skip_memoize?(request)
|
38
|
+
@app.call(env)
|
39
|
+
else
|
40
|
+
memoized_call(env)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def skip_memoize?(request)
|
47
|
+
@opts[:unless] && @opts[:unless].call(request)
|
48
|
+
end
|
49
|
+
|
50
|
+
def memoized_call(env)
|
35
51
|
flipper = env.fetch('flipper')
|
36
52
|
original = flipper.adapter.memoizing?
|
37
53
|
flipper.adapter.memoize = true
|
38
54
|
|
39
|
-
if @opts[:preload_all]
|
40
|
-
names = flipper.features.map(&:name)
|
41
|
-
flipper.preload(names)
|
42
|
-
end
|
55
|
+
flipper.preload_all if @opts[:preload_all]
|
43
56
|
|
44
|
-
|
57
|
+
if (preload = @opts[:preload])
|
58
|
+
flipper.preload(preload)
|
59
|
+
end
|
45
60
|
|
46
61
|
response = @app.call(env)
|
47
62
|
response[2] = Rack::BodyProxy.new(response[2]) do
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# Requires the following methods:
|
2
2
|
# * subject - The instance of the adapter
|
3
|
+
# rubocop:disable Metrics/BlockLength
|
3
4
|
RSpec.shared_examples_for 'a flipper adapter' do
|
4
5
|
let(:flipper) { Flipper.new(subject) }
|
5
6
|
let(:feature) { flipper[:stats] }
|
@@ -231,15 +232,30 @@ RSpec.shared_examples_for 'a flipper adapter' do
|
|
231
232
|
it 'can get multiple features' do
|
232
233
|
expect(subject.add(flipper[:stats])).to eq(true)
|
233
234
|
expect(subject.enable(flipper[:stats], boolean_gate, flipper.boolean)).to eq(true)
|
234
|
-
|
235
235
|
expect(subject.add(flipper[:search])).to eq(true)
|
236
236
|
|
237
237
|
result = subject.get_multi([flipper[:stats], flipper[:search], flipper[:other]])
|
238
238
|
expect(result).to be_instance_of(Hash)
|
239
239
|
|
240
|
-
stats
|
240
|
+
stats = result["stats"]
|
241
|
+
search = result["search"]
|
242
|
+
other = result["other"]
|
241
243
|
expect(stats).to eq(subject.default_config.merge(boolean: 'true'))
|
242
244
|
expect(search).to eq(subject.default_config)
|
243
245
|
expect(other).to eq(subject.default_config)
|
244
246
|
end
|
247
|
+
|
248
|
+
it 'can get all features' do
|
249
|
+
expect(subject.add(flipper[:stats])).to eq(true)
|
250
|
+
expect(subject.enable(flipper[:stats], boolean_gate, flipper.boolean)).to eq(true)
|
251
|
+
expect(subject.add(flipper[:search])).to eq(true)
|
252
|
+
|
253
|
+
result = subject.get_all
|
254
|
+
expect(result).to be_instance_of(Hash)
|
255
|
+
|
256
|
+
stats = result["stats"]
|
257
|
+
search = result["search"]
|
258
|
+
expect(stats).to eq(subject.default_config.merge(boolean: 'true'))
|
259
|
+
expect(search).to eq(subject.default_config)
|
260
|
+
end
|
245
261
|
end
|
@@ -232,11 +232,27 @@ module Flipper
|
|
232
232
|
result = @adapter.get_multi([@flipper[:stats], @flipper[:search], @flipper[:other]])
|
233
233
|
assert_instance_of Hash, result
|
234
234
|
|
235
|
-
stats
|
235
|
+
stats = result["stats"]
|
236
|
+
search = result["search"]
|
237
|
+
other = result["other"]
|
236
238
|
assert_equal @adapter.default_config.merge(boolean: 'true'), stats
|
237
239
|
assert_equal @adapter.default_config, search
|
238
240
|
assert_equal @adapter.default_config, other
|
239
241
|
end
|
242
|
+
|
243
|
+
def test_can_get_all_features
|
244
|
+
assert @adapter.add(@flipper[:stats])
|
245
|
+
assert @adapter.enable(@flipper[:stats], @boolean_gate, @flipper.boolean)
|
246
|
+
assert @adapter.add(@flipper[:search])
|
247
|
+
|
248
|
+
result = @adapter.get_all
|
249
|
+
assert_instance_of Hash, result
|
250
|
+
|
251
|
+
stats = result["stats"]
|
252
|
+
search = result["search"]
|
253
|
+
assert_equal @adapter.default_config.merge(boolean: 'true'), stats
|
254
|
+
assert_equal @adapter.default_config, search
|
255
|
+
end
|
240
256
|
end
|
241
257
|
end
|
242
258
|
end
|
data/lib/flipper/version.rb
CHANGED
@@ -17,7 +17,7 @@ RSpec.describe Flipper::Adapter do
|
|
17
17
|
describe '.default_config' do
|
18
18
|
it 'returns default config' do
|
19
19
|
adapter_class = Class.new do
|
20
|
-
include
|
20
|
+
include Flipper::Adapter # rubocop:disable RSpec/DescribedClass
|
21
21
|
end
|
22
22
|
expect(adapter_class.default_config).to eq(default_config)
|
23
23
|
end
|
@@ -26,7 +26,7 @@ RSpec.describe Flipper::Adapter do
|
|
26
26
|
describe '#default_config' do
|
27
27
|
it 'returns default config' do
|
28
28
|
adapter_class = Class.new do
|
29
|
-
include
|
29
|
+
include Flipper::Adapter # rubocop:disable RSpec/DescribedClass
|
30
30
|
end
|
31
31
|
expect(adapter_class.new.default_config).to eq(default_config)
|
32
32
|
end
|
@@ -94,6 +94,18 @@ RSpec.describe Flipper::Adapters::Http do
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
+
describe "#get_all" do
|
98
|
+
it "raises error when not successful response" do
|
99
|
+
stub_request(:get, "http://app.com/flipper/features")
|
100
|
+
.to_return(status: 503, body: "", headers: {})
|
101
|
+
|
102
|
+
adapter = described_class.new(uri: URI('http://app.com/flipper'))
|
103
|
+
expect do
|
104
|
+
adapter.get_all
|
105
|
+
end.to raise_error(Flipper::Adapters::Http::Error)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
97
109
|
describe "#features" do
|
98
110
|
it "raises error when not successful response" do
|
99
111
|
stub_request(:get, "http://app.com/flipper/features")
|
@@ -49,6 +49,20 @@ RSpec.describe Flipper::Adapters::Instrumented do
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
+
describe '#get_multi' do
|
53
|
+
it 'records instrumentation' do
|
54
|
+
result = subject.get_multi([feature])
|
55
|
+
|
56
|
+
event = instrumenter.events.last
|
57
|
+
expect(event).not_to be_nil
|
58
|
+
expect(event.name).to eq('adapter_operation.flipper')
|
59
|
+
expect(event.payload[:operation]).to eq(:get_multi)
|
60
|
+
expect(event.payload[:adapter_name]).to eq(:memory)
|
61
|
+
expect(event.payload[:feature_names]).to eq([:stats])
|
62
|
+
expect(event.payload[:result]).to be(result)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
52
66
|
describe '#enable' do
|
53
67
|
it 'records instrumentation' do
|
54
68
|
result = subject.enable(feature, gate, thing)
|
@@ -90,7 +90,7 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
90
90
|
subject.memoize = true
|
91
91
|
end
|
92
92
|
|
93
|
-
it 'memoizes
|
93
|
+
it 'memoizes features' do
|
94
94
|
names = %i(stats shiny)
|
95
95
|
features = names.map { |name| flipper[name] }
|
96
96
|
results = subject.get_multi(features)
|
@@ -116,6 +116,39 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
+
describe '#get_all' do
|
120
|
+
context "with memoization enabled" do
|
121
|
+
before do
|
122
|
+
subject.memoize = true
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'memoizes features' do
|
126
|
+
names = %i(stats shiny)
|
127
|
+
features = names.map { |name| flipper[name].tap(&:enable) }
|
128
|
+
results = subject.get_all
|
129
|
+
features.each do |feature|
|
130
|
+
expect(cache[feature.key]).not_to be(nil)
|
131
|
+
expect(cache[feature.key]).to be(results[feature.key])
|
132
|
+
end
|
133
|
+
expect(cache[subject.class::FeaturesKey]).to eq(names.map(&:to_s).to_set)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "with memoization disabled" do
|
138
|
+
before do
|
139
|
+
subject.memoize = false
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'returns result' do
|
143
|
+
names = %i(stats shiny)
|
144
|
+
names.map { |name| flipper[name].tap(&:enable) }
|
145
|
+
result = subject.get_all
|
146
|
+
adapter_result = adapter.get_all
|
147
|
+
expect(result).to eq(adapter_result)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
119
152
|
describe '#enable' do
|
120
153
|
context 'with memoization enabled' do
|
121
154
|
before do
|
data/spec/flipper/dsl_spec.rb
CHANGED
@@ -70,6 +70,42 @@ RSpec.describe Flipper::DSL do
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
+
describe '#preload_all' do
|
74
|
+
let(:instrumenter) { double('Instrumentor', instrument: nil) }
|
75
|
+
let(:dsl) do
|
76
|
+
names.each { |name| adapter.add subject[name] }
|
77
|
+
described_class.new(adapter, instrumenter: instrumenter)
|
78
|
+
end
|
79
|
+
let(:names) { %i(stats shiny) }
|
80
|
+
let(:features) { dsl.preload_all }
|
81
|
+
|
82
|
+
it 'returns array of features' do
|
83
|
+
expect(features).to all be_instance_of(Flipper::Feature)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'sets names' do
|
87
|
+
expect(features.map(&:key)).to eq(names.map(&:to_s))
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'sets adapter' do
|
91
|
+
features.each do |feature|
|
92
|
+
expect(feature.adapter.name).to eq(dsl.adapter.name)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'sets instrumenter' do
|
97
|
+
features.each do |feature|
|
98
|
+
expect(feature.instrumenter).to eq(dsl.instrumenter)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'memoizes the feature' do
|
103
|
+
features.each do |feature|
|
104
|
+
expect(dsl.feature(feature.name)).to equal(feature)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
73
109
|
describe '#[]' do
|
74
110
|
it_should_behave_like 'a DSL feature' do
|
75
111
|
let(:method_name) { :[] }
|
@@ -141,9 +141,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
141
141
|
middleware = described_class.new(app, preload_all: true)
|
142
142
|
middleware.call(env)
|
143
143
|
|
144
|
-
expect(adapter.count(:
|
145
|
-
expect(adapter.count(:get_multi)).to be(1)
|
146
|
-
expect(adapter.last(:get_multi).args).to eq([[flipper[:stats], flipper[:shiny]]])
|
144
|
+
expect(adapter.count(:get_all)).to be(1)
|
147
145
|
end
|
148
146
|
|
149
147
|
it 'caches unknown features for duration of request' do
|
@@ -255,4 +253,34 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
255
253
|
|
256
254
|
include_examples 'flipper middleware'
|
257
255
|
end
|
256
|
+
|
257
|
+
context 'with preload_all and unless option' do
|
258
|
+
let(:app) do
|
259
|
+
# ensure scoped for builder block, annoying...
|
260
|
+
middleware = described_class
|
261
|
+
|
262
|
+
Rack::Builder.new do
|
263
|
+
use middleware, preload_all: true,
|
264
|
+
unless: ->(request) { request.path.start_with?("/assets") }
|
265
|
+
|
266
|
+
map '/' do
|
267
|
+
run ->(_env) { [200, {}, []] }
|
268
|
+
end
|
269
|
+
|
270
|
+
map '/fail' do
|
271
|
+
run ->(_env) { raise 'FAIL!' }
|
272
|
+
end
|
273
|
+
end.to_app
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'does NOT preload_all if request matches unless block' do
|
277
|
+
expect(flipper).to receive(:preload_all).never
|
278
|
+
get '/assets/foo.png', {}, 'flipper' => flipper
|
279
|
+
end
|
280
|
+
|
281
|
+
it 'does preload_all if request does NOT match unless block' do
|
282
|
+
expect(flipper).to receive(:preload_all).once
|
283
|
+
get '/some/other/path', {}, 'flipper' => flipper
|
284
|
+
end
|
285
|
+
end
|
258
286
|
end
|
data/spec/helper.rb
CHANGED
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.11.0.
|
4
|
+
version: 0.11.0.beta4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Feature flipper is the act of enabling/disabling features in your application,
|
14
14
|
ideally without re-deploying or changing anything in your code base. Flipper makes
|