Skip to content

Commit

Permalink
Added :delivery_method to allow another object to handle the delivery…
Browse files Browse the repository at this point in the history
… as well as :deliver! to bypass normal delivery actions
  • Loading branch information
mikel committed Jan 25, 2010
1 parent c156de4 commit a44c999
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 49 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.rdoc
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
== Mon Jan 25 01:44:13 UTC 2010 Mikel Lindsaar <raasdnil@gmail.com>

* Change :deliver! to deliver a mail object, bypassing the :perform_deliveries and :raise_delivery_errors flags, also does not append the mail object to Mail.deliveries, thus the ! (dangerous). The intended use for :deliver! is for people wanting to have their own delivery_handler (like ActionMailer uses) to track and handle delivery failures.
* Added :delivery_handler to Message. Allows you to pass an object that will be sent :deliver_mail(self) by the Mail::Message instance when it is sent :deliver and bypasses the usual delivery method.

== Sat Jan 23 23:49:50 UTC 2010 Mikel Lindsaar <raasdnil@gmail.com>

* Version bump to 2.0.5
Expand Down
101 changes: 89 additions & 12 deletions lib/mail/message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,12 @@ def initialize(*args, &block)

@perform_deliveries = true
@raise_delivery_errors = true

@delivery_handler = nil

@delivery_method = Mail.delivery_method.dup
@delivery_notification_observers = []

if args.flatten.first.respond_to?(:each_pair)
init_with_hash(args.flatten.first)
else
Expand All @@ -118,7 +121,65 @@ def initialize(*args, &block)
self
end

# If you assign a delivery handler, mail will call :deliver_mail on the
# object you assign to delivery_handler, it will pass itself as the
# single argument.
#
# If you define a delivery_handler, then you are responsible for the
# following actions in the delivery cycle:
#
# * Appending the mail object to Mail.deliveries as you see fit.
# * Checking the mail.perform_deliveries flag to decide if you should
# actually call :deliver! the mail object or not.
# * Checking the mail.raise_delivery_errors flag to decide if you
# should raise delivery errors if they occur.
# * Actually calling :deliver! (with the bang) on the mail object to
# get it to deliver itself.
#
# A simple implementation of a delivery_handler would be
#
# class MyObject
#
# def initialize
# @mail = Mail.new('To: mikel@test.lindsaar.net')
# @mail.delivery_handler = self
# end
#
# attr_accessor :mail
#
# def deliver_mail(mail)
# begin
# mail.deliver! if mail.perform_deliveries
# Mail.deliveries << mail
# rescue e => Exception
# raise e if mail.raise_delivery_errors
# end
# end
# end
#
# Then doing:
#
# obj = MyObject.new
# obj.mail.deliver
#
# Would cause Mail to call obj.deliver_mail passing itself as a parameter,
# which then would call #deliver! on the mail object which then goes ahead
# and delivers itself.
attr_accessor :delivery_handler

# If set to false, mail will go through the motions of doing a delivery,
# but not actually call the delivery method. It will however still append
# the email to the Mail.deliveries object. Useful for testing
#
# This setting is ignored by mail (though still available as a flag) if you
# define a delivery_handler
attr_accessor :perform_deliveries

# If set to false, mail will silently catch and ignore any exceptions
# raised through attempting to deliver an email.
#
# This setting is ignored by mail (though still available as a flag) if you
# define a delivery_handler
attr_accessor :raise_delivery_errors

def register_for_delivery_notification(observer)
Expand All @@ -138,22 +199,29 @@ def inform_observers
# Examples:
#
# mail = Mail.read('file.eml')
# mail.deliver!
# mail.deliver
def deliver
if perform_deliveries
begin
delivery_method.deliver!(self)
Mail.deliveries << self
rescue Exception => e # Net::SMTP errors or sendmail pipe errors
raise e if raise_delivery_errors
end
if delivery_handler
delivery_handler.deliver_mail(self)
else
do_delivery
inform_observers
end
self
end

