rollout 1.1.0 → 1.2.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/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rollout (1.2.0)
5
+ redis
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ bourne (1.0)
11
+ mocha (= 0.9.8)
12
+ diff-lcs (1.1.3)
13
+ git (1.2.5)
14
+ jeweler (1.6.4)
15
+ bundler (~> 1.0)
16
+ git (>= 1.2.5)
17
+ rake
18
+ mocha (0.9.8)
19
+ rake
20
+ rake (0.9.2.2)
21
+ redis (2.2.2)
22
+ rspec (2.10.0)
23
+ rspec-core (~> 2.10.0)
24
+ rspec-expectations (~> 2.10.0)
25
+ rspec-mocks (~> 2.10.0)
26
+ rspec-core (2.10.1)
27
+ rspec-expectations (2.10.0)
28
+ diff-lcs (~> 1.1.3)
29
+ rspec-mocks (2.10.1)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ bourne (= 1.0)
36
+ bundler (>= 1.0.0)
37
+ jeweler (~> 1.6.4)
38
+ mocha (= 0.9.8)
39
+ rollout!
40
+ rspec (~> 2.10.0)
data/lib/rollout.rb CHANGED
@@ -4,6 +4,14 @@ class Rollout
4
4
  @groups = {"all" => lambda { |user| true }}
5
5
  end
6
6
 
7
+ def activate_globally(feature)
8
+ @redis.sadd(global_key, feature)
9
+ end
10
+
11
+ def deactivate_globally(feature)
12
+ @redis.srem(global_key, feature)
13
+ end
14
+
7
15
  def activate_group(feature, group)
8
16
  @redis.sadd(group_key(feature), group)
9
17
  end
@@ -16,6 +24,7 @@ class Rollout
16
24
  @redis.del(group_key(feature))
17
25
  @redis.del(user_key(feature))
18
26
  @redis.del(percentage_key(feature))
27
+ deactivate_globally(feature)
19
28
  end
20
29
 
21
30
  def activate_user(feature, user)
@@ -30,10 +39,15 @@ class Rollout
30
39
  @groups[group.to_s] = block
31
40
  end
32
41
 
33
- def active?(feature, user)
34
- user_in_active_group?(feature, user) ||
35
- user_active?(feature, user) ||
36
- user_within_active_percentage?(feature, user)
42
+ def active?(feature, user = nil)
43
+ if user
44
+ active_globally?(feature) ||
45
+ user_in_active_group?(feature, user) ||
46
+ user_active?(feature, user) ||
47
+ user_within_active_percentage?(feature, user)
48
+ else
49
+ active_globally?(feature)
50
+ end
37
51
  end
38
52
 
39
53
  def activate_percentage(feature, percentage)
@@ -44,12 +58,19 @@ class Rollout
44
58
  @redis.del(percentage_key(feature))
45
59
  end
46
60
 
47
- def info(feature)
48
- {
49
- :percentage => (active_percentage(feature) || 0).to_i,
50
- :groups => active_groups(feature).map { |g| g.to_sym },
51
- :users => active_user_ids(feature)
52
- }
61
+ def info(feature = nil)
62
+ if feature
63
+ {
64
+ :percentage => (active_percentage(feature) || 0).to_i,
65
+ :groups => active_groups(feature).map { |g| g.to_sym },
66
+ :users => active_user_ids(feature),
67
+ :global => active_global_features
68
+ }
69
+ else
70
+ {
71
+ :global => active_global_features
72
+ }
73
+ end
53
74
  end
54
75
 
55
76
  private
@@ -69,6 +90,10 @@ class Rollout
69
90
  "#{key(name)}:percentage"
70
91
  end
71
92
 
93
+ def global_key
94
+ "feature:__global__"
95
+ end
96
+
72
97
  def active_groups(feature)
73
98
  @redis.smembers(group_key(feature)) || []
74
99
  end
@@ -77,10 +102,18 @@ class Rollout
77
102
  @redis.smembers(user_key(feature)).map { |id| id.to_i }
78
103
  end
79
104
 
105
+ def active_global_features
106
+ (@redis.smembers(global_key) || []).map(&:to_sym)
107
+ end
108
+
80
109
  def active_percentage(feature)
