flipper-redis 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|