Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: Safe-navigation operator, undefined method `call`

I am trying to compare a number literal with the return value of a function that could return nil or a numeric. Consider this:

def unreliable
  [nil, 42].sample  
end

unreliable > 10

This will blow up 50% of the time with NoMethodError: undefined method '>' for nil:NilClass. So I tried this:

unreliable&.(:>, 10)

That approach does the nil guarding I expect, but I get this when unreliable returns 42:

NoMethodError: undefined method `call' for 42:Fixnum

I suspect this has to do with the quirks of allowing only one instance to exist for each Numeric, see here. And I know I can do this:

foo = unreliable
foo && foo > 10

But is there a way to use the safe navigation operator with a numeric and :>, :<, :==, :+, :-, :/, :*, etc?


Edit: The focus on Numeric in my question is a red herring. See @Jörg's answer. I was confusing Rails's try syntax with the safe-navigation operator's syntax.

like image 783
Ethan Kent Avatar asked May 16 '26 19:05

Ethan Kent


1 Answers

This works fine in Ruby 2.3+ :

unreliable&.> 10

For example :

[-5, 0, nil, 5].each do |unreliable|
  p unreliable&.> 0
end
# false
# false
# nil
# true

The way you tried it, Ruby expects unreliable to be a callable object such as a Proc :

unreliable = Proc.new{ |*params| puts "unreliable has been called with #{params}" }
unreliable&.(:>, 10)
# unreliable has been called with [:>, 10]
unreliable.call(:>, 10)
# unreliable has been called with [:>, 10]
unreliable&.call(:>, 10)
# unreliable has been called with [:>, 10]
unreliable[:>, 10]
# unreliable has been called with [:>, 10]

With the safe-navigation operator, there's no need to put parens and the method should be a method name, not a symbol (Rails' try expects a symbol).

like image 57
Eric Duminil Avatar answered May 19 '26 14:05

Eric Duminil