flipper-active_support_cache_store 1.2.1 → 1.3.0.pre

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84ad91134e2791b47d15a6527ef4ced39f777bed68062d424c0b2e3fa972d629
4
- data.tar.gz: 28b447d2647ecc8c39d9b45d8e8ee64b1092203a4678fb57b608ad4a0ea891f4
3
+ metadata.gz: bbb7450e9a2de26da717b62bffb2f49b7dbb7bd8afd845630793d0d9b140360e
4
+ data.tar.gz: c5497eef3c7643330268af315b027e31e6b7021849c03f503eb8410684f64160
5
5
  SHA512:
6
- metadata.gz: 750c13ea0264aa8eabe37862a21c3e4cc4cee317b713d3970c351c3f0de6e68552c075d01ff4bb6c4db19b504279675fab3c10de12810f9f524232941e9fb642
7
- data.tar.gz: 8f2d0dc2a57c1fba31e9940fcba931ebb26618d7679998de426ef31c7be50aec866cbcf16f06f6920641c6fff1ab36c99b82692cc666c998f523a388e90b23a0
6
+ metadata.gz: c044942a4c2a7b737fc84dcb380858f93487ef08c5662c519074915099e8cbb3a1eb2ace3fe5da4a385c977115c58175ca50a2a58a80d82e199009dd34ec2216
7
+ data.tar.gz: fe145df6098e3926b16b10e6607f4629492822cda6ac2b395960b12315961033955a52f653a9dea6b30111456bc90c358a6ce84814c978448794bdb40647ed28
@@ -1,146 +1,79 @@
1
1
  require 'flipper'
2
+ require 'flipper/adapters/cache_base'
2
3
  require 'active_support/notifications'
3
4
 
4
5
  module Flipper
5
6
  module Adapters
6
7
  # Public: Adapter that wraps another adapter with the ability to cache
7
8
  # adapter calls in ActiveSupport::ActiveSupportCacheStore caches.
8
- #
9
- class ActiveSupportCacheStore
10
- include ::Flipper::Adapter
11
-
12
- # Internal
13
- attr_reader :cache
14
-
15
- # Public
16
- def initialize(adapter, cache, expires_in: nil, write_through: false)
17
- @adapter = adapter
18
- @cache = cache
19
- @write_options = {}
20
- @write_options[:expires_in] = expires_in if expires_in
9
+ class ActiveSupportCacheStore < CacheBase
10
+ def initialize(adapter, cache, ttl = nil, expires_in: :none_provided, write_through: false, prefix: nil)
11
+ if expires_in == :none_provided
12
+ ttl ||= nil
13
+ else
14
+ warn "DEPRECATION WARNING: The `expires_in` kwarg is deprecated for " +
15
+ "Flipper::Adapters::ActiveSupportCacheStore and will be removed " +
16
+ "in the next major version. Please pass in expires in as third " +
17
+ "argument instead."
18
+ ttl = expires_in
19
+ end
20
+ super(adapter, cache, ttl, prefix: prefix)
21
21
  @write_through = write_through
22
-
23
- @cache_version = 'v1'.freeze
24
- @namespace = "flipper/#{@cache_version}".freeze
25
- @features_key = "#{@namespace}/features".freeze
26
- @get_all_key = "#{@namespace}/get_all".freeze
27
- end
28
-
29
- # Public
30
- def features
31
- read_feature_keys
32
22
  end
33
23
 
34
- # Public
35
- def add(feature)
36
- result = @adapter.add(feature)
37
- @cache.delete(@features_key)
38
- result
39
- end
40
-
41
- ## Public
42
24
  def remove(feature)
43
- result = @adapter.remove(feature)
44
- @cache.delete(@features_key)
45
-
46
25
  if @write_through
47
- @cache.write(key_for(feature.key), default_config, @write_options)
48
- else
49
- @cache.delete(key_for(feature.key))
50
- end
51
-
52
- result
53
- end
54
-
55
- ## Public
56
- def clear(feature)
57
- result = @adapter.clear(feature)
58
- @cache.delete(key_for(feature.key))
59
- result
60
- end
61
-
62
- ## Public
63
- def get(feature)
64
- @cache.fetch(key_for(feature.key), @write_options) do
65
- @adapter.get(feature)
66
- end
67
- end
68
-
69
- def get_multi(features)
70
- read_many_features(features)
71
- end
72
-
73
- def get_all
74
- if @cache.write(@get_all_key, Time.now.to_i, @write_options.merge(unless_exist: true))
75
- response = @adapter.get_all
76
- response.each do |key, value|
77
- @cache.write(key_for(key), value, @write_options)
78
- end
79
- @cache.write(@features_key, response.keys.to_set, @write_options)
80
- response
26
+ result = @adapter.remove(feature)
27
+ expire_features_cache
28
+ cache_write feature_cache_key(feature.key), default_config
29
+ result
81
30
  else
