Ruby 2.5 adds Exception#full_message method

Vishal Telangre

By Vishal Telangre

on 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.

1class AverageService
2  attr_reader :numbers, :coerced_numbers
3
4  def initialize(numbers)
5    @numbers = numbers
6    @coerced_numbers = coerce_numbers
7  end
8
9  def average
10    sum / count
11  end
12
13  private
14
15  def coerce_numbers
16    numbers.map do |number|
17      begin
18        Float(number)
19      rescue Exception => exception
20        puts "#{exception.message} (#{exception.class})\n\t#{exception.backtrace.join("\n\t")}"
21        puts "Coercing '#{number}' as 0.0\n\n"
22
23        0.0
24      end
25    end
26  end
27
28  def sum
29    coerced_numbers.map(&:to_f).sum
30  end
31
32  def count
33    coerced_numbers.size.to_f
34  end
35end
36
37average = AverageService.new(ARGV).average
38puts "Average is: #{average}"
1$ RBENV_VERSION=2.4.0 ruby average_service.rb 5 4f 7 1s0
2invalid value for Float(): "4f" (ArgumentError)
3	average_service.rb:18:in `Float'
4	average_service.rb:18:in `block in coerce_numbers'
5	average_service.rb:16:in `map'
6	average_service.rb:16:in `coerce_numbers'
7	average_service.rb:6:in `initialize'
8	average_service.rb:37:in `new'
9	average_service.rb:37:in `<main>'
10
11Coercing '4f' as 0.0
12
13invalid value for Float(): "1s0" (ArgumentError)
14	average_service.rb:18:in `Float'
15	average_service.rb:18:in `block in coerce_numbers'
16	average_service.rb:16:in `map'
17	average_service.rb:16:in `coerce_numbers'
18	average_service.rb:6:in `initialize'
19	average_service.rb:37:in `new'
20	average_service.rb:37:in `<main>'
21
22Coercing '1s0' as 0.0
23
24Average 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.

1class AverageService
2  attr_reader :numbers, :coerced_numbers
3
4  def initialize(numbers)
5    @numbers = numbers
6    @coerced_numbers = coerce_numbers
7  end
8
9  def average
10    sum / count
11  end
12
13  private
14
15  def coerce_numbers
16    numbers.map do |number|
17      begin
18        Float(number)
19      rescue Exception => exception
20        puts exception.full_message
21        puts "Coercing '#{number}' as 0.0\n\n"
22
23        0.0
24      end
25    end
26  end
27
28  def sum
29    coerced_numbers.map(&:to_f).sum
30  end
31
32  def count
33    coerced_numbers.size.to_f
34  end
35end
36
37average = AverageService.new(ARGV).average
38puts "Average is: #{average}"
1$ RBENV_VERSION=2.5.0 ruby average_service.rb 5 4f 7 1s0
2Traceback (most recent call last):
3	6: from average_service.rb:37:in `<main>'
4	5: from average_service.rb:37:in `new'
5	4: from average_service.rb:6:in `initialize'
6	3: from average_service.rb:16:in `coerce_numbers'
7	2: from average_service.rb:16:in `map'
8	1: from average_service.rb:18:in `block in coerce_numbers'
9average_service.rb:18:in `Float': invalid value for Float(): "4f" (ArgumentError)
10
11Coercing '4f' as 0.0
12
13Traceback (most recent call last):
14	6: from average_service.rb:37:in `<main>'
15	5: from average_service.rb:37:in `new'
16	4: from average_service.rb:6:in `initialize'
17	3: from average_service.rb:16:in `coerce_numbers'
18	2: from average_service.rb:16:in `map'
19	1: from average_service.rb:18:in `block in coerce_numbers'
20average_service.rb:18:in `Float': invalid value for Float(): "1s0" (ArgumentError)
21
22Coercing '1s0' as 0.0
23
24Average 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.

Stay up to date with our blogs.

Subscribe to receive email notifications for new blog posts.