# This method bypasses checking perform_deliveries and raise_delivery_errors,
# it also does not append the mail object to the Mail.deliveries array, so use
# with caution. It still however fires callbacks to the observers if they
# are defined.
#
# Returns self
def deliver!
delivery_method.deliver!(self)
inform_observers
self
end

alias :deliver! :deliver

def delivery_method(method = nil, settings = {})
unless method
@delivery_method
Expand Down Expand Up @@ -1571,7 +1639,7 @@ def filename
find_attachment
end

private
private

# 2.1. General Description
# A message consists of header fields (collectively called "the header
Expand Down Expand Up @@ -1679,5 +1747,14 @@ def find_attachment
filename
end

def do_delivery
begin
delivery_method.deliver!(self) if perform_deliveries
Mail.deliveries << self
rescue Exception => e # Net::SMTP errors or sendmail pipe errors
raise e if raise_delivery_errors
end
end

end
end
8 changes: 8 additions & 0 deletions spec/mail/message_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1723,4 +1723,12 @@ def basic_email
mail.parts.first.parts[1].body.decoded.should == "<b>test</b> HTML<br/>\nline #2"
end
end

describe "deliver" do
it "should return self after delivery" do
mail = Mail.new
mail.perform_deliveries = false
mail.deliver.should == mail
end
end
end
4 changes: 2 additions & 2 deletions spec/mail/network/delivery_methods/test_mailer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
subject 'testing'
body 'hello'
end
mail.deliver!
mail.deliver
Mail.deliveries.length.should == 1
Mail.deliveries.first.should == mail
end
Expand All @@ -48,7 +48,7 @@
subject 'testing'
body 'hello'
end
mail.deliver!
mail.deliver
Mail.deliveries.length.should == 1
Mail.deliveries.clear
Mail.deliveries.should be_empty
Expand Down
143 changes: 108 additions & 35 deletions spec/mail/network_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ class MyDelivery; def initialize(settings); end; end

class MyRetriever; def initialize(settings); end; end

class MyDeliveryHandler
def deliver_mail(mail)
mail.deliver!
end
end

describe "Mail" do

before(:each) do
Expand Down Expand Up @@ -167,7 +173,7 @@ class MyRetriever; def initialize(settings); end; end
end

describe "sending emails via SMTP" do

before(:each) do
# Set the delivery method to test as the default
MockSMTP.clear_deliveries
Expand Down Expand Up @@ -205,7 +211,7 @@ class MyRetriever; def initialize(settings); end; end

end

describe "deliveries, perform delivery and observers" do
describe "deliveries" do

class MyDeliveryMethod
attr_accessor :settings
Expand All @@ -217,56 +223,123 @@ class MyObserver
def self.delivered_email(message); end
end

def message
@message ||= Mail.new do
before(:each) do
@message = Mail.new do
from 'mikel@test.lindsaar.net'
to 'ada@test.lindsaar.net'
subject 'Re: No way!'
body 'Yeah sure'
end
@message.delivery_method :test
end

it "should add itself to the deliveries collection on mail on delivery" do
Mail.deliveries.clear
message.deliver
Mail.deliveries.length.should == 1
end
describe "adding to Mail.deliveries" do
it "should add itself to the deliveries collection on mail on delivery" do
Mail.deliveries.clear
@message.deliver
Mail.deliveries.length.should == 1
end

it "should call deliver on the delivery method by default" do
delivery_agent = MyDeliveryMethod.new
message.should_receive(:delivery_method).and_return(delivery_agent)
delivery_agent.should_receive(:deliver!).with(message)
message.deliver
it "should add itself to the deliveries even if told not to perform_deliveries" do
Mail.deliveries.clear
@message.perform_deliveries = false
@message.deliver
Mail.deliveries.length.should == 1
end
end

describe "perform_deliveries" do
it "should call deliver! on the delivery method by default" do
delivery_agent = MyDeliveryMethod.new
@message.should_receive(:delivery_method).and_return(delivery_agent)
delivery_agent.should_receive(:deliver!).with(@message)
@message.deliver
end

