Allow ErrorReporter to handle several error classes

`Rails.error.handle` and `Rails.error.record` are able to filter by list of serveral error classes now, for example like this:

```ruby
Rails.error.handle(ArgumentError, TypeError) do
  [1, 2, 3].first(x) # where `x` might be `-4` or `'4'`
end
```
This commit is contained in:
Martin Spickermann 2022-10-21 12:50:48 +02:00
parent b96ddea5f0
commit 1a9b887c0c
4 changed files with 58 additions and 9 deletions

View File

@ -1,3 +1,14 @@
* `Rails.error.handle` and `Rails.error.record` filter now by multiple error classes.
```ruby
Rails.error.handle(IOError, ArgumentError) do
1 + '1' # raises TypeError
end
1 + 1 # TypeErrors are not IOErrors or ArgumentError, so this will *not* be handled
```
*Martin Spickermann*
* `Class#subclasses` and `Class#descendants` now automatically filter reloaded classes.
Previously they could return old implementations of reloadable classes that have been

View File

@ -42,7 +42,7 @@ module ActiveSupport
# 1 + '1'
# end
#
# Can be restricted to handle only a specific error class:
# Can be restricted to handle only specific error classes:
#
# maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
#
@ -69,9 +69,10 @@ module ActiveSupport
# * +:source+ - This value is passed along to subscribers to indicate the
# source of the error. Subscribers can use this value to ignore certain
# errors. Defaults to <tt>"application"</tt>.
def handle(error_class = StandardError, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
def handle(*error_classes, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
error_classes = [StandardError] if error_classes.blank?
yield
rescue error_class => error
rescue *error_classes => error
report(error, handled: true, severity: severity, context: context, source: source)
fallback.call if fallback
end
@ -84,7 +85,7 @@ module ActiveSupport
# 1 + '1'
# end
#
# Can be restricted to handle only a specific error class:
# Can be restricted to handle only specific error classes:
#
# tags = Rails.error.record(Redis::BaseError) { redis.get("tags") }
#
@ -104,9 +105,10 @@ module ActiveSupport
# * +:source+ - This value is passed along to subscribers to indicate the
# source of the error. Subscribers can use this value to ignore certain
# errors. Defaults to <tt>"application"</tt>.
def record(error_class = StandardError, severity: :error, context: {}, source: DEFAULT_SOURCE)
def record(*error_classes, severity: :error, context: {}, source: DEFAULT_SOURCE)
error_classes = [StandardError] if error_classes.blank?
yield
rescue error_class => error
rescue *error_classes => error
report(error, handled: false, severity: severity, context: context, source: source)
raise
end

View File

@ -68,6 +68,23 @@ class ErrorReporterTest < ActiveSupport::TestCase
assert_equal [], @subscriber.events
end
test "#handle can be scoped to several exception classes" do
assert_raises ArgumentError do
@reporter.handle(NameError, NoMethodError) do
raise ArgumentError
end
end
assert_equal [], @subscriber.events
end
test "#handle swallows and reports matching errors" do
error = ArgumentError.new("Oops")
@reporter.handle(NameError, ArgumentError) do
raise error
end
assert_equal [[error, true, :warning, "application", {}]], @subscriber.events
end
test "#handle passes through the return value" do
result = @reporter.handle do
2 + 2
@ -125,6 +142,25 @@ class ErrorReporterTest < ActiveSupport::TestCase
assert_equal [], @subscriber.events
end
test "#record can be scoped to several exception classes" do
assert_raises ArgumentError do
@reporter.record(NameError, NoMethodError) do
raise ArgumentError
end
end
assert_equal [], @subscriber.events
end
test "#record report any matching, unhandled error and re-raise them" do
error = ArgumentError.new("Oops")
assert_raises ArgumentError do
@reporter.record(NameError, ArgumentError) do
raise error
end
end
assert_equal [[error, false, :error, "application", {}]], @subscriber.events
end
test "#record passes through the return value" do
result = @reporter.record do
2 + 2

View File

@ -131,9 +131,9 @@ Rails.error.handle(context: {user_id: user.id}, severity: :info) do
end
```
### Filtering by Error Class
### Filtering by Error Classes
With `Rails.error.handle` and `Rails.error.record`, you can also choose to only report errors of a certain class. For example:
With `Rails.error.handle` and `Rails.error.record`, you can also choose to only report errors of certain classes. For example:
```ruby
Rails.error.handle(IOError) do