email_campaign 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,21 @@
|
|
1
|
+
module EmailCampaign
|
2
|
+
module EmailHelper
|
3
|
+
|
4
|
+
def email_asset_url(filename)
|
5
|
+
AppBaseURL + url_for(:controller => 'email', :action => 'asset', :method => @method, :filename => filename, :k => @identifier)
|
6
|
+
end
|
7
|
+
|
8
|
+
def email_link_url(url)
|
9
|
+
AppBaseURL + url_for(:controller => 'email', :action => 'link', :url => url, :k => @identifier)
|
10
|
+
end
|
11
|
+
|
12
|
+
def email_unsubscribe_url
|
13
|
+
AppBaseURL + url_for(:controller => 'email', :action => 'unsubscribe', :k => @identifier)
|
14
|
+
end
|
15
|
+
|
16
|
+
def email_web_version_url
|
17
|
+
AppBaseURL + url_for(:controller => 'email', :action => 'web_version', :method => @method, :k => @identifier)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class EmailCampaign::Campaign < ActiveRecord::Base
|
2
|
-
|
2
|
+
self.table_name = 'email_campaigns'
|
3
3
|
|
4
4
|
attr_accessible :name, :mailer, :method, :params_yaml, :deliver_at,
|
5
5
|
:finalized, :queued, :delivered,
|
@@ -9,22 +9,42 @@ class EmailCampaign::Campaign < ActiveRecord::Base
|
|
9
9
|
|
10
10
|
# new_recipients should be an Array of objects that respond to #email, #name, and #subscriber_id
|
11
11
|
# (falls back to #id if #subscriber_id doesn't exist; either way, id should be unique within campaign)
|
12
|
-
def add_recipients(new_recipients
|
12
|
+
def add_recipients(new_recipients)
|
13
13
|
new_recipients = [ new_recipients ] unless new_recipients.is_a?(Array)
|
14
14
|
|
15
|
-
|
15
|
+
processed = 0
|
16
|
+
skipped = 0
|
17
|
+
valid = 0
|
18
|
+
invalid = 0
|
19
|
+
duplicate = 0
|
20
|
+
unsubscribed = 0
|
21
|
+
|
16
22
|
new_recipients.each do |rcpt|
|
17
23
|
subscriber_id = rcpt.subscriber_id || rcpt.id
|
18
|
-
|
24
|
+
|
25
|
+
if subscriber_id && recipients.where(:subscriber_id => subscriber_id).count > 0
|
26
|
+
skipped += 1
|
27
|
+
next
|
28
|
+
end
|
19
29
|
|
20
30
|
r = recipients.create(:name => rcpt.name.strip, :email_address => rcpt.email_address.strip,
|
21
31
|
:subscriber_class_name => rcpt.class.name, :subscriber_id => subscriber_id)
|
22
32
|
|
23
|
-
|
24
|
-
|
33
|
+
processed += 1
|
34
|
+
case
|
35
|
+
when r.unsubscribed then unsubscribed += 1
|
36
|
+
when r.duplicate then duplicate += 1
|
37
|
+
when r.invalid_email then invalid += 1
|
38
|
+
else valid += 1
|
39
|
+
end
|
40
|
+
|
41
|
+
r.queue
|
25
42
|
end
|
26
43
|
|
27
|
-
|
44
|
+
{ :processed => processed, :skipped => skipped,
|
45
|
+
:valid => valid, :invalid => invalid,
|
46
|
+
:duplicate => duplicate, :unsubscribed => unsubscribed,
|
47
|
+
:total => recipients.where(:ready => true).count }
|
28
48
|
end
|
29
49
|
|
30
50
|
def queue(deliver_at = Time.now.utc)
|
@@ -45,21 +65,11 @@ class EmailCampaign::Campaign < ActiveRecord::Base
|
|
45
65
|
process_delivery
|
46
66
|
end
|
47
67
|
|
48
|
-
|
49
|
-
update_attributes(:delivered => true, :delivery_finished_at => Time.now.utc)
|
68
|
+
update_attributes(:queued => false, :delivered => true, :delivery_finished_at => Time.now.utc)
|
50
69
|
end
|
51
70
|
|
52
71
|
def process_delivery
|
53
|
-
|
54
|
-
error = []
|
55
|
-
recipients.where(:ready => true).each do |r|
|
56
|
-
# begin
|
57
|
-
mailer.constantize.send(method.to_sym, r).deliver
|
58
|
-
sent << r
|
59
|
-
# rescue Exception => e
|
60
|
-
# error << [ r, e ]
|
61
|
-
# end
|
62
|
-
end
|
72
|
+
recipients.where(:ready => true).each(&:deliver)
|
63
73
|
end
|
64
74
|
|
65
75
|
end
|
@@ -1,13 +1,40 @@
|
|
1
1
|
class EmailCampaign::Recipient < ActiveRecord::Base
|
2
|
-
|
2
|
+
self.table_name = 'email_campaign_recipients'
|
3
3
|
|
4
4
|
attr_accessible :name, :email_address,
|
5
|
+
:subscriber_class_name, :subscriber_id,
|
5
6
|
:ready, :duplicate, :invalid_email, :unsubscribed,
|
6
|
-
:
|
7
|
+
:failed, :failed_at, :failure_reason,
|
8
|
+
:delivered, :delivered_at
|
7
9
|
|
8
10
|
belongs_to :campaign, :class_name => 'EmailCampaign::Campaign', :foreign_key => 'email_campaign_id'
|
9
11
|
|
10
|
-
|
12
|
+
validates_presence_of :email_address, :subscriber_id
|
13
|
+
|
14
|
+
before_create :generate_identifier, :check_for_duplicates, :check_for_unsubscribe
|
15
|
+
before_save :check_name, :check_email_address
|
16
|
+
|
17
|
+
def generate_identifier(regenerate = false)
|
18
|
+
return identifier if identifier && !regenerate
|
19
|
+
|
20
|
+
attempts = 0
|
21
|
+
new_identifier = nil
|
22
|
+
|
23
|
+
# 28^8 = 378 billion possibilities
|
24
|
+
validchars = 'ABCDEFGHJKLMNPQRTUVWXY346789'
|
25
|
+
|
26
|
+
while new_identifier.nil? && attempts < 10
|
27
|
+
# generate a 8 character identifier
|
28
|
+
new_identifier = ''
|
29
|
+
8.times { new_identifier << validchars[rand(validchars.length)] }
|
30
|
+
|
31
|
+
new_identifier = nil if self.class.count > 0 && self.class.where(:identifier => new_identifier).first
|
32
|
+
|
33
|
+
attempts += 1
|
34
|
+
end
|
35
|
+
|
36
|
+
self.identifier = new_identifier
|
37
|
+
end
|
11
38
|
|
12
39
|
def check_name
|
13
40
|
self.name = nil if name.blank?
|
@@ -16,7 +43,7 @@ class EmailCampaign::Recipient < ActiveRecord::Base
|
|
16
43
|
end
|
17
44
|
|
18
45
|
def check_for_duplicates
|
19
|
-
if
|
46
|
+
if campaign.recipients.where(:email_address => email_address).count > 0
|
20
47
|
self.ready = false
|
21
48
|
self.duplicate = true
|
22
49
|
else
|
@@ -41,52 +68,125 @@ class EmailCampaign::Recipient < ActiveRecord::Base
|
|
41
68
|
if self.class.where(:email_address => email_address, :unsubscribed => true).count > 0
|
42
69
|
self.unsubscribed = true
|
43
70
|
self.ready = false
|
44
|
-
else
|
45
|
-
self.unsubscribed = false
|
46
71
|
end
|
47
72
|
|
48
73
|
true
|
49
74
|
end
|
50
75
|
|
51
76
|
def queue
|
52
|
-
if !duplicate && !invalid_email && !unsubscribed
|
77
|
+
if !duplicate && !invalid_email && !unsubscribed && !failed
|
53
78
|
update_attributes(:ready => true)
|
54
79
|
else
|
55
80
|
false
|
56
81
|
end
|
57
82
|
end
|
58
83
|
|
84
|
+
# resets failed and delivered flags... use sparingly
|
85
|
+
def requeue
|
86
|
+
queue && update_attributes(:failed => false, :failed_at => nil, :failure_reason => nil,
|
87
|
+
:delivered => false, :delivered_at => nil)
|
88
|
+
end
|
89
|
+
|
59
90
|
def deliver
|
60
|
-
|
61
|
-
|
62
|
-
puts "Already attempted, not going to try again."
|
91
|
+
if !ready
|
92
|
+
puts "Not in ready state"
|
63
93
|
return false
|
64
94
|
end
|
65
95
|
|
96
|
+
if delivered
|
97
|
+
puts "Already delivered."
|
98
|
+
return true # not sure yet whether returning true is a good idea, but seems harmless enough
|
99
|
+
end
|
100
|
+
|
66
101
|
if failed
|
67
102
|
puts "Already failed (reason: #{failure_reason}), not going to try again."
|
103
|
+
update_attributes(:ready => false)
|
68
104
|
return false
|
69
105
|
end
|
70
106
|
|
71
107
|
unless update_column(:attempted, true) && increment(:attempts)
|
72
|
-
|
108
|
+
puts "Could not update 'attempted' flag, will not proceed for fear of sending multiple copies"
|
109
|
+
update_attributes(:ready => false, :failed => true, :failed_at => Time.now.utc, :failure_reason => "Could not update 'attempted' flag, will not proceed for fear of sending multiple copies")
|
73
110
|
return false
|
74
111
|
end
|
75
112
|
|
76
|
-
if email_address
|
77
|
-
|
78
|
-
|
79
|
-
self.failure_reason = "Invalid email address"
|
80
|
-
save
|
113
|
+
if !valid_email_address?(email_address)
|
114
|
+
puts "Invalid email address: #{email_address}"
|
115
|
+
update_attributes(:ready => false, :failed => true, :failed_at => Time.now.utc, :failure_reason => "Invalid email address")
|
81
116
|
return false
|
82
117
|
end
|
83
118
|
|
84
|
-
|
85
|
-
|
119
|
+
begin
|
120
|
+
campaign.mailer.constantize.send(campaign.method.to_sym, self).deliver
|
121
|
+
update_attributes(:ready => false, :delivered => true, :delivered_at => Time.now.utc)
|
122
|
+
rescue Exception => e
|
123
|
+
puts e.message
|
124
|
+
update_attributes(:ready => false, :failed => true, :failed_at => Time.now.utc, :failure_reason => "#{e.class.name}: #{e.message}")
|
125
|
+
return false
|
126
|
+
end
|
86
127
|
|
87
128
|
true
|
88
129
|
end
|
89
130
|
|
131
|
+
def self.record_open(identifier, params = {})
|
132
|
+
r = find_by_identifier(identifier)
|
133
|
+
r.record_open(params) if r
|
134
|
+
end
|
135
|
+
|
136
|
+
def record_open(params = {})
|
137
|
+
self.class.transaction do
|
138
|
+
self.opened = true
|
139
|
+
self.opened_at ||= Time.now.utc
|
140
|
+
self.opens += 1
|
141
|
+
save
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.record_click(identifier, params = {})
|
146
|
+
r = find_by_identifier(identifier)
|
147
|
+
r.record_click(params) if r
|
148
|
+
end
|
149
|
+
|
150
|
+
def record_click(params = nil)
|
151
|
+
self.class.transaction do
|
152
|
+
self.opened = true
|
153
|
+
self.opened_at ||= Time.now.utc
|
154
|
+
self.opens ||= 1
|
155
|
+
self.clicked = true
|
156
|
+
self.clicked_at ||= Time.now.utc
|
157
|
+
self.clicks += 1
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.unsubscribe(identifier, params = {})
|
162
|
+
r = find_by_identifier(identifier)
|
163
|
+
r.unsubscribe(params) if r
|
164
|
+
end
|
165
|
+
|
166
|
+
def unsubscribe(params = nil)
|
167
|
+
self.class.transaction do
|
168
|
+
self.class.where(:email_address => email_address, :unsubscribed => false).each do |r|
|
169
|
+
r.update_attributes(:unsubscribed => true, :ready => false)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
save
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.resubscribe(identifier, params = {})
|
176
|
+
r = find_by_identifier(identifier)
|
177
|
+
r.resubscribe(params) if r
|
178
|
+
end
|
179
|
+
|
180
|
+
def resubscribe(params = nil)
|
181
|
+
self.class.transaction do
|
182
|
+
self.class.where(:email_address => email_address, :unsubscribed => true).each do |r|
|
183
|
+
r.update_attributes(:unsubscribed, false)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
|
90
190
|
def to_s
|
91
191
|
name.blank? ? email_address : "#{name} <#{email_address}>"
|
92
192
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: email_campaign
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -108,12 +108,14 @@ files:
|
|
108
108
|
- app/assets/stylesheets/application.css
|
109
109
|
- app/controllers/email_campaigns_controller.rb
|
110
110
|
- app/helpers/.gitkeep
|
111
|
+
- app/helpers/email_campaign/email_helper.rb
|
111
112
|
- app/models/email_campaign/campaign.rb
|
112
113
|
- app/models/email_campaign/recipient.rb
|
113
114
|
- app/views/email_campaigns/index.html.erb
|
114
115
|
- config/routes.rb
|
115
116
|
- db/migrate/20130111224331_create_email_campaigns.rb
|
116
117
|
- db/migrate/20130111225431_create_email_campaign_recipients.rb
|
118
|
+
- db/migrate/201302281520_add_recipient_identifier.rb
|
117
119
|
- email_campaign.gemspec
|
118
120
|
- lib/email_campaign.rb
|
119
121
|
- lib/email_campaign/engine.rb
|