zendesk-ar_mailer 1.4.6 → 2.1.5

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.
@@ -1,17 +1,9 @@
1
1
  require 'optparse'
2
2
  require 'net/smtp'
3
- require 'smtp_tls'
3
+ require 'smtp_tls' unless Net::SMTP.instance_methods.include?("enable_starttls_auto")
4
4
  require 'ar_sendmail_logger'
5
5
  require 'rubygems'
6
6
 
7
- class Object # :nodoc:
8
- unless respond_to? :path2class then
9
- def self.path2class(path)
10
- path.split(/::/).inject self do |k,n| k.const_get n end
11
- end
12
- end
13
- end
14
-
15
7
  ##
16
8
  # Hack in RSET
17
9
 
@@ -30,8 +22,6 @@ class SMTP # :nodoc:
30
22
  end
31
23
  end
32
24
 
33
- module ActionMailer; end # :nodoc:
34
-
35
25
  ##
36
26
  # ActionMailer::ARSendmail delivers email from the email table to the
37
27
  # SMTP server configured in your application's config/environment.rb.
@@ -46,16 +36,15 @@ module ActionMailer; end # :nodoc:
46
36
  # The interesting options are:
47
37
  # * --daemon
48
38
  # * --mailq
49
- # * --create-migration
50
- # * --create-model
51
- # * --table-name
39
+
40
+ module ActionMailer; end
52
41
 
53
42
  class ActionMailer::ARSendmail
54
43
 
55
44
  ##
56
45
  # The version of ActionMailer::ARSendmail you are running.
57
46
 
58
- VERSION = '1.4.6'
47
+ VERSION = '2.1.5'
59
48
 
60
49
  ##
61
50
  # Maximum number of times authentication will be consecutively retried
@@ -81,13 +70,7 @@ class ActionMailer::ARSendmail
81
70
  # Be verbose
82
71
 
83
72
  attr_accessor :verbose
84
-
85
- ##
86
- # ActiveRecord class that holds emails
87
-
88
- attr_reader :email_class
89
-
90
- attr_reader :logger
73
+
91
74
 
92
75
  ##
93
76
  # True if only one delivery attempt will be made per call to run
@@ -98,6 +81,11 @@ class ActionMailer::ARSendmail
98
81
  # Times authentication has failed
99
82
 
100
83
  attr_accessor :failed_auth_count
84
+
85
+ ##
86
+ # Logs the value of this header for successfully sent emails
87
+
88
+ attr_accessor :log_header
101
89
 
102
90
  @@pid_file = nil
103
91
 
@@ -109,41 +97,6 @@ class ActionMailer::ARSendmail
109
97
  end
110
98
  end
111
99
 
112
- ##
113
- # Creates a new migration using +table_name+ and prints it on stdout.
114
-
115
- def self.create_migration(table_name)
116
- require 'active_support'
117
- puts <<-EOF
118
- class Add#{table_name.classify} < ActiveRecord::Migration
119
- def self.up
120
- create_table :#{table_name.tableize} do |t|
121
- t.column :from, :string
122
- t.column :to, :string
123
- t.column :last_send_attempt, :integer, :default => 0
124
- t.column :mail, :text
125
- t.column :created_on, :datetime
126
- end
127
- end
128
-
129
- def self.down
130
- drop_table :#{table_name.tableize}
131
- end
132
- end
133
- EOF
134
- end
135
-
136
- ##
137
- # Creates a new model using +table_name+ and prints it on stdout.
138
-
139
- def self.create_model(table_name)
140
- require 'active_support'
141
- puts <<-EOF
142
- class #{table_name.classify} < ActiveRecord::Base
143
- end
144
- EOF
145
- end
146
-
147
100
  ##
148
101
  # Prints a list of unsent emails and the last delivery attempt, if any.
149
102
  #
@@ -151,9 +104,8 @@ end
151
104
  # known. See http://api.rubyonrails.org/classes/ActiveRecord/Timestamp.html
152
105
  # to learn how to enable ActiveRecord::Timestamp.
153
106
 
154
- def self.mailq(table_name)
155
- klass = table_name.split('::').inject(Object) { |k,n| k.const_get n }
156
- emails = klass.find :all
107
+ def self.mailq
108
+ emails = ActionMailer::Base.email_class.find :all
157
109
 
