You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby on Rails升级后Mailer方法触发ArgumentError问题咨询

问题背景

我们近期将代码库从Ruby 2.7.x + Rails 5.2.x迁移至Ruby 3.2.2 + Rails 6.1.7.3。此前邮件发送器定义如下:

class ApplicationMailer < ActionMailer::Base
...
end
class MyMailer < ApplicationMailer  
  def custom(user: , timestamp: Time.now)
    @user = user
    @timestamp = timestamp
  end
end

调用方式为MyMailer.custom(user: @myuser).deliver_now,升级后触发错误:ArgumentError: wrong number of arguments (given 1, expected 0; required keyword: user)。

我们发现官方推荐通过params哈希传递参数,但该方式不够简洁,官方示例如下:

class MyMailer < ApplicationMailer  
  def custom
    @user = params[:user]
    @timestamp = params[:timestamp] || Time.now
  end
end

现咨询两个问题:

  1. 是Ruby或Rails的哪项变更导致原有方案失效?
  2. 是否仍可使用自定义关键字参数定义Mailer方法?
问题解答

1. 导致失效的变更点

是两方面变化共同作用的结果:

  • Rails Action Mailer调用机制变更:Rails 6.0+重构了Action Mailer的类方法调用逻辑,旧版本中调用MyMailer.custom(user: @myuser)时,Rails会直接将关键字参数传递给对应的实例方法;但升级后,类方法会把所有传入参数打包到params哈希中,不再直接转发给实例方法,实例方法默认被调用时不接收任何参数,这就导致你定义的带关键字参数的实例方法收到0个参数,触发参数不匹配错误。
  • Ruby 3.0+关键字参数严格化:Ruby 3.0开始彻底区分位置参数和关键字参数,不再允许哈希自动转换为关键字参数。Ruby 2.7的兼容模式还能勉强运行,但Ruby 3.2.2的严格模式直接抛出参数错误,放大了这个问题。

2. 仍可使用自定义关键字参数的方案

可以保留自定义关键字参数的定义,只需调整调用方式或稍作修改Mailer实现,提供两种可行方案:

方案一:直接调用实例方法

绕过Rails的类方法代理,直接实例化Mailer后调用实例方法:

MyMailer.new.custom(user: @myuser).deliver_now

这种方式完全沿用原有实例方法定义,不需要修改Mailer代码,仅调整调用处即可。

方案二:自定义类方法转发参数

在Mailer中定义对应的类方法,手动将关键字参数转发给实例方法:

class MyMailer < ApplicationMailer  
  def custom(user: , timestamp: Time.now)
    @user = user
    @timestamp = timestamp
  end

  def self.custom(user:, timestamp: Time.now)
    new.custom(user: user, timestamp: timestamp)
  end
end

这样原来的调用方式MyMailer.custom(user: @myuser).deliver_now可以保持不变,无需修改调用代码。

折中方案:结合params与关键字参数

如果想兼顾Rails 6+的规范和关键字参数的可读性,可以在实例方法中从params解构出关键字参数:

class MyMailer < ApplicationMailer  
  def custom
    user = params.require(:user)
    timestamp = params[:timestamp] || Time.now
    setup_email(user: user, timestamp: timestamp)
  end

  private
  def setup_email(user:, timestamp:)
    @user = user
    @timestamp = timestamp
  end
end

调用时使用Rails推荐的with方法:MyMailer.with(user: @myuser).custom.deliver_now,既符合新规范,又能在内部方法中保留关键字参数的清晰性。

内容的提问来源于stack exchange,提问作者Irina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 12:52:36