flipper 0.11.0.beta3 → 0.11.0.beta4
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/.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
|