(NoMethodError:main:Object 的未定义方法“电子邮件”)。与 Mandrill 和 Mailchimp 确认设计后发送欢迎电子邮件

Posted

技术标签:

【中文标题】(NoMethodError:main:Object 的未定义方法“电子邮件”)。与 Mandrill 和 Mailchimp 确认设计后发送欢迎电子邮件【英文标题】:(NoMethodError: undefined method `email' for main:Object). Sending Welcome Email After Devise Confirm with Mandrill & Mailchimp 【发布时间】:2016-04-20 09:02:24 【问题描述】:

我目前正在重写 Devise Confirmable 方法以在用户确认其帐户后创建欢迎电子邮件。在当前设置下,在 Rails 控制台中运行 UserTransactionMailer.welcome_message(self).deliver_now 会导致以下错误:

    "NoMethodError: undefined method `email' for main:Object
 from /Users/AnthonyEmtman/Documents/projects/Team_Development/kons/app/mailers/user_transaction_mailer.rb:6:in `welcome_message'" 

下面是 user.rb 中的 def confirm! 覆盖,用于触发发送welcome_message 电子邮件。

模型/user.rb:

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, 
         :confirmable, :lockable, :timeoutable, :zxcvbnable

  def confirm!
    send_welcome_message
    super
  end

private

  def send_welcome_message
    UserTransactionMailer.welcome_message(self).deliver_now
  end

end

以下文件是我的 user_transaction_mailer.rb 和 base_mandrill_mailer.rb 文件。 user_transaction_mailer.rb 继承自 base_mandrill_mailer.rb,因此我创建的所有未来邮件程序都可以访问 mandrill_send 方法,有效地减少了我每次需要写入 mandrill_send 方法的发送代码量。

mailers/user_transaction_mailer.rb:

class UserTransactionMailer < BaseMandrillMailer

  def welcome_message(user, opts=)
    options = 
      :subject => "Welcome to Kontracking",
      :email => user.email,
      :global_merge_vars => [
        
          name: "USER_NAME",
          content: user.user_name
        
      ],
      :template_name => "Welcome Message - Kontracking"
    

    mandrill_send options

  end

end

mailers/base_mandrill_mailer.rb:

require "mandrill"

class BaseMandrillMailer < ApplicationMailer

  def mandrill_send(opts=)
    message = 
      :subject => "#opts[:subject]",
      :from_name => "Kontracking",
      :from_email => "admin@kontracking.com",
      :to =>
        ["name" => "Some User",
          "email" => "#opts[:email]",
          "type" => "to"],
      :global_merge_vars => opts[:global_merge_vars]
    
    sending = MANDRILL.messages.send_template opts[:template_name], [], message
    rescue Mandrill::Error => e
      Rails.logger.debug("#e.class: #e.message")
      raise
  end

end

在 Rails 控制台中运行 UserTransactionMailer.welcome_message(User.first).deliver_now 会成功发送到我的电子邮件,包括正确处理我包含的 user_name 的 merge_var 以显示在电子邮件中。我对哈希没有经验,目前无法找出未定义方法问题的解决方案(这可能相当简单)。我怎样才能让它正常工作?

另外,我目前在初始化程序中有 require 'mandrill',所以我应该能够从 base_mandrill_mailer.rb 文件中删除它,对吗?

【问题讨论】:

【参考方案1】:

我解决了我的问题,因为当前的实现总是让 self 未定义。更改 models/user.rb 确认方法以包含消息的 self 和 user 解决了问题。

models/user.rb:

class User < ActiveRecord::Base
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, 
         :confirmable, :lockable, :timeoutable, :zxcvbnable

  def confirm!
    send_welcome_message(self)
    super
  end

private

  def send_welcome_message(user)
    UserTransactionMailer.welcome_message(user).deliver_now
  end

end

所有其他文件都是正确的。我会向希望覆盖 Devise 邮件程序或特定方法并同时利用 Mailchimp/Mandrill 集成的任何人推荐这种方法——尤其是那些在开发团队中工作的人。此方法有效地允许您将电子邮件的设计和更新分流给非技术成员,并且不需要将电子邮件视图更改部署到服务器(待更改的变量名称或电子邮件添加)。

我在下面包含了用于覆盖 Devise 的文件。

mailers/custom_devise_mailer.rb:

class CustomDeviseMailer < Devise::Mailer


  def confirmation_instructions(record, token, opts=)
    options = 
      :subject => "Confirmation Instructions",
      :email => record.email,
      :global_merge_vars => [
        
          name: "confirmation_link",
          content: user_confirmation_url(confirmation_token: token)
          
      ],
      :template_name => "Confirmation Instructions - Kontracking"
    

    mandrill_send options
  end


  def reset_password_instructions(record, token, opts=)
    options = 
      :subject => "Password Reset Instructions",
      :email => record.email,
      :global_merge_vars => [
         
          name: "password_reset_link",
          content: reset_password_url(reset_password_token: record.reset_password_token)
        
      ],
      :template_name => "Password Reset Instructions - Kontracking"
    

    mandrill_send options
  end


  def unlock_instructions(record, token, opts=)
    options = 
      :subject => "Account Unlock Instructions",
      :email => record.email,
      :global_merge_vars => [
        
          name: "account_unlock_link",
          content: user_unlock_url(unlock_token: token)
        
      ],
      :template_name => "Account Unlock Instructions - Kontracking"
    

    mandrill_send options
  end


  def mandrill_send(opts=)
    message = 
      :subject => "#opts[:subject]",
      :from_name => "Kontracking",
      :from_email => "anthony.emtman@kredibleinc.com",
      :to =>
        ["name" => "Some User",
          "email" => "#opts[:email]",
          "type" => "to"],
      :global_merge_vars => opts[:global_merge_vars]
    
    sending = MANDRILL.messages.send_template opts[:template_name], [], message
    rescue Mandrill::Error => e
      Rails.logger.debug("#e.class: #e.message")
      raise
  end


end

您还需要更改您的设计初始化程序中的邮件配置以指向您的自定义邮件。

initializers/devise.rb:

  # ==> Mailer Configuration
  # Configure the e-mail address which will be shown in Devise::Mailer,
  # note that it will be overwritten if you use your own mailer class
  # with default "from" parameter.
  config.mailer_sender = 'john.smith@example.com'

  # Configure the class responsible to send e-mails.
  config.mailer = 'CustomDeviseMailer'

我还为 Mandrill 设置了一个简单的初始化程序(我正在使用 Mandrill API——文件包含在下面)。

初始化程序/mandrill.rb:

require 'mandrill'

MANDRILL = Mandrill::API.new ENV['SMTP_PASSWORD']

config/environments/production.rb:

  # Do not dump schema after migrations.
  config.active_record.dump_schema_after_migration = false

  config.action_mailer.default_url_options =  host: ENV["SMTP_DOMAIN"] 
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.perform_deliveries = true
  config.action_mailer.smtp_settings = 
    address: ENV.fetch("SMTP_ADDRESS"),
    authentication: :plain
    domain: ENV.fetch("SMTP_DOMAIN"),
    enable_starttls_auto: true,
    password: ENV.fetch("SMTP_PASSWORD"),
    port: "587",
    user_name: ENV.fetch("SMTP_USERNAME")
  

config/environments/development.rb:

  # Care if the mailer can't send.
  config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :test
  host = 'localhost:3000'
  config.action_mailer.default_url_options =  host: host 
  config.action_mailer.perform_deliveries = true

config/application.yml:

SMTP_ADDRESS:smtp.mandrillapp.com SMTP_DOMAIN:本地主机 SMTP_PASSWORD: '在此处插入您的 Mandrill API 密钥——无引号' SMTP_USERNAME: '在此处插入您的 Mandrill 用户名 -- 无引号'

我正在使用 figaro 来管理这些。我设置了一个初始化程序,因此如果未在服务器上设置它们,则会导致错误。

initializers/figaro.rb:

Figaro.require_keys("SMTP_ADDRESS", "SMTP_DOMAIN", 
  "SMTP_PASSWORD", "SMTP_USERNAME")

如果您有任何问题,请告诉我!

【讨论】:

以上是关于(NoMethodError:main:Object 的未定义方法“电子邮件”)。与 Mandrill 和 Mailchimp 确认设计后发送欢迎电子邮件的主要内容,如果未能解决你的问题,请参考以下文章

NoMethodError:AjaxDatatablesRails:Module 的未定义方法“配置”

Ruby - NoMethodError:未定义的哈希方法

NoMethodError:升级到 rake 11 后未定义方法“last_comment”

NoMethodError: nil:NilClass / 'additional_paths' [capistrano+webpacker] 的未定义方法“+”

大礼包安装 NoMethodError

最佳就地宝石的 NoMethodError