81
110
  @redis.get(percentage_key(feature))
82
111
  end
83
112
 
113
+ def active_globally?(feature)
114
+ @redis.sismember(global_key, feature)
115
+ end
116
+
84
117
  def user_in_active_group?(feature, user)
85
118
  active_groups(feature).any? do |group|
86
119
  @groups.key?(group) && @groups[group].call(user)
@@ -0,0 +1,3 @@
1
+ class Rollout
2
+ VERSION = "1.2.0"
3
+ end
data/rollout.gemspec CHANGED
@@ -1,54 +1,31 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rollout/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
6
  s.name = "rollout"
8
- s.version = "1.1.0"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
7
+ s.version = Rollout::VERSION
11
8
  s.authors = ["James Golick"]
12
- s.date = "2012-02-20"
13
- s.description = "Conditionally roll out features with redis."
14
- s.email = "jamesgoick@gmail.com"
15
- s.extra_rdoc_files = [
16
- "LICENSE",
17
- "README.rdoc"
18
- ]
19
- s.files = [
20
- ".document",
21
- "LICENSE",
22
- "README.rdoc",
23
- "Rakefile",
24
- "VERSION",
25
- "lib/rollout.rb",
26
- "rollout.gemspec",
27
- "spec/rollout_spec.rb",
28
- "spec/spec.opts",
29
- "spec/spec_helper.rb"
30
- ]
31
- s.homepage = "http://github.com/jamesgolick/rollout"
9
+ s.email = ["jamesgolick@gmail.com"]
10
+ s.description = "Feature flippers with redis."
11
+ s.summary = "Feature flippers with redis."
12
+ s.homepage = "https://github.com/jamesgolick/rollout"
13
+
32
14
  s.require_paths = ["lib"]
33
- s.rubygems_version = "1.8.10"
34
- s.summary = "Conditionally roll out features with redis."
35
15
 
36
- if s.respond_to? :specification_version then
37
- s.specification_version = 3
16
+ s.rubyforge_project = "rollout"
38
17
 
39
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
40
- s.add_development_dependency(%q<rspec>, ["= 1.2.9"])
41
- s.add_development_dependency(%q<bourne>, ["= 1.0.0"])
42
- s.add_development_dependency(%q<redis>, ["= 0.1"])
43
- else
44
- s.add_dependency(%q<rspec>, ["= 1.2.9"])
45
- s.add_dependency(%q<bourne>, ["= 1.0.0"])
46
- s.add_dependency(%q<redis>, ["= 0.1"])
47
- end
48
- else
49
- s.add_dependency(%q<rspec>, ["= 1.2.9"])
50
- s.add_dependency(%q<bourne>, ["= 1.0.0"])
51
- s.add_dependency(%q<redis>, ["= 0.1"])
52
- end
53
- end
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+
23
+ # specify any dependencies here; for example:
24
+ s.add_development_dependency "rspec", "~> 2.10.0"
25
+ s.add_development_dependency "bundler", ">= 1.0.0"
26
+ s.add_development_dependency "jeweler", "~> 1.6.4"
27
+ s.add_development_dependency "bourne", "1.0"
28
+ s.add_development_dependency "mocha", "0.9.8"
54
29
 
30
+ s.add_runtime_dependency "redis"
31
+ end
data/spec/rollout_spec.rb CHANGED
@@ -60,6 +60,7 @@ describe "Rollout" do
60
60
  @rollout.activate_group(:chat, :fivesonly)
61
61
  @rollout.activate_user(:chat, stub(:id => 51))
62
62
  @rollout.activate_percentage(:chat, 100)
63
+ @rollout.activate_globally(:chat)
63
64
  @rollout.deactivate_all(:chat)
64
65
  end
65
66
 
@@ -74,6 +75,10 @@ describe "Rollout" do
74
75
  it "removes the percentage" do
75
76
  @rollout.should_not be_active(:chat, stub(:id => 24))
76
77
  end
78
+
79
+ it "removes globally" do
80
+ @rollout.should_not be_active(:chat)
81
+ end
77
82
  end
78
83
 
79
84
  describe "activating a specific user" do
