Irina
Irina

Reputation: 75

Ruby on Rails update triggered ArgumentError in Mailer functions

We have recently migrated our codebase from Ruby 2.7.x and Rails 5.2.x to Ruby 3.2.2 and Rails 6.1.7.3. Previously, our mailers were defined like this:

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

And we would deliver emails like this: MyMailer.custom(user: @myuser).deliver_now. With the update, this now returns an ArgumentError: wrong number of arguments (given 1, expected 0; required keyword: user) (ArgumentError).

We have found that we are supposed to pass the arguments in a params hash and read from there, but that seems pretty cumbersome and unelegant compared to our previous solution. So my questions are,

This is the way that we are apparently supposed to pass parameters:

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

Upvotes: 5

Views: 668

Answers (2)

fiedl
fiedl

Reputation: 6147

I still see this issue with actionmailer 7.2.0.beta1, mail 2.8.1, and ruby 3.3.2. I'm not sure why because it seems to have been resolved in rails in the meantime.

This is how I could resolve it, however:

Patch send_action in the ApplicationMailer

class ApplicationMailer < ActionMailer::Base
  # ...

  # This patch accounts for the error
  #
  #   ArgumentError:
  #        wrong number of arguments (given 1, expected 0; required keywords:
  #
  # which has appeared with the switch to ruby 3.2.
  #
  # References:
  # - https://github.com/rails/rails/issues/46883
  # - https://github.com/rails/rails/issues/44846
  # - https://stackoverflow.com/q/76262079/2066546
  #
  def send_action(*args)
    if args.second.kind_of? Hash
      super(args.first, **args.second)
    else
      super
    end
  end
end

How this works

  • args.first is the name of the method in the mailer that will receive the arguments and raises the error because of the signature mismatch, e.g. custom in the question.
  • super will essentially call send(args.first, ...), i.e. call the custom method.
  • By passing **args.second we convert the argument hash, which does not match the method signature, to a series of keyword arguments, which do match the signature.

References

Upvotes: 0

Holger Just
Holger Just

Reputation: 55888

ActionMailer still accepts passing arguments on the mailer actions. Howevber, it appears that the keyword argument semantics in Ruby 3.2 are not fully expected by Rails 6.1 which causes the issue you are seeing.

Rails 6.1 was released before Ruby 3.2 was a thing. As recent Ruby releases have changed some semantics with keywords arguments, it's appears likely that Rails 6.1 is not (fully) compatible with Ruby 3.2.

While I have not found any authoritative documentation about this, for the latest commit on the Rails 6.1.7.3 tag, the actionmailer tests fail a test regarding keyword arguments with Ruby 3.2.

As such, you have two possible avenues to fix this issue:

  • You revert to Ruby 3.1 with your Ruby 6.1 app
  • Or you could further upgrade your Rails version to > 7.0.1

As a quick fix, you could probably now first downgrade your Ruby version, then start upgading your Rails application further until you can bump your Ruby version again.

Upvotes: 2

Related Questions