flipper-redis 0.3.0 → 0.5.0
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.
- data/Gemfile +1 -1
- data/README.md +67 -1
- data/examples/basic.rb +27 -0
- data/examples/internals.rb +71 -0
- data/flipper-redis.gemspec +9 -7
- data/lib/flipper-redis.rb +0 -1
- data/lib/flipper/adapters/redis.rb +103 -12
- data/lib/flipper/adapters/redis/version.rb +1 -1
- data/spec/flipper/redis_spec.rb +1 -22
- data/spec/helper.rb +1 -0
- metadata +24 -6
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Flipper Redis
|
2
2
|
|
3
|
-
A redis adapter for [Flipper](https://github.com/jnunemaker/flipper)
|
3
|
+
A [Redis](https://github.com/redis/redis-rb) adapter for [Flipper](https://github.com/jnunemaker/flipper).
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -26,6 +26,72 @@ flipper = Flipper.new(adapter)
|
|
26
26
|
# profit...
|
27
27
|
```
|
28
28
|
|
29
|
+
## Internals
|
30
|
+
|
31
|
+
Each feature is stored in a redis hash, which means getting a feature is single query.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
require 'flipper/adapters/redis'
|
35
|
+
require 'redis/namespace'
|
36
|
+
|
37
|
+
client = Redis.new
|
38
|
+
namespaced_client = Redis::Namespace.new(:flipper, :redis => client)
|
39
|
+
adapter = Flipper::Adapters::Redis.new(namespaced_client)
|
40
|
+
flipper = Flipper.new(adapter)
|
41
|
+
|
42
|
+
# Register a few groups.
|
43
|
+
Flipper.register(:admins) { |thing| thing.admin? }
|
44
|
+
Flipper.register(:early_access) { |thing| thing.early_access? }
|
45
|
+
|
46
|
+
# Create a user class that has flipper_id instance method.
|
47
|
+
User = Struct.new(:flipper_id)
|
48
|
+
|
49
|
+
flipper[:stats].enable
|
50
|
+
flipper[:stats].enable flipper.group(:admins)
|
51
|
+
flipper[:stats].enable flipper.group(:early_access)
|
52
|
+
flipper[:stats].enable User.new('25')
|
53
|
+
flipper[:stats].enable User.new('90')
|
54
|
+
flipper[:stats].enable User.new('180')
|
55
|
+
flipper[:stats].enable flipper.random(15)
|
56
|
+
flipper[:stats].enable flipper.actors(45)
|
57
|
+
|
58
|
+
flipper[:search].enable
|
59
|
+
|
60
|
+
print 'all keys: '
|
61
|
+
pp namespaced_client.keys
|
62
|
+
# all keys: ["stats", "flipper_features", "search"]
|
63
|
+
|
64
|
+
print "known flipper features: "
|
65
|
+
pp namespaced_client.smembers("flipper_features")
|
66
|
+
# known flipper features: ["stats", "search"]
|
67
|
+
|
68
|
+
puts 'stats keys'
|
69
|
+
pp namespaced_client.hgetall('stats')
|
70
|
+
# stats keys
|
71
|
+
# {"boolean"=>"true",
|
72
|
+
# "groups/admins"=>"1",
|
73
|
+
# "actors/25"=>"1",
|
74
|
+
# "percentage_of_random"=>"15",
|
75
|
+
# "percentage_of_actors"=>"45",
|
76
|
+
# "groups/early_access"=>"1",
|
77
|
+
# "actors/90"=>"1",
|
78
|
+
# "actors/180"=>"1"}
|
79
|
+
|
80
|
+
puts 'search keys'
|
81
|
+
pp namespaced_client.hgetall('search')
|
82
|
+
# search keys
|
83
|
+
# {"boolean"=>"true"}
|
84
|
+
|
85
|
+
puts 'flipper get of feature'
|
86
|
+
pp adapter.get(flipper[:stats])
|
87
|
+
# flipper get of feature
|
88
|
+
# {:boolean=>"true",
|
89
|
+
# :groups=>#<Set: {"admins", "early_access"}>,
|
90
|
+
# :actors=>#<Set: {"25", "90", "180"}>,
|
91
|
+
# :percentage_of_actors=>"45",
|
92
|
+
# :percentage_of_random=>"15"}
|
93
|
+
```
|
94
|
+
|
29
95
|
## Contributing
|
30
96
|
|
31
97
|
1. Fork it
|
data/examples/basic.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
root_path = Pathname(__FILE__).dirname.join('..').expand_path
|
5
|
+
lib_path = root_path.join('lib')
|
6
|
+
$:.unshift(lib_path)
|
7
|
+
|
8
|
+
require 'flipper/adapters/redis'
|
9
|
+
client = Redis.new
|
10
|
+
adapter = Flipper::Adapters::Redis.new(client)
|
11
|
+
flipper = Flipper.new(adapter)
|
12
|
+
|
13
|
+
flipper[:stats].enable
|
14
|
+
|
15
|
+
if flipper[:stats].enabled?
|
16
|
+
puts "Enabled!"
|
17
|
+
else
|
18
|
+
puts "Disabled!"
|
19
|
+
end
|
20
|
+
|
21
|
+
flipper[:stats].disable
|
22
|
+
|
23
|
+
if flipper[:stats].enabled?
|
24
|
+
puts "Enabled!"
|
25
|
+
else
|
26
|
+
puts "Disabled!"
|
27
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'pathname'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
root_path = Pathname(__FILE__).dirname.join('..').expand_path
|
6
|
+
lib_path = root_path.join('lib')
|
7
|
+
$:.unshift(lib_path)
|
8
|
+
|
9
|
+
require 'flipper/adapters/redis'
|
10
|
+
require 'redis/namespace'
|
11
|
+
|
12
|
+
client = Redis.new
|
13
|
+
namespaced_client = Redis::Namespace.new(:flipper, :redis => client)
|
14
|
+
adapter = Flipper::Adapters::Redis.new(namespaced_client)
|
15
|
+
flipper = Flipper.new(adapter)
|
16
|
+
|
17
|
+
# Register a few groups.
|
18
|
+
Flipper.register(:admins) { |thing| thing.admin? }
|
19
|
+
Flipper.register(:early_access) { |thing| thing.early_access? }
|
20
|
+
|
21
|
+
# Create a user class that has flipper_id instance method.
|
22
|
+
User = Struct.new(:flipper_id)
|
23
|
+
|
24
|
+
flipper[:stats].enable
|
25
|
+
flipper[:stats].enable flipper.group(:admins)
|
26
|
+
flipper[:stats].enable flipper.group(:early_access)
|
27
|
+
flipper[:stats].enable User.new('25')
|
28
|
+
flipper[:stats].enable User.new('90')
|
29
|
+
flipper[:stats].enable User.new('180')
|
30
|
+
flipper[:stats].enable flipper.random(15)
|
31
|
+
flipper[:stats].enable flipper.actors(45)
|
32
|
+
|
33
|
+
flipper[:search].enable
|
34
|
+
|
35
|
+
print 'all keys: '
|
36
|
+
pp namespaced_client.keys
|
37
|
+
# all keys: ["stats", "flipper_features", "search"]
|
38
|
+
puts
|
39
|
+
|
40
|
+
print "known flipper features: "
|
41
|
+
pp namespaced_client.smembers("flipper_features")
|
42
|
+
# known flipper features: ["stats", "search"]
|
43
|
+
puts
|
44
|
+
|
45
|
+
puts 'stats keys'
|
46
|
+
pp namespaced_client.hgetall('stats')
|
47
|
+
# stats keys
|
48
|
+
# {"boolean"=>"true",
|
49
|
+
# "groups/admins"=>"1",
|
50
|
+
# "actors/25"=>"1",
|
51
|
+
# "percentage_of_random"=>"15",
|
52
|
+
# "percentage_of_actors"=>"45",
|
53
|
+
# "groups/early_access"=>"1",
|
54
|
+
# "actors/90"=>"1",
|
55
|
+
# "actors/180"=>"1"}
|
56
|
+
puts
|
57
|
+
|
58
|
+
puts 'search keys'
|
59
|
+
pp namespaced_client.hgetall('search')
|
60
|
+
# search keys
|
61
|
+
# {"boolean"=>"true"}
|
62
|
+
puts
|
63
|
+
|
64
|
+
puts 'flipper get of feature'
|
65
|
+
pp adapter.get(flipper[:stats])
|
66
|
+
# flipper get of feature
|
67
|
+
# {:boolean=>"true",
|
68
|
+
# :groups=>#<Set: {"admins", "early_access"}>,
|
69
|
+
# :actors=>#<Set: {"25", "90", "180"}>,
|
70
|
+
# :percentage_of_actors=>"45",
|
71
|
+
# :percentage_of_random=>"15"}
|
data/flipper-redis.gemspec
CHANGED
@@ -2,17 +2,19 @@
|
|
2
2
|
require File.expand_path('../lib/flipper/adapters/redis/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "flipper-redis"
|
6
|
+
gem.version = Flipper::Adapters::Redis::VERSION
|
5
7
|
gem.authors = ["John Nunemaker"]
|
6
8
|
gem.email = ["nunemaker@gmail.com"]
|
7
9
|
gem.description = %q{Redis adapter for Flipper}
|
8
10
|
gem.summary = %q{Redis adapter for Flipper}
|
9
11
|
gem.homepage = "http://jnunemaker.github.com/flipper-redis"
|
10
|
-
|
11
|
-
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
-
gem.files = `git ls-files`.split("\n")
|
13
|
-
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
-
gem.name = "flipper-redis"
|
15
12
|
gem.require_paths = ["lib"]
|
16
|
-
|
17
|
-
gem.
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
|
18
|
+
gem.add_dependency 'flipper', '~> 0.5.0'
|
19
|
+
gem.add_dependency 'redis', '~> 3.0.0'
|
18
20
|
end
|
data/lib/flipper-redis.rb
CHANGED
@@ -1,35 +1,126 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'redis'
|
3
|
+
require 'flipper'
|
3
4
|
|
4
5
|
module Flipper
|
5
6
|
module Adapters
|
6
7
|
class Redis
|
8
|
+
include Flipper::Adapter
|
9
|
+
|
10
|
+
# Private: The key that stores the set of known features.
|
11
|
+
FeaturesKey = :flipper_features
|
12
|
+
|
13
|
+
# Public: The name of the adapter.
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
# Public: Initializes a Redis flipper adapter.
|
17
|
+
#
|
18
|
+
# client - The Redis client to use. Feel free to namespace it.
|
7
19
|
def initialize(client)
|
8
20
|
@client = client
|
21
|
+
@name = :redis
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: Gets the values for all gates for a given feature.
|
25
|
+
#
|
26
|
+
# Returns a Hash of Flipper::Gate#key => value.
|
27
|
+
def get(feature)
|
28
|
+
result = {}
|
29
|
+
doc = doc_for(feature)
|
30
|
+
fields = doc.keys
|
31
|
+
|
32
|
+
feature.gates.each do |gate|
|
33
|
+
result[gate.key] = case gate.data_type
|
34
|
+
when :boolean, :integer
|
35
|
+
doc[gate.key.to_s]
|
36
|
+
when :set
|
37
|
+
fields_to_gate_value fields, gate
|
38
|
+
else
|
39
|
+
unsupported_data_type gate.data_type
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
result
|
44
|
+
end
|
45
|
+
|
46
|
+
# Public: Enables a gate for a given thing.
|
47
|
+
#
|
48
|
+
# feature - The Flipper::Feature for the gate.
|
49
|
+
# gate - The Flipper::Gate to disable.
|
50
|
+
# thing - The Flipper::Type being disabled for the gate.
|
51
|
+
#
|
52
|
+
# Returns true.
|
53
|
+
def enable(feature, gate, thing)
|
54
|
+
case gate.data_type
|
55
|
+
when :boolean, :integer
|
56
|
+
@client.hset feature.key, gate.key, thing.value.to_s
|
57
|
+
when :set
|
58
|
+
@client.hset feature.key, to_field(gate, thing), 1
|
59
|
+
else
|
60
|
+
unsupported_data_type gate.data_type
|
61
|
+
end
|
62
|
+
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
# Public: Disables a gate for a given thing.
|
67
|
+
#
|
68
|
+
# feature - The Flipper::Feature for the gate.
|
69
|
+
# gate - The Flipper::Gate to disable.
|
70
|
+
# thing - The Flipper::Type being disabled for the gate.
|
71
|
+
#
|
72
|
+
# Returns true.
|
73
|
+
def disable(feature, gate, thing)
|
74
|
+
case gate.data_type
|
75
|
+
when :boolean
|
76
|
+
@client.del feature.key
|
77
|
+
when :integer
|
78
|
+
@client.hset feature.key, gate.key, thing.value.to_s
|
79
|
+
when :set
|
80
|
+
@client.hdel feature.key, to_field(gate, thing)
|
81
|
+
else
|
82
|
+
unsupported_data_type gate.data_type
|
83
|
+
end
|
84
|
+
|
85
|
+
true
|
9
86
|
end
|
10
87
|
|
11
|
-
|
12
|
-
|
88
|
+
# Public: Adds a feature to the set of known features.
|
89
|
+
def add(feature)
|
90
|
+
@client.sadd FeaturesKey, feature.name
|
91
|
+
true
|
13
92
|
end
|
14
93
|
|
15
|
-
|
16
|
-
|
94
|
+
# Public: The set of known features.
|
95
|
+
def features
|
96
|
+
@client.smembers(FeaturesKey).to_set
|
17
97
|
end
|
18
98
|
|
19
|
-
|
20
|
-
|
99
|
+
# Private: Gets a hash of fields => values for the given feature.
|
100
|
+
#
|
101
|
+
# Returns a Hash of fields => values.
|
102
|
+
def doc_for(feature)
|
103
|
+
@client.hgetall(feature.key)
|
21
104
|
end
|
22
105
|
|
23
|
-
|
24
|
-
|
106
|
+
# Private: Converts gate and thing to hash key.
|
107
|
+
def to_field(gate, thing)
|
108
|
+
"#{gate.key}/#{thing.value}"
|
25
109
|
end
|
26
110
|
|
27
|
-
|
28
|
-
|
111
|
+
# Private: Returns a set of values given an array of fields and a gate.
|
112
|
+
#
|
113
|
+
# Returns a Set of the values enabled for the gate.
|
114
|
+
def fields_to_gate_value(fields, gate)
|
115
|
+
regex = /^#{Regexp.escape(gate.key)}\//
|
116
|
+
keys = fields.grep(regex)
|
117
|
+
values = keys.map { |key| key.split('/', 2).last }
|
118
|
+
values.to_set
|
29
119
|
end
|
30
120
|
|
31
|
-
|
32
|
-
|
121
|
+
# Private
|
122
|
+
def unsupported_data_type(data_type)
|
123
|
+
raise "#{data_type} is not supported by this adapter"
|
33
124
|
end
|
34
125
|
end
|
35
126
|
end
|
data/spec/flipper/redis_spec.rb
CHANGED
@@ -5,32 +5,11 @@ require 'flipper/spec/shared_adapter_specs'
|
|
5
5
|
describe Flipper::Adapters::Redis do
|
6
6
|
let(:client) { Redis.new }
|
7
7
|
|
8
|
-
subject {
|
8
|
+
subject { described_class.new(client) }
|
9
9
|
|
10
10
|
before do
|
11
11
|
client.flushdb
|
12
12
|
end
|
13
13
|
|
14
|
-
def read_key(key)
|
15
|
-
client.get key.to_s
|
16
|
-
rescue RuntimeError => e
|
17
|
-
if e.message =~ /wrong kind of value/
|
18
|
-
client.smembers(key.to_s).to_set
|
19
|
-
else
|
20
|
-
raise
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def write_key(key, value)
|
25
|
-
case value
|
26
|
-
when Array, Set
|
27
|
-
value.each do |member|
|
28
|
-
client.sadd key.to_s, member.to_s
|
29
|
-
end
|
30
|
-
else
|
31
|
-
client.set key.to_s, value.to_s
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
14
|
it_should_behave_like 'a flipper adapter'
|
36
15
|
end
|
data/spec/helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper-redis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: flipper
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 0.5.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,23 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 0.5.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: redis
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 3.0.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 3.0.0
|
30
46
|
description: Redis adapter for Flipper
|
31
47
|
email:
|
32
48
|
- nunemaker@gmail.com
|
@@ -41,6 +57,8 @@ files:
|
|
41
57
|
- LICENSE
|
42
58
|
- README.md
|
43
59
|
- Rakefile
|
60
|
+
- examples/basic.rb
|
61
|
+
- examples/internals.rb
|
44
62
|
- flipper-redis.gemspec
|
45
63
|
- lib/flipper-redis.rb
|
46
64
|
- lib/flipper/adapters/redis.rb
|
@@ -61,7 +79,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
79
|
version: '0'
|
62
80
|
segments:
|
63
81
|
- 0
|
64
|
-
hash:
|
82
|
+
hash: -1629782830808389875
|
65
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
84
|
none: false
|
67
85
|
requirements:
|
@@ -70,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
88
|
version: '0'
|
71
89
|
segments:
|
72
90
|
- 0
|
73
|
-
hash:
|
91
|
+
hash: -1629782830808389875
|
74
92
|
requirements: []
|
75
93
|
rubyforge_project:
|
76
94
|
rubygems_version: 1.8.23
|