March 13, 2018
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})\n\t#{exception.backtrace.join("\n\t")}"
puts "Coercing '#{number}' as 0.0\n\n"
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 4f 7 1s0
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 '1s0' 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\n\n"
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 4f 7 1s0
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 '1s0' 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.
If this blog was helpful, check out our full blog archive.