82
- features = read_feature_keys.map { |key| Flipper::Feature.new(key, self) }
83
- read_many_features(features)
31
+ super
84
32
  end
85
33
  end
86
34
 
87
- ## Public
88
35
  def enable(feature, gate, thing)
89
- result = @adapter.enable(feature, gate, thing)
90
-
91
36
  if @write_through
92
- @cache.write(key_for(feature.key), @adapter.get(feature), @write_options)
37
+ result = @adapter.enable(feature, gate, thing)
38
+ cache_write feature_cache_key(feature.key), @adapter.get(feature)
39
+ result
93
40
  else
94
- @cache.delete(key_for(feature.key))
41
+ super
95
42
  end
96
-
97
- result
98
43
  end
99
44
 
100
- ## Public
101
45
  def disable(feature, gate, thing)
102
- result = @adapter.disable(feature, gate, thing)
103
-
104
46
  if @write_through
105
- @cache.write(key_for(feature.key), @adapter.get(feature), @write_options)
47
+ result = @adapter.disable(feature, gate, thing)
48
+ cache_write feature_cache_key(feature.key), @adapter.get(feature)
49
+ result
106
50
  else
107
- @cache.delete(key_for(feature.key))
51
+ super
108
52
  end
109
-
110
- result
111
53
  end
112
54
 
113
55
  private
114
56
 
115
- def key_for(key)
116
- "#{@namespace}/feature/#{key}"
57
+ def cache_fetch(key, &block)
58
+ @cache.fetch(key, write_options, &block)
117
59
  end
118
60
 
119
- # Internal: Returns an array of the known feature keys.
120
- def read_feature_keys
121
- @cache.fetch(@features_key, @write_options) { @adapter.features }
61
+ def cache_read_multi(keys)
62
+ @cache.read_multi(*keys)
122
63
  end
123
64
 
124
- # Internal: Given an array of features, attempts to read through cache in
125
- # as few network calls as possible.
126
- def read_many_features(features)
127
- keys = features.map { |feature| key_for(feature.key) }
128
- cache_result = @cache.read_multi(*keys)
129
- uncached_features = features.reject { |feature| cache_result[key_for(feature)] }
65
+ def cache_write(key, value)
66
+ @cache.write(key, value, write_options)
67
+ end
130
68
 
131
- if uncached_features.any?
132
- response = @adapter.get_multi(uncached_features)
133
- response.each do |key, value|
134
- @cache.write(key_for(key), value, @write_options)
135
- cache_result[key_for(key)] = value
136
- end
137
- end
69
+ def cache_delete(key)
70
+ @cache.delete(key)
71
+ end
138
72
 
139
- result = {}
140
- features.each do |feature|
141
- result[feature.key] = cache_result[key_for(feature.key)]
142
- end
143
- result
73
+ def write_options
74
+ write_options = {}
75
+ write_options[:expires_in] = @ttl if @ttl
76
+ write_options
144
77
  end
145
78
  end
146
79
  end
@@ -1,5 +1,5 @@
1
1
  module Flipper
2
- VERSION = '1.2.1'.freeze
2
+ VERSION = '1.3.0.pre'.freeze
3
3
 
4
4
  REQUIRED_RUBY_VERSION = '2.6'.freeze
5
5
  NEXT_REQUIRED_RUBY_VERSION = '3.0'.freeze
@@ -8,7 +8,7 @@ RSpec.describe Flipper::Adapters::ActiveSupportCacheStore do
8
8
  end
9
9
  let(:cache) { ActiveSupport::Cache::MemoryStore.new }
10
10
  let(:write_through) { false }
11
- let(:adapter) { described_class.new(memory_adapter, cache, expires_in: 10.seconds, write_through: write_through) }
11
+ let(:adapter) { described_class.new(memory_adapter, cache, 10, write_through: write_through) }
12
12
  let(:flipper) { Flipper.new(adapter) }
13
13
 
14
14
  subject { adapter }
@@ -19,6 +19,92 @@ RSpec.describe Flipper::Adapters::ActiveSupportCacheStore do
19
19
 
20
20
  it_should_behave_like 'a flipper adapter'
21
21
 
