flipper-active_support_cache_store 1.2.2 → 1.3.0.pre

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 528dc13fd274c8753ac7df38a790bfe405fec333d614967cbc9925144e4c985e
4
- data.tar.gz: e3f7b3b9d27ecceea9ef6fd28afe0f492e36d46df5da560caef458bc3c7b38d7
3
+ metadata.gz: bbb7450e9a2de26da717b62bffb2f49b7dbb7bd8afd845630793d0d9b140360e
4
+ data.tar.gz: c5497eef3c7643330268af315b027e31e6b7021849c03f503eb8410684f64160
5
5
  SHA512:
6
- metadata.gz: 5cb25bc61e007c9cb9d8676d725fbe5d2a781218dafe522e2d980a5370a7a5147d2a75c5f85190b11b287d2c183b4b23882c30af1353cf4034609ae8a998483c
7
- data.tar.gz: c7154a8b1d0b8c68abbe5f2d90d1ae8effd0835407d4fcba420fd137c0d526f9525e2b3ad9441505a6b74e8048fe8ff2108df8d08ef8ba8d9ecfa4a0e68c130e
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.2'.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.2
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-19 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.2
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.2
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.2
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: