rollout 1.1.0 → 1.2.0

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