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 CHANGED
@@ -1,9 +1,9 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
+ gem 'redis-namespace', :require => false
4
5
  gem 'rake'
5
6
  gem 'rspec'
6
- gem 'redis'
7
7
 
8
8
  group(:guard) do
9
9
  gem 'guard'
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), the feature flipping gems.
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"}
@@ -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
- gem.version = Flipper::Adapters::Redis::VERSION
17
- gem.add_dependency 'flipper', '~> 0.4'
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,2 +1 @@
1
- require 'flipper'
2
1
  require 'flipper/adapters/redis'
@@ -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
- def read(key)
12
- @client.get key.to_s
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
- def write(key, value)
16
- @client.set key.to_s, value.to_s
94
+ # Public: The set of known features.
95
+ def features
96
+ @client.smembers(FeaturesKey).to_set
17
97
  end
18
98
 
19
- def delete(key)
20
- @client.del key.to_s
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
- def set_add(key, value)
24
- @client.sadd(key.to_s, value.to_s)
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
- def set_delete(key, value)
28
- @client.srem(key.to_s, value.to_s)
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
- def set_members(key)
32
- @client.smembers(key.to_s).to_set
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
@@ -1,7 +1,7 @@
1
1
  module Flipper
2
2
  module Adapters
3
3
  class Redis
4
- VERSION = "0.3.0"
4
+ VERSION = "0.5.0"
5
5
  end
6
6
  end
7
7
  end
@@ -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 { Flipper::Adapters::Redis.new(client) }
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
@@ -22,4 +22,5 @@ RSpec.configure do |config|
22
22
  config.alias_example_to :fit, :focused => true
23
23
  config.alias_example_to :xit, :pending => true
24
24
  config.run_all_when_everything_filtered = true
25
+ config.fail_fast = true
25
26
  end
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.3.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-06 00:00:00.000000000 Z
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: '0.4'
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: '0.4'
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: 1698098931398913105
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: 1698098931398913105
91
+ hash: -1629782830808389875
74
92
  requirements: []
75
93
  rubyforge_project:
76
94
  rubygems_version: 1.8.23