I am doing an upgrade from Ruby 3.0.6 to Ruby 3.2.2 on our project, and I ahve encountered a weird issue that I tried to understand for past few days. We are using RSpec for tests and in one of the tests we have this code:
let(:request) { FactoryBot.build(:custom_request) }
context "when not requesting their data" do
let(:mail) { CustomtMailer.request_email(request: request) }
it "renders the headers" do
#expect something
end
end
and CustomMailer class looks like this:
class CustomMailer < ActionMailer::Base
layout "application_mailer"
def request_email(request:, csv: nil)
#some data processing
recipients = request.email
sender = request.sender
subject = "Some subject"
mail(to: recipients, subject: subject, from: sender)
end
Well, the first problem I have with existing code is that while the request_email is an instance method, it is being called as class method in whole project, and somehow it works??? Second problem is the tests used to pass with Ruby 2.7.8 and Ruby 3.0.6. But after Ruby 3.1 tests are failing, and I get this error:
ArgumentError: wrong number of arguments (given 1, expected 0; required keyword: request)
My first idea was to make the method a class method, since it doesn't use any instance variables, but then I get the problem with the mail function
NoMethodError: undefined method 'mail' for CustomMailer:Class
But the most troubling thing is that the call to the request_email
method doesn't fail, and when I tried debugging and inspected how the params were passed, I saw that all arguments were passed as positional arguments, and are a part of args object, and not kwargs object.
Any ideas why it was working before, and how to fix this?
A lot of time passed, but someone reminded me of this question, and I'm posting a solution now. The way I solved the issue was I moved from
CustomtMailer.request_email(request: request)
to
CustomtMailer.with(request: request).request_email
and in the mailer class, instead of calling keyword argument
def request_email(request:)
# ...
req = request
# ...
end
I gett request value like this
def request_email
# ...
req = params.fetch(:request)
# ...