158
110
  if emails.empty? then
159
111
  puts "Mail queue is empty"
@@ -202,9 +154,7 @@ end
202
154
  options[:MaxAge] = 86400 * 7
203
155
  options[:Once] = false
204
156
  options[:RailsEnv] = ENV['RAILS_ENV']
205
- options[:TableName] = 'Email'
206
157
  options[:Pidfile] = options[:Chdir] + '/log/ar_sendmail.pid'
207
- options[:LogFile] = nil
208
158
 
209
159
  opts = OptionParser.new do |opts|
210
160
  opts.banner = "Usage: #{name} [options]"
@@ -257,12 +207,6 @@ end
257
207
  options[:Pidfile] = pidfile
258
208
  end
259
209
 
260
- opts.on("-l", "--logfile LOGFILE",
261
- "Set the logfile location",
262
- "Default: #{options[:Chdir]}#{options[:LogFile]}", String) do |logfile|
263
- options[:LogFile] = logfile
264
- end
265
-
266
210
  opts.on( "--mailq",
267
211
  "Display a list of emails waiting to be sent") do |mailq|
268
212
  options[:MailQ] = true
@@ -271,18 +215,6 @@ end
271
215
  opts.separator ''
272
216
  opts.separator 'Setup Options:'
273
217
 
274
- opts.on( "--create-migration",
275
- "Prints a migration to add an Email table",
276
- "to stdout") do |create|
277
- options[:Migrate] = true
278
- end
279
-
280
- opts.on( "--create-model",
281
- "Prints a model for an Email ActiveRecord",
282
- "object to stdout") do |create|
283
- options[:Model] = true
284
- end
285
-
286
218
  opts.separator ''
287
219
  opts.separator 'Generic Options:'
288
220
 
@@ -294,20 +226,23 @@ end
294
226
  options[:Chdir] = path
295
227
  end
296
228
 
229
+ opts.on( "--log-file PATH_AND_FILE",
230
+ "Full path to the file the mailer should log to",
231
+ "Default: RAILS_ROOT/log/RAILS_ENV.log") do |log_file|
232
+ options[:LogFile] = log_file
233
+ end
234
+
235
+ opts.on( "--log-header SOME_HEADER",
236
+ "Logs the value of the specified header once an email gets sent") do |header|
237
+ options[:LogHeader] = header
238
+ end
239
+
297
240
  opts.on("-e", "--environment RAILS_ENV",
298
241
  "Set the RAILS_ENV constant",
299
242
  "Default: #{options[:RailsEnv]}") do |env|
300
243
  options[:RailsEnv] = env
301
244
  end
302
245
 
303
- opts.on("-t", "--table-name TABLE_NAME",
304
- "Name of table holding emails",
305
- "Used for both sendmail and",
306
- "migration creation",
307
- "Default: #{options[:TableName]}") do |name|
308
- options[:TableName] = name
309
- end
310
-
311
246
  opts.on("-v", "--[no-]verbose",
312
247
  "Be verbose",
313
248
  "Default: #{options[:Verbose]}") do |verbose|
@@ -319,23 +254,26 @@ end
319
254
  usage opts
320
255
  end
321
256
 
257
+ opts.on("--version", "Version of ARMailer") do
258
+ usage "ar_mailer #{VERSION} (adzap fork)"
259
+ end
260
+
322
261
  opts.separator ''
323
262
  end
324
263
 
325
264
  opts.parse! args
326
265
 
327
- return options if options.include? :Migrate or options.include? :Model
328
-
329
266
  ENV['RAILS_ENV'] = options[:RailsEnv]
330
267
 
331
268
  Dir.chdir options[:Chdir] do
332
269
  begin
333
270
  require 'config/environment'
271
+ require 'action_mailer/ar_mailer'
334
272
  rescue LoadError
335
273
  usage opts, <<-EOF
336
274
  #{name} must be run from a Rails application's root to deliver email.
337
275
  #{Dir.pwd} does not appear to be a Rails application root.
338
- EOF
276
+ EOF
339
277
  end
340
278
  end
341
279
 
@@ -348,14 +286,8 @@ end
348
286
  def self.run(args = ARGV)
349
287
  options = process_args args
350
288
 
