How can you call class methods on mailers when they're not defined as such?

寵の児 提交于 2019-12-10 02:52:06

问题


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

回答1:


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



回答2:


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.



来源:https://stackoverflow.com/questions/17088619/how-can-you-call-class-methods-on-mailers-when-theyre-not-defined-as-such

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!