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