rubysafe-navigation-operator

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.


Solution

  • 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).