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 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.defined_groups
14
- @@defined_groups
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.groups
18
- (@@defined_groups + Group.select(:name).collect(&:name).collect(&:intern)).uniq.sort
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.define_group(name, &block)
22
- @@defined_groups << name
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
- Rollout.send :define_method, "match_#{name}?" do |b|
25
- block.call(b)
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.activate_user(feature, user)
30
- return false if feature.nil? || user.nil?
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.deactivate_user(feature, user)
36
- res_id = [Fixnum, String].include?(user.class) ? user : user.id
37
- Rollout.find_all_by_name_and_user_id(feature, res_id).map(&:destroy)
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.exclude_user(feature, user)
41
- res_id = [Fixnum, String].include?(user.class) ? user : user.id
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.activate_group(feature, group)
46
- return false if feature.nil? || group.nil?
47
- unless defined_groups.include? group
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.deactivate_group(feature, group)
54
- Rollout.find_all_by_name_and_group(feature, group).map(&:destroy)
58
+ def self.data_groups
59
+ Group.all
55
60
  end
56
61
 
57
- def self.activate_percentage(feature, percentage)
58
- Rollout.where("name = ? and percentage is not null", feature).destroy_all
59
- Rollout.create(name: feature, percentage: percentage)
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.activate_user_in_group(group, user)
67
- res_id = [Fixnum, String].include?(user.class) ? user : user.id
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.deactivate_all(feature)
72
- Rollout.find_all_by_name(feature).map(&:destroy)
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.features
76
- Rollout.select("distinct(name)").order(:name).map(&:name)
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.active_groups
80
- Rollout.where("'group' is NOT NULL").map(&:group).uniq
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.id).any?
86
- Rollout.where(name: name).where("user_id = ? or user_id is NULL", user.id.to_i).any? do |rollout|
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.to_i).each do |rollout|
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
- def self.degrade_feature(name)
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.info(feature)
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
- private
121
- def self._active_groups(feature)
122
- Rollout.where('"name" = ? and "group" is not null', feature).map(&:group)
123
- end
124
-
125
- def self._active_user_ids(feature)
126
- Rollout.where('"name" = ? and "user_id" is not null', feature).map(&:user_id)
127
- end
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
- def self._active_percentage(feature)
130
- Rollout.select("percentage").where('"name" = ? and "percentage" is not null', feature).first
155
+ obj
156
+ end
131
157
  end
132
-
133
158
  end
134
159
 
135
160
  ActionController::Base.send :include, ArRollout::Controller::Helpers
@@ -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
- false
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
@@ -1,3 +1,3 @@
1
1
  module ArRollout
2
- VERSION = "0.0.20"
2
+ VERSION = "0.0.21"
3
3
  end
@@ -1,36 +1,86 @@
1
1
  namespace :rollout do
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 "Activate a feature for a group"
13
- task :activate_group, [:feature, :user_id] => :environment do |t, args|
14
- ArRollout.activate_group(args.feature,args.user_id)
15
- end
16
-
17
- desc "Deactivate a feature for a group"
18
- task :deactivate_group, [:feature, :user_id] => :environment do |t, args|
19
- ArRollout.deactivate_group(args.feature,args.user_id)
20
- end
21
-
22
- desc "Activate a feature for a percentage"
23
- task :activate_percentage, [:feature, :percentage] => :environment do |t, args|
24
- ArRollout.activate_percentage(args.feature,args.percentage)
25
- end
26
-
27
- desc "Deactivate a feature"
28
- task :deactivate_all, [:feature] => :environment do |t, args|
29
- ArRollout.deactivate_all(args.feature)
30
- end
31
-
32
- desc "List all features"
33
- task :list, [] => :environment do |t, args|
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.20
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-04 00:00:00.000000000 Z
16
+ date: 2012-12-07 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: rails