ruby-on-railsrubyvalidationruby-3

Ruby 3.0 - wrong number of arguments (given 3, expected 1..2)


We have a project that is using the uk_postcode gem. There is a validator class as follows:

class UkPostcodeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    postcode = UKPostcode.parse(value.to_s)
    return if postcode.full_valid?

    record.errors.add attribute, :invalid_uk_postcode, options
  end
end 

The above was working fine with Ruby 2.7.6 but now I need to update to Ruby 3.0.0. When I do this, our tests are breaking and giving the following error:

Failure/Error: record.errors.add attribute, :invalid_uk_postcode, options
     
     ArgumentError:
       wrong number of arguments (given 3, expected 1..2) 

My Ruby and Rails knowledge is not great yet, but after searching so much online and trying different things, I found that changing record.errors.add attribute, :invalid_uk_postcode, options to record.errors.add attribute, :invalid_uk_postcode, **options fixes the tests. So adding ** to last argument fixes the tests and it seems like the validation then works as normal. From what I have read so far, it seems like the arguments have to be more specific and by adding ** makes it a keyword argument (which I'm assuming then can take on any type/values) but as I'm not an expert in Ruby and Rails yet, it's more of a guess rather than understanding this properly.

Can someone guide better? Does the above change seem fine to fix in this way? Why does adding ** to the last argument fix the error issues?

I am not sure atm, what options is referring to and something I will be looking into, in the future but anyone who knows and can answer, would be appreciated. Thanks

Searched online for the error and can see there are changes to Ruby syntax. Trying to understand this.


Solution

  • Keyword arguments are now fully separated from positional arguments

    def new_style(name, **options)
    end
    
    new_style('John', {age: 10})
    # Ruby 2.6: works
    # Ruby 2.7: warns: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call
    # Ruby 3.0: ArgumentError (wrong number of arguments (given 2, expected 1))
    new_style('John', age: 10)
    # => works
    h = {age: 10}
    new_style('John', **h)
    # => works, ** is mandatory
    

    And the definition of add method is:

    def add(attribute, type = :invalid, **options)
    

    So passing hash in options without ** is not supported now. Instead, you can pass them directly as keyword arguments like this, that is what ** does:

    record.errors.add attribute, :invalid_uk_postcode, count: 25, other_attr: 'any text'
    

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