`method_missing': Emailer类没有定义mail方法 (NoMethodError)

3

我正在学习Peter Cooper的《Beginning Ruby》这本书。
其中有一个简单的例子,使用ActionMailer发送电子邮件。 以下是该示例代码:

require 'action_mailer'

ActionMailer::Base.smtp_settings = {
    :address => "smtp.mail.yahoo.com",
    :port => 465,
    :authentication => :login,
    :user_name => "username@yahoo.com",
    :password => "password",
    :openssl_verify_mode => :ssl
}

class Emailer < ActionMailer::Base
  def self.test_email(email_address, email_body)
    mail(to: email_address, from: 'username@yahoo.com', subject: 'action mailer test', body: email_body)
  end
end

Emailer.test_email('username@gmail.com', 'This is a test e-mail!').deliver_now

正如您所看到的,我想使用雅虎的SMTP服务器向我的Gmail帐户发送电子邮件。 我提供了所需的smtp设置并创建了类方法test_email来使用main方法和deliver_now方法发送电子邮件。 但是,我收到以下错误消息:
/home/asarluhi/.rvm/gems/ruby-2.5.1@beginning_ruby/gems/actionmailer-5.2.0/lib/action_mailer/base.rb:582:in `method_missing': undefined method `mail' for Emailer:Class (NoMethodError)

我还尝试将test_email方法更改为实例方法并使用Emailer.new.test_email...在这种情况下,我收到的错误消息是:

/home/asarluhi/.rvm/gems/ruby-2.5.1@beginning_ruby/gems/mail-2.7.0/lib/mail/message.rb:1396:in `method_missing': undefined method `deliver_now' for #<Mail::Message:0x0000000002e0eac8> (NoMethodError)

我不知道为什么在 Ruby 5.2.0 中方法 mail 和方法 deliver_now 都没有被识别出来,实际上这两个方法都属于 ActionMailer::Base。如果我从 test_email 方法中删除 self(这是原书中的代码,我以为是错误),当我运行包含以上代码的 rb 文件时,会收到一个长长的回溯(21 行),并显示以下错误信息:
/home/asarluhi/.rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/net/protocol.rb:189:in `rbuf_fill': end of file reached (EOFError)

根据作者的说法,ActionMailer可以独立于Rails使用。因此,我在一个专门的rvm gemset中安装了ActionMailer gem,并从书中提取了上面的代码写入一个rb文件。运行这个脚本时,我收到了EOFError错误。我尝试让它工作的所有尝试都失败了,例如将test_email方法添加self或在Emailer类的实例上调用test_email方法。


1
尝试使用deliver,据我记得,这在Rails 5中已经改变了。 - Marek Lipka
1
@Asarluhi:将其设置为实例方法,但将其作为类方法进行调用。Action Mailer 类就是这样奇怪。 - Sergio Tulentsev
1
@Asarluhi:在你编辑文件时,你把结尾搞砸了。检查每个开头关键字(classdef等)是否都有相应的结尾(end)。 - Sergio Tulentsev
1
事实上,我刚意识到我的关于不平衡端的假设是基于经验判断的(而且可能会成为其他问题的问题)。尽管如此,我仍需要一个MCVE来进一步帮助你。 - Sergio Tulentsev
1
@Asarluhi:确实,我现在遇到了EOF错误。SMTP凭据有效吗? - Sergio Tulentsev
显示剩余14条评论
3个回答

1
问题是使用了 'self'。必须为


def test_email(email_address, email_body)
  ...
end

代替

def self.test_email(email_address, email_body)
  ...
end

因此,Emailer.test_email.deliver_now 将起作用。在 docs 中我们可以看到 mail 是实例方法,但在 self.method_name 中只有类方法可用。类方法是属于该类的一部分功能,但不与任何特定单个实例绑定。反向语句也同样适用。 为什么 Emailer 是实例而不是类?这是来自 ActionMailer::Base 的魔法。


1
但是test_email并不是该类的初始化器 - 但它并不是。 "initializer"在Ruby中具有特定的含义。尽管如此,其中肯定有一些魔法。 - Sergio Tulentsev
另外,Emailer.test_email 返回的是 ActionMailer::MessageDelivery,而不是 Emailer - Sergio Tulentsev
是的,你说得对,那不是初始化程序,并且返回特定对象(从方法邮件中)Mail::Message。 - Leo
@Leo 你说得对,这就是原始代码的写法,但它仍然无法工作,因为我收到了一个end of file reached (EOFError)的错误,附带着一长串的回溯信息(21行)。有些问题我看不出来。 - Asarluhi

1

我没有雅虎账号来检查,但这个配置已经可以使用我的Gmail账号:

require 'action_mailer'

ActionMailer::Base.delivery_method = :smtp
ActionMailer::Base.smtp_settings = {
    :address => "smtp.gmail.com",
    :port => 587,
    :authentication => :plain,
    :user_name => "sender@gmail.com",
    :password => "<password>",
    :enable_starttls_auto => true
}


class Emailer < ActionMailer::Base
  def test_email(email_address, email_body)
    mail(to: email_address, from: 'sender@gmail.com', subject: 'action mailer test', body: email_body)
  end
end

Emailer.test_email('recipient@gmail.com', 'This is a test e-mail!').deliver_now

显然对于雅虎,需要使用端口587和:enable_starttls_auto => true。通过这些更改现在它可以工作了。我也有一个Gmail账户,但是我收到了一个Net::SMTPAuthenticationError错误以及我的收件箱中的一条通知,称有一个应用程序尝试被阻止登录,可能会使我的账户面临风险。他们说我可以继续使用此应用程序,方法是允许访问不安全的应用程序,但这可能会使我的帐户容易受到攻击。 - Asarluhi

0

导致此错误消息的一种方式是您可能只是使用了错误的命名空间,例如我曾经

AlertsMailer.new_user_signup(user).deliver_now

但对我来说,它应该是这样的

MessagesMailer.new_user_signup(user).deliver_now

所以Rails找不到该名称空间的方法。像我学到的那样,仔细检查您的代码是值得的:)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接