flipper 0.11.0.beta9 → 0.11.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/Gemfile +1 -1
- data/examples/importing.rb +1 -9
- data/lib/flipper/adapters/memoizable.rb +31 -18
- data/lib/flipper/adapters/memory.rb +45 -15
- data/lib/flipper/adapters/pstore.rb +85 -43
- data/lib/flipper/adapters/read_only.rb +8 -0
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/memoizable_spec.rb +56 -13
- data/spec/flipper/middleware/memoizer_spec.rb +45 -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: 7fe7a64b6dfb6cbefeb2f111e3ea777018016ff5
|
4
|
+
data.tar.gz: 3c8e8754b21fdc37f9f90772ca893a0e16931f33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63863f70f3b8499f2d6f802eb6cfe20e19c84fad799c8b27d92de31b0ffba968f623b2dad4c387b9fe55c974d83a82f58253e8d683badac9ab9a4c084cafc216
|
7
|
+
data.tar.gz: 27f0085cfd6865a80dcd2722f81e2b516d3cd67b9702999fb90737cce263f6dd2afe5e013cbf470a6522447e70df7748f692b28cb1b7ca3a2e2ccf7e4cea4e42
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
@@ -13,7 +13,7 @@ gem 'statsd-ruby', '~> 1.2.1'
|
|
13
13
|
gem 'rspec', '~> 3.0'
|
14
14
|
gem 'rack-test', '~> 0.6.3'
|
15
15
|
gem 'sqlite3', '~> 1.3.11'
|
16
|
-
gem 'rails', "~> #{ENV['RAILS_VERSION'] || '
|
16
|
+
gem 'rails', "~> #{ENV['RAILS_VERSION'] || '5.1.4'}"
|
17
17
|
gem 'minitest', '~> 5.8.0'
|
18
18
|
gem 'rubocop', '~> 0.45.0'
|
19
19
|
gem 'rubocop-rspec', '= 1.5.1'
|
data/examples/importing.rb
CHANGED
@@ -1,17 +1,9 @@
|
|
1
1
|
require File.expand_path('../example_setup', __FILE__)
|
2
|
-
|
2
|
+
require_relative 'active_record/ar_setup'
|
3
3
|
require 'flipper'
|
4
4
|
require 'flipper/adapters/redis'
|
5
5
|
require 'flipper/adapters/active_record'
|
6
6
|
|
7
|
-
# Active Record boiler plate, feel free to ignore.
|
8
|
-
ActiveRecord::Base.establish_connection({
|
9
|
-
adapter: 'sqlite3',
|
10
|
-
database: ':memory:',
|
11
|
-
})
|
12
|
-
require 'generators/flipper/templates/migration'
|
13
|
-
CreateFlipperTables.up
|
14
|
-
|
15
7
|
# Say you are using redis...
|
16
8
|
redis_adapter = Flipper::Adapters::Redis.new(Redis.new)
|
17
9
|
redis_flipper = Flipper.new(redis_adapter)
|
@@ -9,6 +9,7 @@ module Flipper
|
|
9
9
|
include ::Flipper::Adapter
|
10
10
|
|
11
11
|
FeaturesKey = :flipper_features
|
12
|
+
GetAllKey = :all_memoized
|
12
13
|
|
13
14
|
# Internal
|
14
15
|
attr_reader :cache
|
@@ -19,6 +20,11 @@ module Flipper
|
|
19
20
|
# Internal: The adapter this adapter is wrapping.
|
20
21
|
attr_reader :adapter
|
21
22
|
|
23
|
+
# Private
|
24
|
+
def self.key_for(key)
|
25
|
+
"feature/#{key}"
|
26
|
+
end
|
27
|
+
|
22
28
|
# Public
|
23
29
|
def initialize(adapter, cache = nil)
|
24
30
|
super(adapter)
|
@@ -26,7 +32,6 @@ module Flipper
|
|
26
32
|
@name = :memoizable
|
27
33
|
@cache = cache || {}
|
28
34
|
@memoize = false
|
29
|
-
@all_fetched = false
|
30
35
|
end
|
31
36
|
|
32
37
|
# Public
|
@@ -63,7 +68,7 @@ module Flipper
|
|
63
68
|
# Public
|
64
69
|
def get(feature)
|
65
70
|
if memoizing?
|
66
|
-
cache.fetch(feature.key) { cache[feature.key] = @adapter.get(feature) }
|
71
|
+
cache.fetch(key_for(feature.key)) { cache[key_for(feature.key)] = @adapter.get(feature) }
|
67
72
|
else
|
68
73
|
@adapter.get(feature)
|
69
74
|
end
|
@@ -72,18 +77,18 @@ module Flipper
|
|
72
77
|
# Public
|
73
78
|
def get_multi(features)
|
74
79
|
if memoizing?
|
75
|
-
uncached_features = features.reject { |feature| cache[feature.key] }
|
80
|
+
uncached_features = features.reject { |feature| cache[key_for(feature.key)] }
|
76
81
|
|
77
82
|
if uncached_features.any?
|
78
83
|
response = @adapter.get_multi(uncached_features)
|
79
84
|
response.each do |key, hash|
|
80
|
-
cache[key] = hash
|
85
|
+
cache[key_for(key)] = hash
|
81
86
|
end
|
82
87
|
end
|
83
88
|
|
84
89
|
result = {}
|
85
90
|
features.each do |feature|
|
86
|
-
result[feature.key] = cache[feature.key]
|
91
|
+
result[feature.key] = cache[key_for(feature.key)]
|
87
92
|
end
|
88
93
|
result
|
89
94
|
else
|
@@ -93,20 +98,25 @@ module Flipper
|
|
93
98
|
|
94
99
|
def get_all
|
95
100
|
if memoizing?
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
101
|
+
response = nil
|
102
|
+
if cache[GetAllKey]
|
103
|
+
response = {}
|
104
|
+
cache[FeaturesKey].each do |key|
|
105
|
+
response[key] = cache[key_for(key)]
|
100
106
|
end
|
101
|
-
|
102
|
-
|
107
|
+
else
|
108
|
+
response = @adapter.get_all
|
109
|
+
response.each do |key, value|
|
110
|
+
cache[key_for(key)] = value
|
111
|
+
end
|
112
|
+
cache[FeaturesKey] = response.keys.to_set
|
113
|
+
cache[GetAllKey] = true
|
103
114
|
end
|
104
115
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
result
|
116
|
+
# Ensures that looking up other features that do not exist doesn't
|
117
|
+
# result in N+1 adapter calls.
|
118
|
+
response.default_proc = ->(memo, key) { memo[key] = default_config }
|
119
|
+
response
|
110
120
|
else
|
111
121
|
@adapter.get_all
|
112
122
|
end
|
@@ -130,7 +140,6 @@ module Flipper
|
|
130
140
|
#
|
131
141
|
# value - The Boolean that decides if local caching is on.
|
132
142
|
def memoize=(value)
|
133
|
-
@all_fetched = false
|
134
143
|
cache.clear
|
135
144
|
@memoize = value
|
136
145
|
end
|
@@ -142,8 +151,12 @@ module Flipper
|
|
142
151
|
|
143
152
|
private
|
144
153
|
|
154
|
+
def key_for(key)
|
155
|
+
self.class.key_for(key)
|
156
|
+
end
|
157
|
+
|
145
158
|
def expire_feature(feature)
|
146
|
-
cache.delete(feature.key) if memoizing?
|
159
|
+
cache.delete(key_for(feature.key)) if memoizing?
|
147
160
|
end
|
148
161
|
|
149
162
|
def expire_features_set
|
@@ -7,7 +7,7 @@ module Flipper
|
|
7
7
|
class Memory
|
8
8
|
include ::Flipper::Adapter
|
9
9
|
|
10
|
-
FeaturesKey = :
|
10
|
+
FeaturesKey = :features
|
11
11
|
|
12
12
|
# Public: The name of the adapter.
|
13
13
|
attr_reader :name
|
@@ -20,7 +20,7 @@ module Flipper
|
|
20
20
|
|
21
21
|
# Public: The set of known features.
|
22
22
|
def features
|
23
|
-
|
23
|
+
read_feature_keys
|
24
24
|
end
|
25
25
|
|
26
26
|
# Public: Adds a feature to the set of known features.
|
@@ -47,21 +47,19 @@ module Flipper
|
|
47
47
|
|
48
48
|
# Public
|
49
49
|
def get(feature)
|
50
|
-
|
50
|
+
read_feature(feature)
|
51
|
+
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
else
|
60
|
-
raise "#{gate} is not supported by this adapter yet"
|
61
|
-
end
|
53
|
+
def get_multi(features)
|
54
|
+
read_many_features(features)
|
55
|
+
end
|
56
|
+
|
57
|
+
def get_all
|
58
|
+
features = read_feature_keys.map do |key|
|
59
|
+
Flipper::Feature.new(key, self)
|
62
60
|
end
|
63
61
|
|
64
|
-
|
62
|
+
read_many_features(features)
|
65
63
|
end
|
66
64
|
|
67
65
|
# Public
|
@@ -103,9 +101,41 @@ module Flipper
|
|
103
101
|
"#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
|
104
102
|
end
|
105
103
|
|
104
|
+
private
|
105
|
+
|
106
|
+
def read_feature_keys
|
107
|
+
set_members(FeaturesKey)
|
108
|
+
end
|
109
|
+
|
106
110
|
# Private
|
107
111
|
def key(feature, gate)
|
108
|
-
"
|
112
|
+
"feature/#{feature.key}/#{gate.key}"
|
113
|
+
end
|
114
|
+
|
115
|
+
def read_many_features(features)
|
116
|
+
result = {}
|
117
|
+
features.each do |feature|
|
118
|
+
result[feature.key] = read_feature(feature)
|
119
|
+
end
|
120
|
+
result
|
121
|
+
end
|
122
|
+
|
123
|
+
def read_feature(feature)
|
124
|
+
result = {}
|
125
|
+
|
126
|
+
feature.gates.each do |gate|
|
127
|
+
result[gate.key] =
|
128
|
+
case gate.data_type
|
129
|
+
when :boolean, :integer
|
130
|
+
read key(feature, gate)
|
131
|
+
when :set
|
132
|
+
set_members key(feature, gate)
|
133
|
+
else
|
134
|
+
raise "#{gate} is not supported by this adapter yet"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
result
|
109
139
|
end
|
110
140
|
|
111
141
|
# Private
|
@@ -25,59 +25,68 @@ module Flipper
|
|
25
25
|
|
26
26
|
# Public: The set of known features.
|
27
27
|
def features
|
28
|
-
|
28
|
+
@store.transaction do
|
29
|
+
read_feature_keys
|
30
|
+
end
|
29
31
|
end
|
30
32
|
|
31
33
|
# Public: Adds a feature to the set of known features.
|
32
34
|
def add(feature)
|
33
|
-
|
35
|
+
@store.transaction do
|
36
|
+
set_add FeaturesKey, feature.key
|
37
|
+
end
|
34
38
|
true
|
35
39
|
end
|
36
40
|
|
37
41
|
# Public: Removes a feature from the set of known features and clears
|
38
42
|
# all the values for the feature.
|
39
43
|
def remove(feature)
|
40
|
-
|
41
|
-
|
44
|
+
@store.transaction do
|
45
|
+
set_delete FeaturesKey, feature.key
|
46
|
+
clear_gates(feature)
|
47
|
+
end
|
42
48
|
true
|
43
49
|
end
|
44
50
|
|
45
51
|
# Public: Clears all the gate values for a feature.
|
46
52
|
def clear(feature)
|
47
|
-
|
48
|
-
|
53
|
+
@store.transaction do
|
54
|
+
clear_gates(feature)
|
49
55
|
end
|
50
56
|
true
|
51
57
|
end
|
52
58
|
|
53
59
|
# Public
|
54
60
|
def get(feature)
|
55
|
-
|
61
|
+
@store.transaction do
|
62
|
+
result_for_feature(feature)
|
63
|
+
end
|
64
|
+
end
|
56
65
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
when :boolean, :integer
|
61
|
-
read key(feature, gate)
|
62
|
-
when :set
|
63
|
-
set_members key(feature, gate)
|
64
|
-
else
|
65
|
-
raise "#{gate} is not supported by this adapter yet"
|
66
|
-
end
|
66
|
+
def get_multi(features)
|
67
|
+
@store.transaction do
|
68
|
+
read_many_features(features)
|
67
69
|
end
|
70
|
+
end
|
68
71
|
|
69
|
-
|
72
|
+
def get_all
|
73
|
+
@store.transaction do
|
74
|
+
features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) }
|
75
|
+
read_many_features(features)
|
76
|
+
end
|
70
77
|
end
|
71
78
|
|
72
79
|
# Public
|
73
80
|
def enable(feature, gate, thing)
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
+
@store.transaction do
|
82
|
+
case gate.data_type
|
83
|
+
when :boolean, :integer
|
84
|
+
write key(feature, gate), thing.value.to_s
|
85
|
+
when :set
|
86
|
+
set_add key(feature, gate), thing.value.to_s
|
87
|
+
else
|
88
|
+
raise "#{gate} is not supported by this adapter yet"
|
89
|
+
end
|
81
90
|
end
|
82
91
|
|
83
92
|
true
|
@@ -89,9 +98,13 @@ module Flipper
|
|
89
98
|
when :boolean
|
90
99
|
clear(feature)
|
91
100
|
when :integer
|
92
|
-
|
101
|
+
@store.transaction do
|
102
|
+
write key(feature, gate), thing.value.to_s
|
103
|
+
end
|
93
104
|
when :set
|
94
|
-
|
105
|
+
@store.transaction do
|
106
|
+
set_delete key(feature, gate), thing.value.to_s
|
107
|
+
end
|
95
108
|
else
|
96
109
|
raise "#{gate} is not supported by this adapter yet"
|
97
110
|
end
|
@@ -111,6 +124,42 @@ module Flipper
|
|
111
124
|
|
112
125
|
private
|
113
126
|
|
127
|
+
def clear_gates(feature)
|
128
|
+
feature.gates.each do |gate|
|
129
|
+
delete key(feature, gate)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def read_feature_keys
|
134
|
+
set_members FeaturesKey
|
135
|
+
end
|
136
|
+
|
137
|
+
def read_many_features(features)
|
138
|
+
result = {}
|
139
|
+
features.each do |feature|
|
140
|
+
result[feature.key] = result_for_feature(feature)
|
141
|
+
end
|
142
|
+
result
|
143
|
+
end
|
144
|
+
|
145
|
+
def result_for_feature(feature)
|
146
|
+
result = {}
|
147
|
+
|
148
|
+
feature.gates.each do |gate|
|
149
|
+
result[gate.key] =
|
150
|
+
case gate.data_type
|
151
|
+
when :boolean, :integer
|
152
|
+
read key(feature, gate)
|
153
|
+
when :set
|
154
|
+
set_members key(feature, gate)
|
155
|
+
else
|
156
|
+
raise "#{gate} is not supported by this adapter yet"
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
result
|
161
|
+
end
|
162
|
+
|
114
163
|
# Private
|
115
164
|
def key(feature, gate)
|
116
165
|
"#{feature.key}/#{gate.key}"
|
@@ -118,23 +167,17 @@ module Flipper
|
|
118
167
|
|
119
168
|
# Private
|
120
169
|
def read(key)
|
121
|
-
@store.
|
122
|
-
@store[key.to_s]
|
123
|
-
end
|
170
|
+
@store[key.to_s]
|
124
171
|
end
|
125
172
|
|
126
173
|
# Private
|
127
174
|
def write(key, value)
|
128
|
-
@store.
|
129
|
-
@store[key.to_s] = value.to_s
|
130
|
-
end
|
175
|
+
@store[key.to_s] = value.to_s
|
131
176
|
end
|
132
177
|
|
133
178
|
# Private
|
134
179
|
def delete(key)
|
135
|
-
@store.
|
136
|
-
@store.delete(key.to_s)
|
137
|
-
end
|
180
|
+
@store.delete(key.to_s)
|
138
181
|
end
|
139
182
|
|
140
183
|
# Private
|
@@ -154,14 +197,13 @@ module Flipper
|
|
154
197
|
# Private
|
155
198
|
def set_members(key)
|
156
199
|
key = key.to_s
|
157
|
-
@store.transaction do
|
158
|
-
@store[key] ||= Set.new
|
159
200
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
201
|
+
@store[key] ||= Set.new
|
202
|
+
|
203
|
+
if block_given?
|
204
|
+
yield @store[key]
|
205
|
+
else
|
206
|
+
@store[key]
|
165
207
|
end
|
166
208
|
end
|
167
209
|
end
|
data/lib/flipper/version.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'flipper/adapters/memoizable'
|
3
3
|
require 'flipper/adapters/memory'
|
4
|
+
require 'flipper/adapters/operation_logger'
|
4
5
|
require 'flipper/spec/shared_adapter_specs'
|
5
6
|
|
6
7
|
RSpec.describe Flipper::Adapters::Memoizable do
|
@@ -66,7 +67,7 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
66
67
|
it 'memoizes feature' do
|
67
68
|
feature = flipper[:stats]
|
68
69
|
result = subject.get(feature)
|
69
|
-
expect(cache[feature.key]).to be(result)
|
70
|
+
expect(cache[described_class.key_for(feature.key)]).to be(result)
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
@@ -95,8 +96,8 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
95
96
|
features = names.map { |name| flipper[name] }
|
96
97
|
results = subject.get_multi(features)
|
97
98
|
features.each do |feature|
|
98
|
-
expect(cache[feature.key]).not_to be(nil)
|
99
|
-
expect(cache[feature.key]).to be(results[feature.key])
|
99
|
+
expect(cache[described_class.key_for(feature.key)]).not_to be(nil)
|
100
|
+
expect(cache[described_class.key_for(feature.key)]).to be(results[feature.key])
|
100
101
|
end
|
101
102
|
end
|
102
103
|
end
|
@@ -127,11 +128,32 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
127
128
|
features = names.map { |name| flipper[name].tap(&:enable) }
|
128
129
|
results = subject.get_all
|
129
130
|
features.each do |feature|
|
130
|
-
expect(cache[feature.key]).not_to be(nil)
|
131
|
-
expect(cache[feature.key]).to be(results[feature.key])
|
131
|
+
expect(cache[described_class.key_for(feature.key)]).not_to be(nil)
|
132
|
+
expect(cache[described_class.key_for(feature.key)]).to be(results[feature.key])
|
132
133
|
end
|
133
134
|
expect(cache[subject.class::FeaturesKey]).to eq(names.map(&:to_s).to_set)
|
134
135
|
end
|
136
|
+
|
137
|
+
it 'only calls get_all once for memoized adapter' do
|
138
|
+
adapter = Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new)
|
139
|
+
cache = {}
|
140
|
+
instance = described_class.new(adapter, cache)
|
141
|
+
instance.memoize = true
|
142
|
+
|
143
|
+
instance.get_all
|
144
|
+
expect(adapter.count(:get_all)).to be(1)
|
145
|
+
|
146
|
+
instance.get_all
|
147
|
+
expect(adapter.count(:get_all)).to be(1)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'returns default_config for unknown feature keys' do
|
151
|
+
first = subject.get_all
|
152
|
+
expect(first['doesntexist']).to eq(subject.default_config)
|
153
|
+
|
154
|
+
second = subject.get_all
|
155
|
+
expect(second['doesntexist']).to eq(subject.default_config)
|
156
|
+
end
|
135
157
|
end
|
136
158
|
|
137
159
|
context "with memoization disabled" do
|
@@ -146,6 +168,27 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
146
168
|
adapter_result = adapter.get_all
|
147
169
|
expect(result).to eq(adapter_result)
|
148
170
|
end
|
171
|
+
|
172
|
+
it 'calls get_all every time for memoized adapter' do
|
173
|
+
adapter = Flipper::Adapters::OperationLogger.new(Flipper::Adapters::Memory.new)
|
174
|
+
cache = {}
|
175
|
+
instance = described_class.new(adapter, cache)
|
176
|
+
instance.memoize = false
|
177
|
+
|
178
|
+
instance.get_all
|
179
|
+
expect(adapter.count(:get_all)).to be(1)
|
180
|
+
|
181
|
+
instance.get_all
|
182
|
+
expect(adapter.count(:get_all)).to be(2)
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'returns nil for unknown feature keys' do
|
186
|
+
first = subject.get_all
|
187
|
+
expect(first['doesntexist']).to be(nil)
|
188
|
+
|
189
|
+
second = subject.get_all
|
190
|
+
expect(second['doesntexist']).to be(nil)
|
191
|
+
end
|
149
192
|
end
|
150
193
|
end
|
151
194
|
|
@@ -158,9 +201,9 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
158
201
|
it 'unmemoizes feature' do
|
159
202
|
feature = flipper[:stats]
|
160
203
|
gate = feature.gate(:boolean)
|
161
|
-
cache[feature.key] = { some: 'thing' }
|
204
|
+
cache[described_class.key_for(feature.key)] = { some: 'thing' }
|
162
205
|
subject.enable(feature, gate, flipper.bool)
|
163
|
-
expect(cache[feature.key]).to be_nil
|
206
|
+
expect(cache[described_class.key_for(feature.key)]).to be_nil
|
164
207
|
end
|
165
208
|
end
|
166
209
|
|
@@ -188,9 +231,9 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
188
231
|
it 'unmemoizes feature' do
|
189
232
|
feature = flipper[:stats]
|
190
233
|
gate = feature.gate(:boolean)
|
191
|
-
cache[feature.key] = { some: 'thing' }
|
234
|
+
cache[described_class.key_for(feature.key)] = { some: 'thing' }
|
192
235
|
subject.disable(feature, gate, flipper.bool)
|
193
|
-
expect(cache[feature.key]).to be_nil
|
236
|
+
expect(cache[described_class.key_for(feature.key)]).to be_nil
|
194
237
|
end
|
195
238
|
end
|
196
239
|
|
@@ -272,9 +315,9 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
272
315
|
|
273
316
|
it 'unmemoizes the feature' do
|
274
317
|
feature = flipper[:stats]
|
275
|
-
cache[feature.key] = { some: 'thing' }
|
318
|
+
cache[described_class.key_for(feature.key)] = { some: 'thing' }
|
276
319
|
subject.remove(feature)
|
277
|
-
expect(cache[feature.key]).to be_nil
|
320
|
+
expect(cache[described_class.key_for(feature.key)]).to be_nil
|
278
321
|
end
|
279
322
|
end
|
280
323
|
|
@@ -297,9 +340,9 @@ RSpec.describe Flipper::Adapters::Memoizable do
|
|
297
340
|
|
298
341
|
it 'unmemoizes feature' do
|
299
342
|
feature = flipper[:stats]
|
300
|
-
cache[feature.key] = { some: 'thing' }
|
343
|
+
cache[described_class.key_for(feature.key)] = { some: 'thing' }
|
301
344
|
subject.clear(feature)
|
302
|
-
expect(cache[feature.key]).to be_nil
|
345
|
+
expect(cache[described_class.key_for(feature.key)]).to be_nil
|
303
346
|
end
|
304
347
|
end
|
305
348
|
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'rack/test'
|
3
|
+
require 'active_support/cache'
|
4
|
+
require 'active_support/cache/dalli_store'
|
3
5
|
require 'flipper/middleware/memoizer'
|
6
|
+
require 'flipper/adapters/active_support_cache_store'
|
4
7
|
require 'flipper/adapters/operation_logger'
|
5
8
|
require 'flipper/adapters/memory'
|
6
9
|
|
@@ -141,6 +144,7 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
141
144
|
middleware = described_class.new(app, preload_all: true)
|
142
145
|
middleware.call(env)
|
143
146
|
|
147
|
+
expect(adapter.operations.size).to be(1)
|
144
148
|
expect(adapter.count(:get_all)).to be(1)
|
145
149
|
end
|
146
150
|
|
@@ -335,4 +339,45 @@ RSpec.describe Flipper::Middleware::Memoizer do
|
|
335
339
|
get '/some/other/path', {}, 'flipper' => flipper
|
336
340
|
end
|
337
341
|
end
|
342
|
+
|
343
|
+
context 'with preload_all and caching adapter' do
|
344
|
+
it 'eagerly caches known features for duration of request' do
|
345
|
+
memory = Flipper::Adapters::Memory.new
|
346
|
+
logged_memory = Flipper::Adapters::OperationLogger.new(memory)
|
347
|
+
cache = ActiveSupport::Cache::DalliStore.new
|
348
|
+
cache.clear
|
349
|
+
cached = Flipper::Adapters::ActiveSupportCacheStore.new(logged_memory, cache, expires_in: 10)
|
350
|
+
logged_cached = Flipper::Adapters::OperationLogger.new(cached)
|
351
|
+
memo = {}
|
352
|
+
flipper = Flipper.new(logged_cached)
|
353
|
+
flipper[:stats].enable
|
354
|
+
flipper[:shiny].enable
|
355
|
+
|
356
|
+
# clear the log of operations
|
357
|
+
logged_memory.reset
|
358
|
+
logged_cached.reset
|
359
|
+
|
360
|
+
app = lambda do |_env|
|
361
|
+
flipper[:stats].enabled?
|
362
|
+
flipper[:stats].enabled?
|
363
|
+
flipper[:shiny].enabled?
|
364
|
+
flipper[:shiny].enabled?
|
365
|
+
[200, {}, []]
|
366
|
+
end
|
367
|
+
|
368
|
+
middleware = described_class.new(app, preload_all: true)
|
369
|
+
|
370
|
+
middleware.call('flipper' => flipper)
|
371
|
+
expect(logged_cached.count(:get_all)).to be(1)
|
372
|
+
expect(logged_memory.count(:get_all)).to be(1)
|
373
|
+
|
374
|
+
middleware.call('flipper' => flipper)
|
375
|
+
expect(logged_cached.count(:get_all)).to be(2)
|
376
|
+
expect(logged_memory.count(:get_all)).to be(1)
|
377
|
+
|
378
|
+
middleware.call('flipper' => flipper)
|
379
|
+
expect(logged_cached.count(:get_all)).to be(3)
|
380
|
+
expect(logged_memory.count(:get_all)).to be(1)
|
381
|
+
end
|
382
|
+
end
|
338
383
|
end
|
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.rc1
|
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-11-04 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
|