Rails ternary operator with exception gotcha

Ternary operators are a shorthand for if/else statements. In Ruby, the syntax is:

condition ? first branch : second branch

You can throw exceptions in Ruby in either branch, but there is a slight gotcha.

An example if/else would look like this:

# ternary_exception.rb

class ArmsTooShortError < Exception
end

class TyrannosaurusRex 
    def initialize(arm_length)
        @arm_length = arm_length
    end

    def can_reach(distance)
        if @arm_length > distance 
            true 
        else
            raise ArmsTooShortError.new("rrrawwwr I can't reach it") 
        end 
    end
end 

trex = TyrannosaurusRex.new(3)
puts "Expect true: #{trex.can_reach(2)}"
puts "Expect exception: #{trex.can_reach(4)}"

At first try, updating the if/else statement might look like this:

    def can_reach_ternary_error(distance)
        @arm_length > distance ? true : raise ArmsTooShortError.new("rrrawwwr I can't reach it") 
    end

But instead of the expected error, we get:

syntax error, unexpected constant, expecting `do' or '{' or '('

This error is slightly misleading, because I’d expect the error itself to be the constant. The fix here is to wrap the entire raise statements in parentheses:

    def can_reach_ternary_success_parens(distance)
        @arm_length > distance ? true : (raise ArmsTooShortError.new("rrrawwwr I can't reach it"))
    end

Doing the same thing with do or {} throws different errors, so in this case, use parentheses.

Feel free to try it out here: https://replit.com/@eweil505/trex-ternary-operator?v=1

Or run it yourself:

class ArmsTooShortError < Exception
end

class TyrannosaurusRex 
    def initialize(arm_length)
        @arm_length = arm_length
    end

    def can_reach(distance)
        if @arm_length > distance 
            true 
        else
            raise ArmsTooShortError.new("rrrawwwr I can't reach it") 
        end 
    end

    # def can_reach_ternary_error(distance)
    #     @arm_length > distance ? true : raise ArmsTooShortError.new("rrrawwwr I can't reach it") 
    # end

    def can_reach_ternary_success_parens(distance)
        @arm_length > distance ? true : (raise ArmsTooShortError.new("rrrawwwr I can't reach it"))
    end

    # def can_reach_ternary_error_braces(distance)
    #     @arm_length > distance ? true : {raise ArmsTooShortError.new("rrrawwwr I can't reach it")}
    # end

    # def can_reach_ternary_error_do_end(distance)
    #     @arm_length > distance ? true : do raise ArmsTooShortError.new("rrrawwwr I can't reach it") end
    # end
end 

trex = TyrannosaurusRex.new(3)
puts "Expect true: #{trex.can_reach(2)}"

## Uncomment to See Syntax Error Output:
# puts "Expect exception: #{trex.can_reach(4)}"
#puts "Syntax Error: #{trex.can_reach_ternary_error(4)}"

## Yields:
# syntax error, unexpected constant, expecting `do' or '{' or '('


puts "Exception Success: #{trex.can_reach_ternary_success_parens(4)}"

## Uncomment to See Braces Error Output: 
# puts "Exception Error Braces: #{trex.can_reach_ternary_error_braces(4)}"

## Yields: 
# main.rb:26: syntax error, unexpected '}', expecting `end'

## Uncomment to See Do-End Error Output: 
# puts "Exception Error Do-End: #{trex.can_reach_ternary_error_do_end(4)}"

## Yields: 
# main.rb:30: syntax error, unexpected `do'
# ...m_length > distance ? true : do raise ArmsTooShortError.new(...
# main.rb:32: syntax error, unexpected `end', expecting end-of-input