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.
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/