Rails class 给出 NoMethodError 未定义的方法`each'

Rails class gives NoMethodError undefined method `each'

我正在使用 redmine,我已经安装了一个用于管理邮件的插件。

当我尝试发送邮件时出现以下错误

[ActiveJob] [ActionMailer::DeliveryJob] [uuid] Error performing ActionMailer::DeliveryJob (Job ID: uuid) from Async(mailers) in 41.81ms: NoMethodError (undefined method `each' for #<User:id>):

这是给我错误的文件

module EncryptMails

  def self.included(base) # :nodoc:
    base.send(:include, InstanceMethods)
    base.class_eval do
      alias_method :mail_without_relocation, :mail
      alias_method :mail, :mail_with_relocation
    end
  end

  module InstanceMethods

    # action names to be processed by this plugin
    def actions
      [
        'attachments_added',
        'document_added',
        'issue_add',
        'issue_edit',
        'message_posted',
        'news_added',
        'news_comment_added',
        'wiki_content_added',
        'wiki_content_updated'
      ]
    end

    # dispatched mail method
    def mail_with_relocation(headers={}, &block)

      # pass unchanged, if action does not match or plugin is inactive
      act = Setting.plugin_openpgp['activation']
      return mail_without_relocation(headers, &block) if
        act == 'none' or not actions.include? @_action_name or
        (act == 'project' and not project.try('module_enabled?', 'openpgp'))

      # relocate recipients
      recipients = relocate_recipients(headers)
      header = @_message.header.to_s

      # render and deliver encrypted mail
      reset(header)
      m = mail_without_relocation prepare_headers(
        headers, recipients[:encrypted], encrypt = true, sign = true
      ) do |format|
        format.text
      end
      m.deliver

      # render and deliver filtered mail
      reset(header)
      tpl = @_action_name + '.filtered'
      m = mail_without_relocation prepare_headers(
        headers, recipients[:filtered], encrypt = false, sign = true
      ) do |format|
        format.text { render tpl }
        format.html { render tpl } unless Setting.plain_text_mail?
      end
      m.deliver

      # render unchanged mail (deliverd by calling method)
      reset(header)
      m = mail_without_relocation prepare_headers(
        headers, recipients[:unchanged], encrypt = false, sign = false
      ) do |format|
        format.text
        format.html unless Setting.plain_text_mail?
      end

      m
      end

    # get project dependent on action and object
    def project

      case @_action_name
        when 'attachments_added'
          @attachments.first.project
        when 'document_added'
          @document.project
        when 'issue_add', 'issue_edit'
          @issue.project
        when 'message_posted'
          @message.project
        when 'news_added', 'news_comment_added'
          @news.project
        when 'wiki_content_added', 'wiki_content_updated'
          @wiki_content.project
        else
          nil
      end

    end

    # relocates reciepients (to, cc) of message
    def relocate_recipients(headers)

      # hash to be returned
      recipients = {
        :encrypted => {:to => [], :cc => []},
        :blocked   => {:to => [], :cc => []},
        :filtered  => {:to => [], :cc => []},
        :unchanged => {:to => [], :cc => []},
        :lost      => {:to => [], :cc => []}
      }

      # relocation of reciepients
      [:to, :cc].each do |field|
        headers[field].each do |user|

          # encrypted
          unless Pgpkey.find_by(user_id: user.id).nil?
            recipients[:encrypted][field].push user and next
          end

          # unencrypted
          case Setting.plugin_openpgp['unencrypted_mails']
            when 'blocked'
              recipients[:blocked][field].push user
            when 'filtered'
              recipients[:filtered][field].push user
            when 'unchanged'
              recipients[:unchanged][field].push user
            else
              recipients[:lost][field].push user
          end

        end unless headers[field].blank?
      end

      recipients

    end

    # resets the mail for sending mails multiple times
    def reset(header)

      @_mail_was_called = false
      @_message = Mail.new
      @_message.header header

    end

    # prepares the headers for different configurations
    def prepare_headers(headers, recipients, encrypt, sign)

      h = headers.deep_dup

      # headers for recipients
      h[:to] = recipients[:to]
      h[:cc] = recipients[:cc]

      # headers for gpg
      h[:gpg] = {
        encrypt: false,
        sign: false
      }

      # headers for encryption
      if encrypt
        h[:gpg][:encrypt] = true
        # add pgp keys for emails
        h[:gpg][:keys] = {}
        [:to, :cc].each do |field|
          h[field].each do |user|
            user_key = Pgpkey.find_by user_id: user.id
            unless user_key.nil?
              h[:gpg][:keys][user.mail] = user_key.fpr
            end
          end unless h[field].blank?
        end
      end

      # headers for signature
      if sign
        server_key = Pgpkey.find_by(:user_id => 0)
        unless server_key.nil?
          h[:gpg][:sign] = true
          h[:gpg][:sign_as] = Setting['mail_from']
          h[:gpg][:password] = server_key.secret
        end
      end

      h

    end

  end
end

日志的堆栈告诉我错误在第 109 行

# relocation of reciepients
[:to, :cc].each do |field|

我不是 ruby 和 rails 的专家,但我看到 each 是 Ruby 数组的一种方法,而不是自定义方法,所以我不明白为什么会出现错误。

我哪里做错了,我该如何解决这个错误?

你确定问题不在这条线上吗?

h[field].each do |user|

field 会有 :to:cc 所以 h[field] 可能是一个 User 实例。

如果要让h[:to]h[:cc]成为单个用户或用户数组,则将其包装在Array():

# relocation of reciepients
[:to, :cc].each do |field|
  Array(headers[field]).each do |user|
  #^^^^

我也会移动尾随的 unless 以免遗漏它,可能是这样的:

%i[to cc].select { |field| headers[field].present? }.each do |field|
  Array(headers[filed]).each do |user|
    #...
  end
end