ar_rollout 0.0.20 → 0.0.21
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/ar_rollout.rb +93 -68
- data/lib/ar_rollout/rollout.rb +15 -3
- data/lib/ar_rollout/version.rb +1 -1
- data/lib/tasks/ar_rollout_tasks.rake +82 -32
- metadata +2 -2
data/lib/ar_rollout.rb
CHANGED
@@ -3,87 +3,120 @@ require 'ar_rollout/group.rb'
|
|
3
3
|
require 'ar_rollout/membership.rb'
|
4
4
|
require 'ar_rollout/opt_out.rb'
|
5
5
|
require 'ar_rollout/helper.rb'
|
6
|
+
|
6
7
|
module ArRollout
|
7
8
|
@@defined_groups = []
|
9
|
+
@@scanned_features = nil
|
8
10
|
|
9
11
|
def self.configure
|
10
12
|
yield self
|
11
13
|
end
|
12
14
|
|
13
|
-
def self.
|
14
|
-
|
15
|
+
def self.activate_user(feature, user)
|
16
|
+
permit_user(feature, user)
|
17
|
+
Rollout.find_or_create_by_name_and_user_id!(feature, get_id(user))
|
15
18
|
end
|
16
19
|
|
17
|
-
def self.
|
18
|
-
(
|
20
|
+
def self.deactivate_user(feature, user)
|
21
|
+
Rollout.find_all_by_name_and_user_id(feature, get_id(user)).each(&:destroy)
|
19
22
|
end
|
20
23
|
|
21
|
-
def self.
|
22
|
-
|
24
|
+
def self.omit_user(feature, user)
|
25
|
+
OptOut.find_or_create_by_feature_and_user_id!(feature, get_id(user))
|
26
|
+
end
|
23
27
|
|
24
|
-
|
25
|
-
|
28
|
+
def self.permit_user(feature, user)
|
29
|
+
OptOut.find_by_feature_and_user_id(feature, get_id(user)).try(:destroy)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.activate_group(feature, group)
|
33
|
+
unless defined_groups.include?(group) || Group.find_by_name(group)
|
34
|
+
Group.create!(name: group)
|
26
35
|
end
|
36
|
+
|
37
|
+
Rollout.find_or_create_by_name_and_group!(feature, group)
|
27
38
|
end
|
28
39
|
|
29
|
-
def self.
|
30
|
-
|
31
|
-
res_id = [Fixnum, String].include?(user.class) ? user : user.id
|
32
|
-
Rollout.find_or_create_by_name_and_user_id(feature, res_id)
|
40
|
+
def self.deactivate_group(feature, group)
|
41
|
+
Rollout.find_all_by_name_and_group(feature, group).each(&:destroy)
|
33
42
|
end
|
34
43
|
|
35
|
-
def self.
|
36
|
-
|
37
|
-
Rollout.
|
44
|
+
def self.activate_percentage(feature, percentage)
|
45
|
+
Rollout.where(name: feature).where('"percentage" IS NOT NULL').each(&:destroy)
|
46
|
+
Rollout.create!(name: feature, percentage: percentage)
|
38
47
|
end
|
39
48
|
|
40
|
-
def self.
|
41
|
-
|
42
|
-
OptOut.create(feature: feature, user_id: res_id)
|
49
|
+
def self.deactivate_percentage(feature)
|
50
|
+
Rollout.where(name: feature).where('"percentage" IS NOT NULL').each(&:destroy)
|
43
51
|
end
|
44
52
|
|
45
|
-
def self.
|
46
|
-
|
47
|
-
|
48
|
-
get_group(group)
|
49
|
-
end
|
50
|
-
Rollout.find_or_create_by_name_and_group(feature, group)
|
53
|
+
def self.deactivate(feature)
|
54
|
+
Rollout.where(name: feature).destroy_all
|
55
|
+
OptOut.where(feature: feature).destroy_all
|
51
56
|
end
|
52
57
|
|
53
|
-
def self.
|
54
|
-
|
58
|
+
def self.data_groups
|
59
|
+
Group.all
|
55
60
|
end
|
56
61
|
|
57
|
-
def self.
|
58
|
-
|
59
|
-
|
62
|
+
def self.defined_groups
|
63
|
+
@@defined_groups
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.groups
|
67
|
+
(defined_groups + data_groups.collect(&:name).collect(&:intern)).uniq.sort
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.active_groups
|
71
|
+
Rollout.where('"group" IS NOT NULL').collect(&:group).uniq.sort
|
60
72
|
end
|
61
73
|
|
62
74
|
def self.get_group(group)
|
63
|
-
Group.find_or_create_by_name(group)
|
75
|
+
Group.find_or_create_by_name!(group) unless defined_groups.include?(group.intern)
|
64
76
|
end
|
65
77
|
|
66
|
-
def self.
|
67
|
-
|
68
|
-
Membership.find_or_create_by_group_id_and_user_id(get_group(group).id, res_id)
|
78
|
+
def self.create_group(group)
|
79
|
+
get_group(group)
|
69
80
|
end
|
70
81
|
|
71
|
-
def self.
|
72
|
-
|
82
|
+
def self.change_group_name(old_name, new_name)
|
83
|
+
if group = Group.find_by_name(old_name)
|
84
|
+
group.update_attributes!(name: new_name)
|
85
|
+
Rollout.find_all_by_group(old_name).each { |rollout| rollout.update_attributes!(group: new_name) }
|
86
|
+
end
|
73
87
|
end
|
74
88
|
|
75
|
-
def self.
|
76
|
-
|
89
|
+
def self.add_user_to_group(group, user)
|
90
|
+
Membership.find_or_create_by_group_id_and_user_id!(get_group(group).id, get_id(user))
|
77
91
|
end
|
78
92
|
|
79
|
-
def self.
|
80
|
-
|
93
|
+
def self.remove_user_from_group(group, user)
|
94
|
+
Membership.find_by_group_id_and_user_id(get_group(group).id, get_id(user)).try(&:destroy)
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.delete_group(group)
|
98
|
+
Group.find_by_name(group).try(:destroy)
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.define_group(name, &block)
|
102
|
+
@@defined_groups << name
|
103
|
+
|
104
|
+
Rollout.send :define_method, "match_#{name}?" do |b|
|
105
|
+
block.call(b)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.features
|
110
|
+
scanned_feature_names = scanned_features.collect { |scanned_feature| scanned_feature[0] }
|
111
|
+
Rollout.select('distinct("name")').where('"name" not in (?)', scanned_feature_names).inject(scanned_feature_names) do |arr, rollout|
|
112
|
+
arr << rollout.name
|
113
|
+
end.sort
|
81
114
|
end
|
82
115
|
|
83
116
|
def self.active?(name, user)
|
84
117
|
return false unless user
|
85
|
-
unless OptOut.where(feature: name, user_id: user
|
86
|
-
Rollout.where(name: name).where("user_id = ?
|
118
|
+
unless OptOut.where(feature: name, user_id: get_id(user)).any?
|
119
|
+
Rollout.where(name: name).where('"user_id" = ? OR user_id IS NULL', get_id(user)).any? do |rollout|
|
87
120
|
rollout.match?(user)
|
88
121
|
end
|
89
122
|
end
|
@@ -92,44 +125,36 @@ module ArRollout
|
|
92
125
|
def self.all_active(user)
|
93
126
|
return false unless user
|
94
127
|
rollouts = []
|
95
|
-
Rollout.where("user_id = ? or user_id is NULL", user.id
|
128
|
+
Rollout.where("user_id = ? or user_id is NULL", user.id).each do |rollout|
|
96
129
|
unless OptOut.where(feature: rollout.name, user_id: user.id).any?
|
97
130
|
rollouts << rollout.name if rollout.match?(user)
|
98
131
|
end
|
99
132
|
end
|
100
|
-
rollouts.uniq
|
133
|
+
rollouts.uniq.sort
|
101
134
|
end
|
102
135
|
|
103
|
-
|
104
|
-
yield
|
105
|
-
rescue StandardError => e
|
106
|
-
Rollout.where(name: name).each do |rollout|
|
107
|
-
rollout.increment!(:failure_count)
|
108
|
-
end
|
109
|
-
raise e
|
110
|
-
end
|
136
|
+
private
|
111
137
|
|
112
|
-
def self.
|
113
|
-
|
114
|
-
:percentage => (_active_percentage(feature) || 0).to_i,
|
115
|
-
:groups => _active_groups(feature).map { |g| g.to_sym },
|
116
|
-
:users => _active_user_ids(feature)
|
117
|
-
}
|
138
|
+
def self.get_id(user)
|
139
|
+
[Fixnum, String].include?(user.class) ? user.to_i : user.id
|
118
140
|
end
|
119
141
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
142
|
+
def self.scanned_features
|
143
|
+
@@scanned_features ||= Dir["app/views/**/*", 'app/controllers/**/*', 'app/helpers/**/*'].inject({}) do |obj, path|
|
144
|
+
unless File.directory?(path)
|
145
|
+
File.open(path) do |f|
|
146
|
+
f.grep(/rollout\?/) do |line, no|
|
147
|
+
line.scan(/rollout\?\s*\(*:(\w+)/).each do |line|
|
148
|
+
obj[line[0]] ||= []
|
149
|
+
obj[line[0]] << path
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
128
154
|
|
129
|
-
|
130
|
-
|
155
|
+
obj
|
156
|
+
end
|
131
157
|
end
|
132
|
-
|
133
158
|
end
|
134
159
|
|
135
160
|
ActionController::Base.send :include, ArRollout::Controller::Helpers
|
data/lib/ar_rollout/rollout.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
class Rollout < ActiveRecord::Base
|
2
2
|
attr_accessible :name, :group, :user_id, :percentage
|
3
|
+
validates :name, presence: true
|
4
|
+
validate :validate_one_rollout
|
3
5
|
|
4
6
|
def match?(user)
|
5
7
|
return false unless user
|
@@ -13,10 +15,13 @@ class Rollout < ActiveRecord::Base
|
|
13
15
|
def match_group?(user)
|
14
16
|
if Rollout.method_defined? "match_#{group}?"
|
15
17
|
send "match_#{group}?", user
|
16
|
-
elsif group = Group.find_by_name(group) && group.memberships.where('user_id = ?', user.id).any?
|
17
|
-
true
|
18
18
|
else
|
19
|
-
|
19
|
+
data_group = Group.find_by_name(group)
|
20
|
+
if data_group && data_group.memberships.where('user_id = ?', user.id).any?
|
21
|
+
true
|
22
|
+
else
|
23
|
+
false
|
24
|
+
end
|
20
25
|
end
|
21
26
|
end
|
22
27
|
|
@@ -28,4 +33,11 @@ class Rollout < ActiveRecord::Base
|
|
28
33
|
percentage ? ((user.id.to_i % 100) < percentage.to_i) : false
|
29
34
|
end
|
30
35
|
|
36
|
+
private
|
37
|
+
|
38
|
+
def validate_one_rollout
|
39
|
+
unless group || user_id || percentage
|
40
|
+
errors.add(:base, 'Must have group, user_id, or percentage')
|
41
|
+
end
|
42
|
+
end
|
31
43
|
end
|
data/lib/ar_rollout/version.rb
CHANGED
@@ -1,36 +1,86 @@
|
|
1
1
|
namespace :rollout do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
desc "
|
33
|
-
task :
|
2
|
+
desc "Activate a feature for a specific user"
|
3
|
+
task :activate_user, [:feature, :user_id] => :environment do |t, args|
|
4
|
+
ArRollout.activate_user(args.feature,args.user_id)
|
5
|
+
end
|
6
|
+
|
7
|
+
desc "Deactivate a feature for a specific user"
|
8
|
+
task :deactivate_user, [:feature, :user_id] => :environment do |t, args|
|
9
|
+
ArRollout.deactivate_user(args.feature,args.user_id)
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Exclude a user from a feature"
|
13
|
+
task :exclude_user, [:feature, :user_id] => :environment do |t, args|
|
14
|
+
ArRollout.omit_user(args.feature, args.user_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
desc "Permit a user to have a feature rolled out to it"
|
18
|
+
task :permit_user, [:feature, :user_id] => :environment do |t, args|
|
19
|
+
ArRollout.permit_user(args.feature, args.user_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Activate a feature for a group"
|
23
|
+
task :activate_group, [:feature, :group] => :environment do |t, args|
|
24
|
+
ArRollout.activate_group(args.feature,args.group)
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Deactivate a feature for a group"
|
28
|
+
task :deactivate_group, [:feature, :group] => :environment do |t, args|
|
29
|
+
ArRollout.deactivate_group(args.feature,args.group)
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Activate a feature for a percentage"
|
33
|
+
task :activate_percentage, [:feature, :percentage] => :environment do |t, args|
|
34
|
+
ArRollout.activate_percentage(args.feature,args.percentage)
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Deactivate percentage rollout for a feature"
|
38
|
+
task :deactivate_percentage, [:feature] => :environment do |t, args|
|
39
|
+
ArRollout.deactivate_percentage(args.feature)
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "Deactivate a feature"
|
43
|
+
task :deactivate, [:feature] => :environment do |t, args|
|
44
|
+
ArRollout.deactivate(args.feature)
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "List groups"
|
48
|
+
task :groups, [] => :environment do |t, args|
|
49
|
+
puts ArRollout.groups
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "List active groups"
|
53
|
+
task :active_groups, [] => :environment do |t, args|
|
54
|
+
puts ArRollout.active_groups
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "Create a group"
|
58
|
+
task :create_group, [:group] => :environment do |t, args|
|
59
|
+
ArRollout.create_group(args.group)
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "Change a group's name"
|
63
|
+
task :change_group_name, [:old_name, :new_name] => :environment do |t, args|
|
64
|
+
ArRollout.change_group_name(args.old_name, args.new_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "Add a user to a group"
|
68
|
+
task :add_user_to_group, [:group, :user_id] => :environment do |t, args|
|
69
|
+
ArRollout.add_user_to_group(args.group, args.user_id)
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "Remove a user from a group"
|
73
|
+
task :remove_user_from_group, [:group, :user_id] => :environment do |t, args|
|
74
|
+
ArRollout.remove_user_from_group(args.group, args.user_id)
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "Delete a group"
|
78
|
+
task :delete_group, [:group] => :environment do |t, args|
|
79
|
+
ArRollout.delete_group(args.group)
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "List features"
|
83
|
+
task :features, [] => :environment do |t, args|
|
34
84
|
puts ArRollout.features
|
35
85
|
end
|
36
86
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ar_rollout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.21
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2012-12-
|
16
|
+
date: 2012-12-07 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: rails
|