email_campaign 0.1.2 → 0.1.3
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.
@@ -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
|