Question

When sending mail in Rails, usually one would do something like this:

UserMailer.password_reset(user).deliver

But if we look inside UserMailer we can see this:

def password_reset(user) # not self.password_reset
  # ...
end

Notice that the method name is not prefixed with self. Looking at it, it seems like you need to instantiate the object first as below. How does Rails do this?

UserMailer.new.password_reset(user).deliver
Was it helpful?

Solution

That's a great question. In the source (https://github.com/rails/rails/blob/master/actionmailer/lib/action_mailer/base.rb), Rails uses method_missing to create a new instance of the ActionMailer. Here's the relevant excerpt from the source:

def method_missing(method_name, *args) # :nodoc:
  if respond_to?(method_name)
    new(method_name, *args).message
  else
    super
  end
end

OTHER TIPS

Please note that the self.deliver method is deprecated, have a look at the code given below. Since deliver method is defined as a class method you don't have to instantiate the mailer class.

action_mailer/deprecated_api.rb

module ActionMailer
  module DeprecatedApi #:nodoc:
  extend ActiveSupport::Concern

  module ClassMethods
  # Deliver the given mail object directly. This can be used to deliver
  # a preconstructed mail object, like:
  #
  #   email = MyMailer.create_some_mail(parameters)
  #   email.set_some_obscure_header "frobnicate"
  #   MyMailer.deliver(email)
  def deliver(mail, show_warning=true)
    if show_warning
      ActiveSupport::Deprecation.warn "#{self}.deliver is deprecated, call " <<
        "deliver in the mailer instance instead", caller[0,2]
    end

    raise "no mail object available for delivery!" unless mail
    wrap_delivery_behavior(mail)
    mail.deliver
    mail
  end

Sending mail

Once a mailer action and template are defined, you can deliver your message or create it and save it for delivery later:

mail = Notifier.welcome(david)  # => a Mail::Message object
mail.deliver  # sends the email

#Above 2 steps can be combined
Notifier.welcome(david).deliver

You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.

The method password_reset returns a Mail::Message object which can then just be told deliver to send itself out.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top