This blog is part of our Ruby 2.5 series.

Before Ruby 2.5, if we want to log a caught exception, we would need to format it ourselves.

class AverageService attr_reader :numbers , :coerced_numbers def initialize ( numbers ) @numbers = numbers @coerced_numbers = coerce_numbers end def average sum / count end private def coerce_numbers numbers . map do | number | begin Float ( number ) rescue Exception => exception puts " #{ exception . message } ( #{ exception . class } )

\t #{ exception . backtrace . join ( "

\t " ) } " puts "Coercing ' #{ number } ' as 0.0



" 0.0 end end end def sum coerced_numbers . map ( & :to_f ). sum end def count coerced_numbers . size . to_f end end average = AverageService . new ( ARGV ). average puts "Average is: #{ average } "

$ RBENV_VERSION = 2.4 . 0 ruby average_service . rb 5 4 f 7 1 s0 invalid value for Float (): "4f" ( ArgumentError ) average_service . rb : 18 :in `Float' average_service.rb:18:in ` block in coerce_numbers ' average_service.rb:16:in `map' average_service . rb : 16 :in `coerce_numbers' average_service.rb:6:in ` initialize ' average_service.rb:37:in `new' average_service . rb : 37 :in `<main>' Coercing '4f' as 0.0 invalid value for Float(): "1s0" (ArgumentError) average_service.rb:18:in ` Float ' average_service.rb:18:in `block in coerce_numbers' average_service . rb : 16 :in `map' average_service.rb:16:in ` coerce_numbers ' average_service.rb:6:in `initialize' average_service . rb : 37 :in `new' average_service.rb:37:in ` < main > ' Coercing ' 1 s0 ' as 0.0 Average of [ 5.0 , 0.0 , 7.0 , 0.0 ] is: 3.0

It was proposed that there should be a simple method to print the caught exception using the same format that ruby uses while printing an uncaught exception.

Some of the proposed method names were display , formatted , to_formatted_s , long_message , and full_message .

Matz approved the Exception#full_message method name.

In Ruby 2.5, we can re-write above example as follows.

class AverageService attr_reader :numbers , :coerced_numbers def initialize ( numbers ) @numbers = numbers @coerced_numbers = coerce_numbers end def average sum / count end private def coerce_numbers numbers . map do | number | begin Float ( number ) rescue Exception => exception puts exception . full_message puts "Coercing ' #{ number } ' as 0.0



" 0.0 end end end def sum coerced_numbers . map ( & :to_f ). sum end def count coerced_numbers . size . to_f end end average = AverageService . new ( ARGV ). average puts "Average is: #{ average } "

$ RBENV_VERSION = 2.5 . 0 ruby average_service . rb 5 4 f 7 1 s0 Traceback ( most recent call last ): 6 : from average_service . rb : 37 :in `<main>' 5: from average_service.rb:37:in ` new ' 4: from average_service.rb:6:in `initialize' 3 : from average_service . rb : 16 :in `coerce_numbers' 2: from average_service.rb:16:in ` map ' 1: from average_service.rb:18:in `block in coerce_numbers' average_service . rb : 18 :in `Float': invalid value for Float(): "4f" (ArgumentError) Coercing '4f' as 0.0 Traceback (most recent call last): 6: from average_service.rb:37:in ` < main > ' 5: from average_service.rb:37:in `new' 4 : from average_service . rb : 6 :in `initialize' 3: from average_service.rb:16:in ` coerce_numbers ' 2: from average_service.rb:16:in `map' 1 : from average_service . rb : 18 :in `block in coerce_numbers' average_service.rb:18:in ` Float ': invalid value for Float(): "1s0" (ArgumentError) Coercing ' 1 s0 ' as 0.0 Average of [ 5.0 , 0.0 , 7.0 , 0.0 ] is: 3.0

Note that, Ruby 2.5 prints exception backtrace in reverse order if STDERR is unchanged and is a TTY as discussed in our previous blog post.