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
- set_table_name "email_campaigns"
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, limit = nil)
12
+ def add_recipients(new_recipients)
13
13
  new_recipients = [ new_recipients ] unless new_recipients.is_a?(Array)
14
14
 
15
- count = 0
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
- # next if subscriber_id && recipients.where(:subscriber_id => subscriber_id).count > 0
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
- r.queue unless limit && count >= limit
24
- count += 1
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
- recipients.where(:ready => true).count
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
- # update_attributes(:delivered => true, :delivered_at => Time.now.utc)
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
- sent = []
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
- set_table_name "email_campaign_recipients"
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
- :subscriber_class_name, :subscriber_id
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
- before_save :check_name, :check_for_duplicates, :check_email_address, :check_for_unsubscribe
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 self.campaign.recipients.where(:email_address => email_address).where('id != ?', id).count > 0
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
- # if we want to allow retries in the future we can change this bit
61
- if attempted && attempts > 0
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
- print "Could not update 'attempted' flag, will not proceed for fear of sending multiple copies"
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 !~ /^[\w\d]+([\w\d\!\#\$\%\&\*\+\-\/\=\?\^\`\{\|\}\~\.]*[\w\d]+)*@([-\w\d]+\.)+[\w]{2,}$/
77
- print "Invalid email address: #{email_address}"
78
- self.failed = true
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
- # TODO: wrap this with begin;rescue;end and set failed/failure_reason in case of exception
85
- Mailer.email_campaign(self).deliver
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
@@ -0,0 +1,8 @@
1
+ class AddRecipientIdentifier < ActiveRecord::Migration
2
+
3
+ def change
4
+ add_column :email_campaign_recipients, :identifier, :string, :limit => 35
5
+ add_index :email_campaign_recipients, :identifier
6
+ end
7
+
8
+ end
@@ -1,3 +1,3 @@
1
1
  module EmailCampaign
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.3"
3
3
  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.2
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