ruby-on-railsrubyruby-3

ArgumentError (wrong number of arguments (given 2, expected 1)) after updating to Ruby 3.0


When I try to update our site to Ruby 3.0.0 I get this error:

ArgumentError (wrong number of arguments (given 2, expected 1))

 % rails console
Loading development environment (Rails 6.1.0)
irb(main):001:0> puts RUBY_VERSION
3.0.0
irb(main):002:0> puts IceCube::VERSION
0.16.3
irb(main):003:0> schedule = IceCube::Schedule.new
=> #<IceCube::Schedule:0x00007fccfe19cfa8 @start_time=2020-12-27 11:14:30 -0800, @all_recurrence_rules=[], @all_exception_rules=[]>
irb(main):004:0> puts schedule.to_ical
Traceback (most recent call last):
        1: from (irb):4:in `<main>'
ArgumentError (wrong number of arguments (given 2, expected 1))

Here are the same commands with Ruby 2.7.2 which work

% rails console
Loading development environment (Rails 6.1.0)
irb(main):001:0> puts RUBY_VERSION
2.7.2
irb(main):002:0> puts IceCube::VERSION
0.16.3
irb(main):003:0> schedule = IceCube::Schedule.new
=> #<IceCube::Schedule:0x00007f9da3128fe8 @start_time=2020-12-27 11:12:50 -0800, @all_recurrence_rules=[], @all_exception_rules=[]>
irb(main):004:0> puts schedule.to_ical
DTSTART;TZID=PST:20201227T111250

What's confusing is that it also works with a plain ruby script with the same gem versions as the first example

% irb
irb(main):001:0> puts RUBY_VERSION
3.0.0
irb(main):002:0> gem 'ice_cube'
irb(main):003:0> require 'ice_cube'
irb(main):004:0> puts IceCube::VERSION
0.16.3
irb(main):005:0> schedule = IceCube::Schedule.new
=> #<IceCube::Schedule:0x00007ff2fb0d5b88 @start_time=2020-12-27 11:11:02 -0800, @all_recurrence_rules=[], @all_exception_rules=[]>
irb(main):006:0> puts schedule.to_ical
DTSTART;TZID=PST:20201227T111102

Does anyone know where I can start to fix this? We're just starting the project and I am hoping to use Ruby 3


Solution

  • TL;DR: ice_cube 0.16.4 has already sorted out these compatibility problems with ruby 3.x, so just upgrade.


    The issue has to do with how keyword arguments are handled in ruby 3.0 and how ice_cube is passing its arguments to I18n.localize.
    So, extracting and simplifying the buggy code, this worked on ruby < 3

    RUBY_VERSION # "2.7.2"
    
    def foo(*args)
      bar(*args)
    end
    
    def bar(object, locale: nil, format: nil, **options)
      puts "object:#{object}"
      puts "locale:#{locale}"
      puts "format:#{format}"
      puts "options:#{options}"
    end
    
    foo('date', format: 'whatever')
    # object:date
    # locale:
    # format:whatever
    # options:{}
    

    And from ruby >= 3

    RUBY_VERSION # "3.0.0"
    
    def foo(*args)
      bar(*args)
    end
    
    def bar(object, locale: nil, format: nil, **options)
      puts "object:#{object}"
      puts "locale:#{locale}"
      puts "format:#{format}"
      puts "options:#{options}"
    end
    
    foo('date', format: 'whatever')
    # Traceback (most recent call last):
    #        16: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/3.0.0/bundler/cli.rb:24:in `start'
    #        15: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor/base.rb:485:in `start'
    #        14: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/3.0.0/bundler/cli.rb:30:in `dispatch'
    #        13: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor.rb:392:in `dispatch'
    #        12: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor/invocation.rb:127:in `invoke_command'
    #        11: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/3.0.0/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
    #        10: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/3.0.0/bundler/cli.rb:497:in `exec'
    #         9: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/3.0.0/bundler/cli/exec.rb:28:in `run'
    #         8: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/3.0.0/bundler/cli/exec.rb:63:in `kernel_load'
    #         7: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/3.0.0/bundler/cli/exec.rb:63:in `load'
    #         6: from /Users/alter/.rbenv/versions/3.0.0/bin/irb:23:in `<top (required)>'
    #         5: from /Users/alter/.rbenv/versions/3.0.0/bin/irb:23:in `load'
    #         4: from /Users/alter/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>'
    #         3: from (irb):11:in `<main>'
    #         2: from (irb):3:in `foo'
    #         1: from (irb):5:in `bar'
    # ArgumentError (wrong number of arguments (given 2, expected 1))
    

    So, the fix I can see is to explicitely passing the parameters to the method:

    def foo2(object, **options)
      bar(object, **options)
    end
    
    foo2('date', format: 'whatever')
    # object:date
    # locale:
    # format:whatever
    # options:{}
    

    Or in terms of the involved module:

    module IceCube
      module I18n
        def self.l(object, **options)
          backend.l(object, **options)
        end
      end
    end
    

    and send a PR, use your own fork, monkey-patch, whatever works best for you.
    But most important, brace yourself to find this issue in a lot of other gems, as the first stable ruby 3 version was just released 2 days ago, so probably you'll have these type of issues with other gems, specially the unmaintained ones. Good luck.