zendesk-ar_mailer 1.4.6 → 2.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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