Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added :delivery_method to allow another object to handle the delivery…

… as well as :deliver! to bypass normal delivery actions
  • Loading branch information...
commit a44c999e7d80a8ba39b1f552eec15346b01cfad1 1 parent c156de4
@mikel authored
View
5 CHANGELOG.rdoc
@@ -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
View
101 lib/mail/message.rb
@@ -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
@@ -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)
@@ -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
@@ -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
@@ -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
View
8 spec/mail/message_spec.rb
@@ -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
View
4 spec/mail/network/delivery_methods/test_mailer_spec.rb
@@ -33,7 +33,7 @@
subject 'testing'
body 'hello'
end
- mail.deliver!
+ mail.deliver
Mail.deliveries.length.should == 1
Mail.deliveries.first.should == mail
end
@@ -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
View
143 spec/mail/network_spec.rb
@@ -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
@@ -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
@@ -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
@@ -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
Please sign in to comment.
Something went wrong with that request. Please try again.