351
- if options.include? :Migrate then
352
- create_migration options[:TableName]
353
- exit
354
- elsif options.include? :Model then
355
- create_model options[:TableName]
356
- exit
357
- elsif options.include? :MailQ then
358
- mailq options[:TableName]
289
+ if options.include? :MailQ then
290
+ mailq
359
291
  exit
360
292
  end
361
293
 
@@ -410,24 +342,22 @@ end
410
342
  # Valid options are:
411
343
  # <tt>:BatchSize</tt>:: Maximum number of emails to send per delay
412
344
  # <tt>:Delay</tt>:: Delay between deliver attempts
413
- # <tt>:TableName</tt>:: Table name that stores the emails
414
345
  # <tt>:Once</tt>:: Only attempt to deliver emails once when run is called
415
346
  # <tt>:Verbose</tt>:: Be verbose.
416
347
 
417
348
  def initialize(options = {})
418
349
  options[:Delay] ||= 60
419
- options[:TableName] ||= 'Email'
420
350
  options[:MaxAge] ||= 86400 * 7
421
351
 
422
352
  @batch_size = options[:BatchSize]
423
353
  @delay = options[:Delay]
424
- @email_class = Object.path2class options[:TableName]
425
354
  @once = options[:Once]
426
355
  @verbose = options[:Verbose]
427
356
  @max_age = options[:MaxAge]
428
-
357
+ @log_header = options[:LogHeader]
358
+
429
359
  @failed_auth_count = 0
430
- @logger = ArSendmailLogger.new(options[:LogFile] || options[:Chdir] + '/log/ar_sendmail.log')
360
+ @logger = options[:LogFile] ? ARSendmailLogger.new(options[:LogFile]) : ActionMailer::Base.logger
431
361
  end
432
362
 
433
363
  ##
@@ -438,7 +368,7 @@ end
438
368
  return if @max_age == 0
439
369
  timeout = Time.now - @max_age
440
370
  conditions = ['last_send_attempt > 0 and created_on < ?', timeout]
441
- mail = @email_class.destroy_all conditions
371
+ mail = ActionMailer::Base.email_class.destroy_all conditions
442
372
 
443
373
  log "expired #{mail.length} emails from the queue"
444
374
  end
@@ -447,29 +377,40 @@ end
447
377
  # Delivers +emails+ to ActionMailer's SMTP server and destroys them.
448
378
 
449
379
  def deliver(emails)
450
- return if emails.empty?
451
- user = smtp_settings[:user] || smtp_settings[:user_name]
452
- Net::SMTP.start smtp_settings[:address], smtp_settings[:port],
453
- smtp_settings[:domain], user,
454
- smtp_settings[:password],
455
- smtp_settings[:authentication],
456
- smtp_settings[:tls] do |smtp|
380
+ settings = [
381
+ smtp_settings[:domain],
382
+ (smtp_settings[:user] || smtp_settings[:user_name]),
383
+ smtp_settings[:password],
384
+ smtp_settings[:authentication]
385
+ ]
386
+
387
+ smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
388
+ if smtp.respond_to?(:enable_starttls_auto)
389
+ smtp.enable_starttls_auto unless smtp_settings[:tls] == false
390
+ else
391
+ settings << smtp_settings[:tls]
392
+ end
393
+
394
+ smtp.start(*settings) do |session|
457
395
  @failed_auth_count = 0
458
396
  until emails.empty? do
459
397
  email = emails.shift
460
398
  begin
461
- res = smtp.send_message email.mail, email.from, email.to
462
- email.destroy
463
- if email.respond_to?(:context) && email.context.to_s != ''
464
- log "sent email %011d [%s] from %s to %s: %p" % [email.id, email.context, email.from, email.to, res]
465
- else
466
- log "sent email %011d from %s to %s: %p" % [email.id, email.from, email.to, res]
399
+ res = session.send_message email.mail, email.from, email.to
400
+ hdr = ''
401
+
402
+ if @log_header && email.mail =~ /#{@log_header}: (.+)/
403
+ hdr = "[#{$1.chomp}] "
467
404
  end
405
+
406
+ email.destroy
407
+ log "sent email %011d %sfrom %s to %s: %p" %
408
+ [email.id, hdr, email.from, email.to, res]
468
409
  rescue Net::SMTPFatalError => e
