开发者

rails 3: how to abort delivery method in actionmailer?

开发者 https://www.devze.com 2023-03-16 15:14 出处:网络
In my mailer controller, under certain conditions (missing data) we abort sending the email. How do I exit the controller method without still rendering a view in that case?

In my mailer controller, under certain conditions (missing data) we abort sending the email.

How do I exit the controller method without still rendering a view in that case?

return if @some_email_data.nil?

Doesn't do the trick since the view is still rendered (throwing an error every place I try to use @some_email_data unless I add a lot of nil che开发者_运维知识库cks)

And even if I do the nil checks, it complains there's no 'sender' (because I supposed did a 'return' before getting to the line where I set the sender and subject.

Neither does render ... return

Basically, RETURN DOESN'T RETURN inside a mailer method!


A much simpler solution than the accepted answer would be something like:

class SomeMailer < ActionMailer::Base
  def some_method
    if @some_email_data.nil?
      self.message.perform_deliveries = false
    else
      mail(...)  
    end  
  end
end

If you're using Rails 3.2.9 (or later things even better) - there you can finally conditionally call mail(). Here's the related GitHub thread. Now the code can be reworked like this:

class SomeMailer < ActionMailer::Base
  def some_method
    unless @some_email_data.nil?
      mail(...)  
    end  
  end
end


I just encountered same thing here.

My solution was following:

module BulletproofMailer
  class BlackholeMailMessage < Mail::Message
    def self.deliver
      false
    end
  end

  class AbortDeliveryError < StandardError
  end

  class Base < ActionMailer::Base

    def abort_delivery
      raise AbortDeliveryError
    end

    def process(*args)
      begin
        super *args
      rescue AbortDeliveryError
        self.message = BulletproofMailer::BlackholeMailMessage
      end
    end
  end
end

Using these wrapper mailer would look like this:

class EventMailer < BulletproofMailer::Base
  include Resque::Mailer
  def event_created(event_id)
    begin
      @event = CalendarEvent.find(event_id)
    rescue ActiveRecord::RecordNotFound
      abort_delivery
    end
  end
end

It is also posted in my blog.


I've found this method that seems the least-invasive, as it works across all mailer methods without requiring you to remember to catch an error. In our case, we just want a setting to completely disable mailers for certain environments. Tested in Rails 6, although I'm sure it'll work just fine in Rails 5 as well, maybe lower.

class ApplicationMailer < ActionMailer::Base
  class AbortDeliveryError < StandardError; end

  before_action :ensure_notifications_enabled
  rescue_from AbortDeliveryError, with: -> {}

  def ensure_notifications_enabled
    raise AbortDeliveryError.new unless <your_condition>
  end

  ...

end

The empty lambda causes Rails 6 to just return an ActionMailer::Base::NullMail instance, which doesn't get delivered (same as if your mailer method didn't call mail, or returned prematurely).


Setting self.message.perform_deliveries = false did not work for me.

I used a similar approach as some of the other answers - using error handling to control the flow and prevent the mail from being sent.

The example below is aborting mail from being sent in non-Production ENVs to non-whitelisted emails, but the helper method logic can be whatever you need for your scenario.

class BaseMailer < ActionMailer::Base
  class AbortedMailer < StandardError; end

  def mail(**args)
    whitelist_mail_delivery(args[:to])
    super(args)
  rescue AbortedMailer
    Rails.logger.info "Mail aborted! We do not send emails to external email accounts outside of Production ENV"
  end

  private

  def whitelist_mail_delivery(to_email)
    return if Rails.env.production?

    raise AbortedMailer.new unless internal_email?(to_email)
  end

  def internal_email?(to_email)
    to_email.include?('@widgetbusiness.com')
  end
end


I just clear the @to field and return, so deliver aborts when it doesn't have anything there. (Or just return before setting @to).


I haven't spent much time with rails 3 but you could try using

redirect_to some_other_route

alternatively, if you're really just checking for missing data you could do a js validation of the form fields and only submit if it passes.

0

精彩评论

暂无评论...
验证码 换一张
取 消