@@ -106,6 +111,16 @@ describe "Rollout" do
106
111
  end
107
112
  end
108
113
 
114
+ describe "activating a feature globally" do
115
+ before do
116
+ @rollout.activate_globally(:chat)
117
+ end
118
+
119
+ it "activates the feature" do
120
+ @rollout.should be_active(:chat)
121
+ end
122
+ end
123
+
109
124
  describe "activating a feature for a percentage of users" do
110
125
  before do
111
126
  @rollout.activate_percentage(:chat, 20)
@@ -148,33 +163,58 @@ describe "Rollout" do
148
163
  end
149
164
  end
150
165
 
166
+ describe "deactivating the feature globally" do
167
+ before do
168
+ @rollout.activate_globally(:chat)
169
+ @rollout.deactivate_globally(:chat)
170
+ end
171
+
172
+ it "becomes inactivate" do
173
+ @rollout.should_not be_active(:chat)
174
+ end
175
+ end
176
+
151
177
  describe "#info" do
178
+ context "global features" do
179
+ let(:features) { [:signup, :chat, :table] }
180
+
181
+ before do
182
+ features.each do |f|
183
+ @rollout.activate_globally(f)
184
+ end
185
+ end
186
+
187
+ it "returns all global features" do
188
+ @rollout.info.should eq({ :global => features.reverse })
189
+ end
190
+ end
191
+
152
192
  describe "with a percentage set" do
153
193
  before do
154
194
  @rollout.activate_percentage(:chat, 10)
155
195
  @rollout.activate_group(:chat, :caretakers)
156
196
  @rollout.activate_group(:chat, :greeters)
197
+ @rollout.activate_globally(:signup)
157
198
  @rollout.activate_user(:chat, stub(:id => 42))
158
199
  end
159
200
 
160
201
  it "returns info about all the activations" do
161
202
  @rollout.info(:chat).should == {
162
203
  :percentage => 10,
163
- :groups => [:greeters, :caretakers],
164
- :users => [42]
204
+ :groups => [:greeters, :caretakers],
205
+ :users => [42],
206
+ :global => [:signup]
165
207
  }
166
208
  end
167
209
  end
168
210
 
169
211
  describe "without a percentage set" do
170
- before do
171
- end
172
-
173
212
  it "the percentage defaults to 0" do
174
213
  @rollout.info(:chat).should == {
175
214
  :percentage => 0,
176
- :groups => [],
177
- :users => []
215
+ :groups => [],
216
+ :users => [],
217
+ :global => []
178
218
  }
179
219
  end
180
220
  end
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,11 @@
1
1
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
2
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
3
  require 'rollout'
4
- require 'spec'
5
- require 'spec/autorun'
4
+ require 'rspec'
6
5
  require 'bourne'
7
6
  require 'redis'
8
7
 
9
- Spec::Runner.configure do |config|
8
+ RSpec.configure do |config|
10
9
  config.mock_with :mocha
11
10
  config.before { Redis.new.flushdb }
12
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rollout
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,60 +9,95 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-20 00:00:00.000000000 Z
12
+ date: 2012-09-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70160153622800 !ruby/object:Gem::Requirement
16
+ requirement: &70171348875940 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
- - - =
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.10.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70171348875940
25
+ - !ruby/object:Gem::Dependency
26
+ name: bundler
27
+ requirement: &70171348875080 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70171348875080
36
+ - !ruby/object:Gem::Dependency
37
+ name: jeweler
38
+ requirement: &70171348874140 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
20
42
  - !ruby/object:Gem::Version
21
- version: 1.2.9
43
+ version: 1.6.4
22
44
  type: :development
23
45
  prerelease: false
24
- version_requirements: *70160153622800
46
+ version_requirements: *70171348874140
25
47
  - !ruby/object:Gem::Dependency
26
48
  name: bourne
27
- requirement: &70160153621860 !ruby/object:Gem::Requirement
49
+ requirement: &70171348872540 !ruby/object:Gem::Requirement
28
50
  none: false
29
51
  requirements:
30
52
  - - =
31
53
  - !ruby/object:Gem::Version
32
- version: 1.0.0
54
+ version: '1.0'
33
55
  type: :development