469
410
  log "5xx error sending email %d, removing from queue: %p(%s):\n\t%s" %
470
411
  [email.id, e.message, e.class, e.backtrace.join("\n\t")]
471
412
  email.destroy
472
- smtp.reset
413
+ session.reset
473
414
  rescue Net::SMTPServerBusy => e
474
415
  log "server too busy, sleeping #{@delay} seconds"
475
416
  sleep delay
@@ -479,7 +420,7 @@ end
479
420
  email.save rescue nil
480
421
  log "error sending email %d: %p(%s):\n\t%s" %
481
422
  [email.id, e.message, e.class, e.backtrace.join("\n\t")]
482
- smtp.reset
423
+ session.reset
483
424
  end
484
425
  end
485
426
  end
@@ -512,7 +453,7 @@ end
512
453
  def find_emails
513
454
  options = { :conditions => ['last_send_attempt < ?', Time.now.to_i - 300] }
514
455
  options[:limit] = batch_size unless batch_size.nil?
515
- mail = @email_class.find :all, options
456
+ mail = ActionMailer::Base.email_class.find :all, options
516
457
 
517
458
  log "found #{mail.length} emails to send"
518
459
  mail
@@ -532,7 +473,7 @@ end
532
473
  def log(message)
533
474
  message = "ar_sendmail #{Time.now}: #{message}"
534
475
  $stderr.puts message if @verbose
535
- logger.info(message)
476
+ @logger.info message
536
477
  end
537
478
 
538
479
  ##
@@ -546,7 +487,8 @@ end
546
487
  now = Time.now
547
488
  begin
548
489
  cleanup
549
- deliver find_emails
490
+ emails = find_emails
491
+ deliver(emails) unless emails.empty?
550
492
  rescue ActiveRecord::Transactions::TransactionError
551
493
  end
552
494
  break if @once
@@ -567,4 +509,3 @@ end
567
509
  end
568
510
 
569
511
  end
570
-
@@ -1,6 +1,11 @@
1
1
  require 'logger'
2
2
 
3
- class ArSendmailLogger < ::Logger
3
+ class ARSendmailLogger < ::Logger
4
+
5
+ def initialize(path)
6
+ FileUtils.mkdir_p(File.dirname(path))
7
+ super(path)
8
+ end
4
9
 
5
10
  def format_message(severity, timestamp, progname, message)
6
11
  "#{message}\n"
@@ -11,7 +11,11 @@ require 'yaml'
11
11
  config_file = '/etc/ar_sendmail.conf'
12
12
 
13
13
  begin
14
- config = YAML::load(IO.read(config_file))
14
+ config = YAML::load(IO.read(config_file)) || {}
15
+ if config.empty? || (config.has_key?('defaults') && config.size == 1)
16
+ puts "No mailers defined. Exiting."
17
+ exit
18
+ end
15
19
  rescue Errno::ENOENT
16
20
  puts "Config file not found at '#{config_file}'!"
17
21
  exit
@@ -30,8 +34,7 @@ def start(app, options)
30
34
  end
31
35
 
32
36
  def stop(app, options)
33
- path = options['chdir']
34
- pid_file = File.expand_path(options['pidfile'], path)
37
+ pid_file = File.expand_path(options['pidfile'], options['chdir'])
35
38
  if File.exist? pid_file
36
39
  begin
37
40
  pid = open(pid_file).read.to_i
@@ -1,5 +1,5 @@
1
1
  require 'net/smtp'
2
- require 'smtp_tls'
2
+ require 'smtp_tls' unless Net::SMTP.instance_methods.include?("enable_starttls_auto")
3
3
  require 'time'
4
4
 
5
5
  class Net::SMTP
@@ -18,13 +18,7 @@ class Net::SMTP
18
18
  attr_reader :send_message_block
19
19
  attr_accessor :reset_called
20
20
 
21
- send :remove_method, :start
22
-
23
- end
24
-
25
- def self.start(*args)
26
- @start_block.call if @start_block
27
- yield new(nil)
21
+ # send :remove_method, :start
28
22
  end
29
23
 
30
24
  def self.on_send_message(&block)
@@ -32,7 +26,15 @@ class Net::SMTP
32
26
  end
33
27
 
34
28
  def self.on_start(&block)
35
- @start_block = block
29
+ if block_given?
30
+ @start_block = block
31
+ else
32
+ @start_block
33
+ end
34
+ end
35
+
36
+ def self.clear_on_start
37
+ @start_block = nil
36
38
  end
37
39
 
38
40
  def self.reset
@@ -42,6 +44,11 @@ class Net::SMTP
42
44
  @reset_called = 0
43
45
  end
44
46
 
47
+ def start(*args)
48
+ self.class.on_start.call if self.class.on_start
49
+ yield self
50
+ end
51
+
45
52
  alias test_old_reset reset if instance_methods.include? 'reset'
46
53
 
47
54
  def reset
@@ -68,6 +75,11 @@ class ActionMailer::Base
68
75
 
69
76
  @server_settings = {}
70
77
 
78
+ class << self
79
+ cattr_accessor :email_class
80
+ attr_accessor :delivery_method
81
+ end
82
+
71
83
  def self.logger
72
84
  o = Object.new
73
85
  def o.info(arg) end
@@ -82,6 +94,7 @@ class ActionMailer::Base
82
94
 
83
95
  def self.reset
84
96
  server_settings.clear
97
+ self.email_class = Email
85
98
  end
86
99
 
87
100
  def self.server_settings
@@ -105,7 +118,7 @@ class Email
105
118
 
106
119
  START = Time.parse 'Thu Aug 10 2006 11:19:48'
107
120
 
108
- attr_accessor :from, :to, :mail, :last_send_attempt, :created_on, :id, :context
121
+ attr_accessor :from, :to, :mail, :last_send_attempt, :created_on, :id
109
122
 
110
123
  @records = []
111
124
  @id = 0
@@ -114,7 +127,7 @@ class Email
114
127
 
115
128
  def self.create(record)
116
129
  record = new record[:from], record[:to], record[:mail],
117
- record[:last_send_attempt], record[:context]
130
+ record[:last_send_attempt]
118
131
  records << record
119
132
  return record
120
133
  end
@@ -147,14 +160,13 @@ class Email
147
160
  records.clear
148
161
  end
149
162
 
150
- def initialize(from, to, mail, last_send_attempt = nil, context = nil)
163
+ def initialize(from, to, mail, last_send_attempt = nil)
151
164
  @from = from
152
165
  @to = to
153
166
  @mail = mail
154
167
  @id = self.class.id += 1
155
168
  @created_on = START + @id
156
169
  @last_send_attempt = last_send_attempt || 0
157
- @context = context
158
170
  end
159
171
 
160
172
  def destroy
@@ -171,7 +183,7 @@ class Email
171
183
 
172
184
  end
173
185
 
174
- Mail = Email
186
+ Newsletter = Email
175
187
 
176
188
  class String
177
189
  def classify
@@ -183,4 +195,3 @@ class String
183
195
  end
184
196
 
185
197
  end
186
-
@@ -1,20 +1,13 @@
1
- require 'test/unit'
2
- require 'action_mailer'
3
- require 'action_mailer/ar_mailer'
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
4
2
 
5
- ##
6
- # Pretend mailer
7
-
8
- class Mailer < ActionMailer::ARMailer
3
+ class Mailer < ActionMailer::Base
4
+ self.delivery_method = :activerecord
9
5
 
10
6
  def mail
11
7
  @mail = Object.new
12
8
  def @mail.encoded() 'email' end
13
9
  def @mail.from() ['nobody@example.com'] end
14
10
  def @mail.destinations() %w[user1@example.com user2@example.com] end
15
- def @mail.header_string(header_name, default = nil)
16
- default
17
- end
18
11
  end
19
12
 
20
13
  end
@@ -25,15 +18,15 @@ class TestARMailer < Test::Unit::TestCase
25
18
  Mailer.email_class = Email
26
19
 
27
20
  Email.records.clear
28
- Mail.records.clear
21
+ Newsletter.records.clear
29
22
  end
30
23
 
31
24
  def test_self_email_class_equals
32
- Mailer.email_class = Mail
25
+ Mailer.email_class = Newsletter
33
26
 
34
27
  Mailer.deliver_mail
35
28
 
36
- assert_equal 2, Mail.records.length
29
+ assert_equal 2, Newsletter.records.length
37
30
  end
38
31
 
39
32
  def test_perform_delivery_activerecord