22
+ it "knows ttl" do
23
+ expect(adapter.ttl).to eq(10)
24
+ end
25
+
26
+ it "knows ttl when only expires_in provided" do
27
+ silence do
28
+ adapter = described_class.new(memory_adapter, cache, expires_in: 10)
29
+ expect(adapter.ttl).to eq(10)
30
+ end
31
+ end
32
+
33
+ it "knows ttl when ttl and expires_in are provided" do
34
+ silence do
35
+ adapter = described_class.new(memory_adapter, cache, 200, expires_in: 10)
36
+ expect(adapter.ttl).to eq(10)
37
+ end
38
+ end
39
+
40
+ it "knows default when no ttl or expires_in provided" do
41
+ adapter = described_class.new(memory_adapter, cache)
42
+ expect(adapter.ttl).to be(nil)
43
+ end
44
+
45
+ it "knows features_cache_key" do
46
+ expect(adapter.features_cache_key).to eq("flipper/v1/features")
47
+ end
48
+
49
+ it "can expire features cache" do
50
+ # cache the features
51
+ adapter.features
52
+ expect(cache.read("flipper/v1/features")).not_to be(nil)
53
+
54
+ # expire cache
55
+ adapter.expire_features_cache
56
+ expect(cache.read("flipper/v1/features")).to be(nil)
57
+ end
58
+
59
+ it "can expire feature cache" do
60
+ # cache the features
61
+ adapter.get(flipper[:stats])
62
+ expect(cache.read("flipper/v1/feature/stats")).not_to be(nil)
63
+
64
+ # expire cache
65
+ adapter.expire_feature_cache("stats")
66
+ expect(cache.read("flipper/v1/feature/stats")).to be(nil)
67
+ end
68
+
69
+ it "can generate feature cache key" do
70
+ expect(adapter.feature_cache_key("stats")).to eq("flipper/v1/feature/stats")
71
+ end
72
+
73
+ context "when using a prefix" do
74
+ let(:adapter) { described_class.new(memory_adapter, cache, 10, prefix: "foo/") }
75
+ it_should_behave_like 'a flipper adapter'
76
+
77
+ it "knows features_cache_key" do
78
+ expect(adapter.features_cache_key).to eq("foo/flipper/v1/features")
79
+ end
80
+
81
+ it "can generate feature cache key" do
82
+ expect(adapter.feature_cache_key("stats")).to eq("foo/flipper/v1/feature/stats")
83
+ end
84
+
85
+ it "uses the prefix for all keys" do
86
+ # check individual feature get cached with prefix
87
+ adapter.get(flipper[:stats])
88
+ expect(cache.read("foo/flipper/v1/feature/stats")).not_to be(nil)
89
+
90
+ # check individual feature expired with prefix
91
+ adapter.remove(flipper[:stats])
92
+ expect(cache.read("foo/flipper/v1/feature/stats")).to be(nil)
93
+
94
+ # enable some stuff
95
+ flipper.enable_percentage_of_actors(:search, 10)
96
+ flipper.enable(:stats)
97
+
98
+ # populate the cache
99
+ adapter.get_all
100
+
101
+ # verify cached with prefix
102
+ expect(cache.read("foo/flipper/v1/features")).to eq(Set["stats", "search"])
103
+ expect(cache.read("foo/flipper/v1/feature/search")[:percentage_of_actors]).to eq("10")
104
+ expect(cache.read("foo/flipper/v1/feature/stats")[:boolean]).to eq("true")
105
+ end
106
+ end
107
+
22
108
  describe '#remove' do
23
109
  let(:feature) { flipper[:stats] }
24
110
 
@@ -130,17 +216,19 @@ RSpec.describe Flipper::Adapters::ActiveSupportCacheStore do
130
216
  adapter.get_all
131
217
  expect(cache.read("flipper/v1/feature/#{stats.key}")[:boolean]).to eq('true')
132
218
  expect(cache.read("flipper/v1/feature/#{search.key}")[:boolean]).to be(nil)
133
- expect(cache.read("flipper/v1/get_all")).to be_within(2).of(Time.now.to_i)
219
+ expect(cache.read("flipper/v1/features")).to eq(Set["stats", "search"])
134
220
  end
135
221
 
136
222
  it 'returns same result when already cached' do
137
223
  expect(adapter.get_all).to eq(adapter.get_all)
138
224
  end
139
225
 
140
- it 'only invokes one call to wrapped adapter' do
226
+ it 'only invokes two calls to wrapped adapter (for features set and gate data for each feature in set)' do
141
227
  memory_adapter.reset
142
228
  5.times { adapter.get_all }
143
- expect(memory_adapter.count(:get_all)).to eq(1)
229
+ expect(memory_adapter.count(:features)).to eq(1)
230
+ expect(memory_adapter.count(:get_multi)).to eq(1)
231
+ expect(memory_adapter.count).to eq(2)
144
232
  end
145
233
  end
146
234
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flipper-active_support_cache_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-15 00:00:00.000000000 Z
11
+ date: 2024-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: flipper
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 1.2.1
19
+ version: 1.3.0.pre
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 1.2.1
26
+ version: 1.3.0.pre
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -63,7 +63,7 @@ metadata:
63
63
  homepage_uri: https://www.flippercloud.io
64
64
  source_code_uri: https://github.com/flippercloud/flipper
65
65
  bug_tracker_uri: https://github.com/flippercloud/flipper/issues
66
- changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.2.1
66
+ changelog_uri: https://github.com/flippercloud/flipper/releases/tag/v1.3.0.pre
67
67
  post_install_message:
68
68
  rdoc_options: []
69
69
  require_paths: