How do I troubleshoot "wrong number of arguments" in solidus affirm

712 views Asked by At

Struggling to get solidus_affirm working and hoping anyone might have some ideas. I always have trouble with "wrong number of arguments" errors like this.

Can get through checkout all the way to the confirm step, where it throws a 500.

ArgumentError in Spree::CheckoutController#update
wrong number of arguments (given 2, expected 1)
Extracted source (around line #28):

    def post(path, **data)
      connection.post(normalized_path(path), data)
    end

affirm-ruby (1.1.2) lib/affirm/client.rb:28:in 'post'
affirm-ruby (1.1.2) lib/affirm/client.rb:10:in 'public_send'
affirm-ruby (1.1.2) lib/affirm/client.rb:10:in 'request'
affirm-ruby (1.1.2) lib/affirm/charge.rb:7:in 'authorize'
solidus_affirm (4f8d9ee63345) lib/solidus_affirm/affirm_client.rb:18:in 'authorize'
solidus_core (3.1.5) app/models/spree/payment_method.rb:40:in 'authorize'

if I put a binding.pry in lib/affirm/charge.rb:7:in 'authorize' of the affirm gem, and try the respond Client.request method there, I get a faraday deprecation warning, not sure if that's the cause or unrelated. Seems like it's injecting an auth header or something?

pry(Affirm::Charge)> respond Client.request(:post, "charges", checkout_token: token)
WARNING: 'Faraday::Connection#basic_auth' is deprecated; it will be removed in version 2.0.
While initializing your connection, use '#request(:basic_auth, ...)' instead.
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.

In that same binding.pry, I tried a few things to see what came back:

[2] pry(Affirm::Charge)> respond Client.request(:post)
ArgumentError: wrong number of arguments (given 1, expected 2)

pry(Affirm::Charge)> respond Client.request(:post, "charges")
WARNING: 'Faraday::Connection#basic_auth' is deprecated; it will be removed in version 2.0.
While initializing your connection, use '#request(:basic_auth, ...)' instead.
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
ArgumentError: wrong number of arguments (given 2, expected 1)

Any help troubleshooting the error would be much appreciated.

2

There are 2 answers

0
embold-tyler On

Apparently the error was caused by a change in the behavior of the double splat operator in Ruby 3 and needs to be fixed in the affirm-ruby gem. I submitted a PR and overrode the methods in an initializer.

In Ruby 3.0, positional arguments and keyword arguments will be separated. Ruby 2.7 will warn for behaviors that will change in Ruby 3.0. If you see the following warnings, you need to update your code:

Using the last argument as keyword parameters is deprecated, or Passing the keyword argument as the last hash parameter is deprecated, or Splitting the last argument into positional and keyword parameters is deprecated In most cases, you can avoid the incompatibility by adding the double splat operator. It explicitly specifies passing keyword arguments instead of a Hash object. Likewise, you may add braces {} to explicitly pass a Hash object, instead of keyword arguments.

Source: https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/

The splat operator was breaking things: request(method, path, **data)

I created an initializer at config/initializers/affirm-ruby.rb to override the gem's methods in our Solidus app.

module Affirm
  class Client
    class << self
      def request(method, path, data={})
        new.public_send(method, path, data)
      end
    end

    def get(path, data={})
      connection.get(normalized_path(path), data)
    end

    def post(path, data={})
      connection.post(normalized_path(path), data)
    end
  end
end
0
Michael Bugglin On

The problem is that the call to request() is ambiguous.

respond Client.request(:post, "charges", checkout_token: token)

Looking at the source for the request() method, it takes two specific positional arguments, followed by any number of positional arguments. That's the problem: it only takes positional arguments. Your call includes a keyword argument of checkout_token, but the request() method doesn't know what that is, because there's no checkout_token keyword parameter on the method definition.

If you need to give a method a hash, you should give it exactly that, just like the article you linked suggests:

respond Client.request(:post, "charges", { checkout_token: token })