flipper-cache-store 0.11.0.beta7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/docs/cache_store/README.md +99 -0
- data/lib/flipper/adapters/cache_store.rb +104 -0
- data/lib/flipper/version.rb +3 -0
- data/spec/flipper/adapters/cache_store_spec.rb +50 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e05826a8bee645823d83c2fc1497001a21061cb3
|
4
|
+
data.tar.gz: 4b57d8c399fe0025e17ba36dc668f76fa78b77a1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6b90ec6e64f7aeb62fef75403f52ba9c823ee3d67b9a46cdfd74a2430b2f2effba63dc343392c91348b7338dbbcb3cbd5db8bda479a2f2181224c8f0731e8c4c
|
7
|
+
data.tar.gz: b7b502a2905d855a4a79d060e12b96e60ebc282494ede5aa5281953f13e3b6c9181cbe52bb43964fe3e58bcad283ed42376dd749e451e94f945531c1b70f9cd9
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# Flipper CacheStore
|
2
|
+
|
3
|
+
A [CacheStore](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html) adapter for [Flipper](https://github.com/jnunemaker/flipper).
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'flipper-cache-store'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself with:
|
16
|
+
|
17
|
+
$ gem install flipper-cache-store
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require 'active_support/cache'
|
23
|
+
require 'flipper/adapters/memory'
|
24
|
+
require 'flipper/adapters/cache_store'
|
25
|
+
|
26
|
+
memory_adapter = Flipper::Adapters::Memory.new
|
27
|
+
cache = ActiveSupport::Cache::MemoryStore.new
|
28
|
+
adapter = Flipper::Adapters::CacheStore.new(memory_adapter, cache, expires_in: 5.minutes)
|
29
|
+
flipper = Flipper.new(adapter)
|
30
|
+
```
|
31
|
+
Setting `expires_in` is optional and will set an expiration time on Flipper cache keys. If specified, all flipper keys will use this `expires_in` over the `expires_in` passed to your ActiveSupport cache constructor.
|
32
|
+
|
33
|
+
## Internals
|
34
|
+
|
35
|
+
Each feature is stored in the underlying cache store.
|
36
|
+
|
37
|
+
This is an example using `ActiveSupport::Cache::MemoryStore` with the [Flipper memory adapter](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/memory.rb).
|
38
|
+
|
39
|
+
Each key is namespaced under `flipper/v1/feature/`
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
require 'active_support/cache'
|
43
|
+
require 'flipper/adapters/memory'
|
44
|
+
require 'flipper/adapters/cache_store'
|
45
|
+
|
46
|
+
memory_adapter = Flipper::Adapters::Memory.new
|
47
|
+
cache = ActiveSupport::Cache::MemoryStore.new
|
48
|
+
adapter = Flipper::Adapters::CacheStore.new(memory_adapter, cache)
|
49
|
+
flipper = Flipper.new(adapter)
|
50
|
+
|
51
|
+
# Register a few groups.
|
52
|
+
Flipper.register(:admins) { |thing| thing.admin? }
|
53
|
+
Flipper.register(:early_access) { |thing| thing.early_access? }
|
54
|
+
|
55
|
+
# Create a user class that has flipper_id instance method.
|
56
|
+
User = Struct.new(:flipper_id)
|
57
|
+
|
58
|
+
flipper[:stats].enable
|
59
|
+
flipper[:stats].enable_group :admins
|
60
|
+
flipper[:stats].enable_group :early_access
|
61
|
+
flipper[:stats].enable_actor User.new('25')
|
62
|
+
flipper[:stats].enable_actor User.new('90')
|
63
|
+
flipper[:stats].enable_actor User.new('180')
|
64
|
+
flipper[:stats].enable_percentage_of_time 15
|
65
|
+
flipper[:stats].enable_percentage_of_actors 45
|
66
|
+
flipper[:search].enable
|
67
|
+
|
68
|
+
# reading all feature keys
|
69
|
+
pp cache.read("flipper/v1/features")
|
70
|
+
#<Set: {"stats", "search"}>
|
71
|
+
|
72
|
+
# reading a single feature
|
73
|
+
pp cache.read("flipper/v1/feature/stats")
|
74
|
+
{
|
75
|
+
:boolean=>"true",
|
76
|
+
:groups=>#<Set: {"admins", "early_access"}>,
|
77
|
+
:actors=>#<Set: {"25", "90", "180"}>,
|
78
|
+
:percentage_of_actors=>"45",
|
79
|
+
:percentage_of_time=>"15"
|
80
|
+
}
|
81
|
+
|
82
|
+
# flipper get of feature
|
83
|
+
pp adapter.get(flipper[:stats])
|
84
|
+
{
|
85
|
+
:boolean=>"true",
|
86
|
+
:groups=>#<Set: {"admins", "early_access"}>,
|
87
|
+
:actors=>#<Set: {"25", "90", "180"}>,
|
88
|
+
:percentage_of_actors=>"45",
|
89
|
+
:percentage_of_time=>"15"
|
90
|
+
}
|
91
|
+
```
|
92
|
+
|
93
|
+
## Contributing
|
94
|
+
|
95
|
+
1. Fork it
|
96
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
97
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
98
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
99
|
+
5. Create new Pull Request
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Flipper
|
2
|
+
module Adapters
|
3
|
+
# Public: Adapter that wraps another adapter with the ability to cache
|
4
|
+
# adapter calls in ActiveSupport::CacheStore caches.
|
5
|
+
#
|
6
|
+
class CacheStore
|
7
|
+
include ::Flipper::Adapter
|
8
|
+
|
9
|
+
Version = 'v1'.freeze
|
10
|
+
Namespace = "flipper/#{Version}".freeze
|
11
|
+
FeaturesKey = "#{Namespace}/features".freeze
|
12
|
+
|
13
|
+
# Private
|
14
|
+
def self.key_for(key)
|
15
|
+
"#{Namespace}/feature/#{key}"
|
16
|
+
end
|
17
|
+
|
18
|
+
# Internal
|
19
|
+
attr_reader :cache
|
20
|
+
|
21
|
+
# Public: The name of the adapter.
|
22
|
+
attr_reader :name
|
23
|
+
|
24
|
+
# Public
|
25
|
+
def initialize(adapter, cache, expires_in: nil)
|
26
|
+
@adapter = adapter
|
27
|
+
@name = :cache_store
|
28
|
+
@cache = cache
|
29
|
+
@write_options = {}
|
30
|
+
@write_options.merge!(expires_in: expires_in) if expires_in
|
31
|
+
end
|
32
|
+
|
33
|
+
# Public
|
34
|
+
def features
|
35
|
+
@cache.fetch(FeaturesKey, @write_options) do
|
36
|
+
@adapter.features
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Public
|
41
|
+
def add(feature)
|
42
|
+
result = @adapter.add(feature)
|
43
|
+
@cache.delete(FeaturesKey)
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
## Public
|
48
|
+
def remove(feature)
|
49
|
+
result = @adapter.remove(feature)
|
50
|
+
@cache.delete(FeaturesKey)
|
51
|
+
@cache.delete(key_for(feature.key))
|
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
|
+
keys = features.map { |feature| key_for(feature.key) }
|
71
|
+
result = @cache.read_multi(keys)
|
72
|
+
uncached_features = features.reject { |feature| result[feature.key] }
|
73
|
+
if uncached_features.any?
|
74
|
+
response = @adapter.get_multi(uncached_features)
|
75
|
+
response.each do |key, value|
|
76
|
+
@cache.write(key_for(key), value, @write_options)
|
77
|
+
result[key] = value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
result
|
81
|
+
end
|
82
|
+
|
83
|
+
## Public
|
84
|
+
def enable(feature, gate, thing)
|
85
|
+
result = @adapter.enable(feature, gate, thing)
|
86
|
+
@cache.delete(key_for(feature.key))
|
87
|
+
result
|
88
|
+
end
|
89
|
+
|
90
|
+
## Public
|
91
|
+
def disable(feature, gate, thing)
|
92
|
+
result = @adapter.disable(feature, gate, thing)
|
93
|
+
@cache.delete(key_for(feature.key))
|
94
|
+
result
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def key_for(key)
|
100
|
+
self.class.key_for(key)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'active_support/cache'
|
3
|
+
require 'flipper/adapters/memory'
|
4
|
+
require 'flipper/adapters/cache_store'
|
5
|
+
require 'flipper/spec/shared_adapter_specs'
|
6
|
+
|
7
|
+
RSpec.describe Flipper::Adapters::CacheStore do
|
8
|
+
let(:memory_adapter) { Flipper::Adapters::Memory.new }
|
9
|
+
let(:cache) { ActiveSupport::Cache::MemoryStore.new }
|
10
|
+
let(:adapter) { described_class.new(memory_adapter, cache) }
|
11
|
+
let(:flipper) { Flipper.new(adapter) }
|
12
|
+
|
13
|
+
subject { adapter }
|
14
|
+
|
15
|
+
it_should_behave_like 'a flipper adapter'
|
16
|
+
|
17
|
+
describe '#remove' do
|
18
|
+
it 'expires feature' do
|
19
|
+
feature = flipper[:stats]
|
20
|
+
adapter.get(feature)
|
21
|
+
adapter.remove(feature)
|
22
|
+
expect(cache.read(described_class.key_for(feature))).to be(nil)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#get_multi' do
|
27
|
+
it 'warms uncached features' do
|
28
|
+
stats = flipper[:stats]
|
29
|
+
search = flipper[:search]
|
30
|
+
other = flipper[:other]
|
31
|
+
stats.enable
|
32
|
+
search.enable
|
33
|
+
|
34
|
+
adapter.get(stats)
|
35
|
+
expect(cache.read(described_class.key_for(search))).to be(nil)
|
36
|
+
expect(cache.read(described_class.key_for(other))).to be(nil)
|
37
|
+
|
38
|
+
adapter.get_multi([stats, search, other])
|
39
|
+
|
40
|
+
expect(cache.read(described_class.key_for(search))[:boolean]).to eq('true')
|
41
|
+
expect(cache.read(described_class.key_for(other))[:boolean]).to be(nil)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#name' do
|
46
|
+
it 'is cache_store' do
|
47
|
+
expect(subject.name).to be(:cache_store)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flipper-cache-store
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.11.0.beta7
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Nunemaker
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-07-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: flipper
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.11.0.beta7
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.11.0.beta7
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.2'
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '6'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '3.2'
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '6'
|
47
|
+
description: ActiveSupport::Cache::Store adapter for Flipper
|
48
|
+
email:
|
49
|
+
- nunemaker@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- docs/cache_store/README.md
|
55
|
+
- lib/flipper/adapters/cache_store.rb
|
56
|
+
- lib/flipper/version.rb
|
57
|
+
- spec/flipper/adapters/cache_store_spec.rb
|
58
|
+
homepage: https://github.com/jnunemaker/flipper
|
59
|
+
licenses:
|
60
|
+
- MIT
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.3.1
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 2.5.2
|
79
|
+
signing_key:
|
80
|
+
specification_version: 4
|
81
|
+
summary: ActiveSupport::Cache::Store adapter for Flipper
|
82
|
+
test_files:
|
83
|
+
- spec/flipper/adapters/cache_store_spec.rb
|