34
56
  prerelease: false
35
- version_requirements: *70160153621860
57
+ version_requirements: *70171348872540
36
58
  - !ruby/object:Gem::Dependency
37
- name: redis
38
- requirement: &70160153619840 !ruby/object:Gem::Requirement
59
+ name: mocha
60
+ requirement: &70171348871700 !ruby/object:Gem::Requirement
39
61
  none: false
40
62
  requirements:
41
63
  - - =
42
64
  - !ruby/object:Gem::Version
43
- version: '0.1'
65
+ version: 0.9.8
44
66
  type: :development
45
67
  prerelease: false
46
- version_requirements: *70160153619840
47
- description: Conditionally roll out features with redis.
48
- email: jamesgoick@gmail.com
68
+ version_requirements: *70171348871700
69
+ - !ruby/object:Gem::Dependency
70
+ name: redis
71
+ requirement: &70171348870700 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *70171348870700
80
+ description: Feature flippers with redis.
81
+ email:
82
+ - jamesgolick@gmail.com
49
83
  executables: []
50
84
  extensions: []
51
- extra_rdoc_files:
52
- - LICENSE
53
- - README.rdoc
85
+ extra_rdoc_files: []
54
86
  files:
55
87
  - .document
88
+ - .gitignore
89
+ - Gemfile
90
+ - Gemfile.lock
56
91
  - LICENSE
57
92
  - README.rdoc
58
- - Rakefile
59
93
  - VERSION
60
94
  - lib/rollout.rb
95
+ - lib/rollout/version.rb
61
96
  - rollout.gemspec
62
97
  - spec/rollout_spec.rb
63
98
  - spec/spec.opts
64
99
  - spec/spec_helper.rb
65
- homepage: http://github.com/jamesgolick/rollout
100
+ homepage: https://github.com/jamesgolick/rollout
66
101
  licenses: []
67
102
  post_install_message:
68
103
  rdoc_options: []
@@ -81,9 +116,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
116
  - !ruby/object:Gem::Version
82
117
  version: '0'
83
118
  requirements: []
84
- rubyforge_project:
119
+ rubyforge_project: rollout
85
120
  rubygems_version: 1.8.10
86
121
  signing_key:
87
122
  specification_version: 3
88
- summary: Conditionally roll out features with redis.
89
- test_files: []
123
+ summary: Feature flippers with redis.
124
+ test_files:
125
+ - spec/rollout_spec.rb
126
+ - spec/spec.opts
127
+ - spec/spec_helper.rb
data/Rakefile DELETED
@@ -1,47 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
-
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "rollout"
8
- gem.summary = %Q{Conditionally roll out features with redis.}
9
- gem.description = %Q{Conditionally roll out features with redis.}
10
- gem.email = "jamesgoick@gmail.com"
11
- gem.homepage = "http://github.com/jamesgolick/rollout"
12
- gem.authors = ["James Golick"]
13
- gem.add_development_dependency "rspec", "1.2.9"
14
- gem.add_development_dependency "bourne", "1.0.0"
15
- gem.add_development_dependency "redis", "0.1"
16
- # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
- end
18
- Jeweler::GemcutterTasks.new
19
- rescue LoadError
20
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
- end
22
-
23
- require 'spec/rake/spectask'
24
- Spec::Rake::SpecTask.new(:spec) do |spec|
25
- spec.libs << 'lib' << 'spec'
26
- spec.spec_files = FileList['spec/**/*_spec.rb']
27
- end
28
-
29
- Spec::Rake::SpecTask.new(:rcov) do |spec|
30
- spec.libs << 'lib' << 'spec'
31
- spec.pattern = 'spec/**/*_spec.rb'
32
- spec.rcov = true
33
- end
34
-
35
- task :spec => :check_dependencies
36
-
37
- task :default => :spec
38
-
39
- require 'rake/rdoctask'
40
- Rake::RDocTask.new do |rdoc|
41
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
42
-
43
- rdoc.rdoc_dir = 'rdoc'
44
- rdoc.title = "rollout #{version}"
45
- rdoc.rdoc_files.include('README*')
46
- rdoc.rdoc_files.include('lib/**/*.rb')
47
- end