it "should not call deliver if perform deliveries is set to false" do
message.perform_deliveries = false
delivery_agent = MyDeliveryMethod.new
message.should_not_receive(:delivery_method).and_return(delivery_agent)
delivery_agent.should_not_receive(:deliver!)
message.deliver
it "should not call deliver if perform deliveries is set to false" do
@message.perform_deliveries = false
delivery_agent = MyDeliveryMethod.new
@message.should_not_receive(:delivery_method).and_return(delivery_agent)
delivery_agent.should_not_receive(:deliver!)
@message.deliver
end
end

it "should tell it's observers that it was told to deliver an email" do
message.register_for_delivery_notification(MyObserver)
MyObserver.should_receive(:delivered_email).with(message).once
message.deliver
describe "observers" do
it "should tell it's observers that it was told to deliver an email" do
@message.register_for_delivery_notification(MyObserver)
MyObserver.should_receive(:delivered_email).with(@message).once
@message.deliver
end

it "should tell it's observers that it was told to deliver an email even if perform_deliveries is false" do
@message.register_for_delivery_notification(MyObserver)
@message.perform_deliveries = false
MyObserver.should_receive(:delivered_email).with(@message).once
@message.deliver
end

it "should tell it's observers that it was told to deliver an email even if it is using a delivery_handler" do
@message.delivery_handler = MyDeliveryHandler.new
@message.register_for_delivery_notification(MyObserver)
@message.perform_deliveries = false
MyObserver.should_receive(:delivered_email).with(@message).once
@message.deliver
end

end

it "should pass on delivery errors if raised" do
delivery_agent = MyDeliveryMethod.new
message.stub!(:delivery_method).and_return(delivery_agent)
delivery_agent.stub!(:deliver!).and_raise(Exception)
doing { message.deliver }.should raise_error(Exception)
describe "raise_delivery_errors" do
it "should pass on delivery errors if raised" do
delivery_agent = MyDeliveryMethod.new
@message.stub!(:delivery_method).and_return(delivery_agent)
delivery_agent.stub!(:deliver!).and_raise(Exception)
doing { @message.deliver }.should raise_error(Exception)
end

it "should not pass on delivery errors if raised raise_delivery_errors is set to false" do
delivery_agent = MyDeliveryMethod.new
@message.stub!(:delivery_method).and_return(delivery_agent)
@message.raise_delivery_errors = false
delivery_agent.stub!(:deliver!).and_raise(Exception)
doing { @message.deliver }.should_not raise_error(Exception)
end
end

it "should not pass on delivery errors if raised raise_delivery_errors is set to false" do
delivery_agent = MyDeliveryMethod.new
message.stub!(:delivery_method).and_return(delivery_agent)
message.raise_delivery_errors = false
delivery_agent.stub!(:deliver!).and_raise(Exception)
doing { message.deliver }.should_not raise_error(Exception)
describe "delivery_handler" do

it "should allow you to hand off performing the actual delivery to another object" do
delivery_handler = MyDeliveryHandler.new
delivery_handler.should_receive(:deliver_mail).with(@message).exactly(:once)
@message.delivery_handler = delivery_handler
@message.deliver
end

it "mail should be told to :deliver once and then :deliver! once by the delivery handler" do
@message.delivery_handler = MyDeliveryHandler.new
@message.should_receive(:deliver!).exactly(:once)
@message.deliver
end

it "mail only call it's delivery_method once" do
@message.delivery_handler = MyDeliveryHandler.new
@message.should_receive(:delivery_method).exactly(:once).and_return(Mail::TestMailer.new({}))
@message.deliver
end

it "mail should not catch any exceptions when using a delivery_handler" do
@message.delivery_handler = MyDeliveryHandler.new
@message.should_receive(:delivery_method).and_raise(Exception)
doing { @message.deliver }.should raise_error(Exception)
end

it "mail should not modify the Mail.deliveries object if using a delivery_handler" do
@message.delivery_handler = MyDeliveryHandler.new
doing { @message.deliver }.should_not change(Mail, :deliveries)
end

end

end

end

0 comments on commit a44c999

Please sign in to comment.