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 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