ar_rollout 0.0.20 → 0.0.21
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/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
|