Getting wrong number of arguments calling a function with keyword arguments With Ruby 3.2
Asked Answered
L

1

6

I updated the Ruby version from 2.7.3 to 3.2.0 in my project and some tests started to fail. Among those is one that is calling a function with keyword arguments. This is the error it's throwing:

1) NotificationsMailer notify Sets body
 Failure/Error:
   def notify(recipient_emails:, subject:, body:)
     @body = body
     @subject = subject
     mail(bcc: recipient_emails, subject: @subject, reply_to: VENDOR_SUPPORT_EMAIL)
   end

 ArgumentError:
   wrong number of arguments (given 1, expected 0; required keywords: recipient_emails, subject, body)
 # ./app/mailers/notifications_mailer.rb:5:in `notify'
 # /usr/local/bundle/gems/actiontext-6.1.6/lib/action_text/rendering.rb:20:in `with_renderer'
 # /usr/local/bundle/gems/actiontext-6.1.6/lib/action_text/engine.rb:59:in `block (4 levels) in <class:Engine>'
 # ./spec/mailers/notifications_mailer_spec.rb:16:in `block (3 levels) in <top (required)>'

This is the definition of the function being called:

 class NotificationsMailer < ApplicationMailer

  VENDOR_SUPPORT_EMAIL = "[email protected]"

  def notify(recipient_emails:, subject:, body:)
    @body = body
    @subject = subject
    mail(bcc: recipient_emails, subject: @subject, reply_to: VENDOR_SUPPORT_EMAIL)
  end

end

And this is the test file:

require "rails_helper"

RSpec.describe NotificationsMailer, type: :mailer do
  describe 'notify' do
    let(:recipient_emails) { %w[[email protected] [email protected]] }

    let(:mail) do
      NotificationsMailer.notify(
        recipient_emails: recipient_emails,
        subject: 'subject',
        body: 'body'
      )
    end

    it 'Sets body' do
      expect(mail.body.encoded).to match 'body'
    end

    it 'Sets subject' do
      expect(mail.subject).to eq 'subject'
    end

    it "Does not set To" do
      expect(mail.to).to be_nil
    end

    it "Does not set CC" do
      expect(mail.cc).to be_nil
    end

    it "Sets BCC with recipients' emails" do
      expect(mail.bcc).to eq ['[email protected]', '[email protected]']
    end

    it 'Sets reply_to with vendor support email' do
      expect(mail.reply_to).to eq ['[email protected]']
    end
  end
end

I know ruby 3 introduced some changes to keyword arguments, but as I'm calling the function with the arguments in the same order and specifying all the keywords, I don't see where my problem is.

Following some threads I tried sending the arguments in a hash and some other things that weren't so promising, but still getting the error, no clue of what's happening there.

Leopoldeen answered 11/2, 2023 at 23:10 Comment(6)
This is interesting. Can you compose a simple reproducible code?Hebron
Please do not post images of plaintext as they are not appropriate on StackOverflow. Please copy and paste plaintext into your question. Please include a minimal reproducible example in your post. It should include your exact inputs, exact outputs, and detailed steps you took so that the problem can be reproduced by others. As stated, your error cannot be reproduced: gist.github.com/anothermh/bb549cc62e9d37c229b83c00ff674cbfDorman
Can you post the entire stack trace? Is there no line number associated with the error?Roasting
@Roasting .. I updated the error with all the detailsLeopoldeen
"I'm calling the function with the arguments […]" – not quite. notify is an instance method but you call it as a class method. This is where Rails comes into play – the (hidden) class method handles the instantiation and passing of arguments. The latter fails for keyword arguments.Miscue
@Miscue has the most useful answer here as it explains the actual problem rather than jumping to a solution. Instantiating the class explicitly by calling NotificationsMailer.new.notify(...) will also solve the problem and allow use of keyword arguments.Debunk
M
6

I was able to reproduce your issue with Rails 6.1.7.2 and Ruby 3.2.1. The problem is that Rails 6.1 and Ruby 3.2 aren't fully compatible. There are efforts to backport 3.2 compatibility fixes to Rails 6.1 (see https://github.com/rails/rails/pull/46895) but they haven't been released yet it seems.

You could upgrade to a lower version of Ruby instead (3.1.2 worked fine for me, I'm sure 3.1.3 would too), or you could change your mailer code to be like this:

  def notify(recipient_emails, subject, body)
    [omitted]
  end

(and obviously also change how you call that function).

There's some risk, though, that you'll run into more kwargs related issues with Rails 6.1 and Ruby 3.2, so maybe going with Ruby 3.1 is the better option.

Monolingual answered 12/2, 2023 at 7:58 Comment(2)
Is upgrading rails an option as well?Mercorr
@Mercorr Yes, Rails 7.0.3 or later should have that fixed.Monolingual

© 2022 - 2024 — McMap. All rights reserved.