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