flipper 0.11.0.beta9 → 0.11.0.rc1
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.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
|