diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md
index ceacec13653..5d831c937e7 100644
--- a/activesupport/CHANGELOG.md
+++ b/activesupport/CHANGELOG.md
@@ -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
diff --git a/activesupport/lib/active_support/error_reporter.rb b/activesupport/lib/active_support/error_reporter.rb
index 0fe702da10e..d0bb402eeab 100644
--- a/activesupport/lib/active_support/error_reporter.rb
+++ b/activesupport/lib/active_support/error_reporter.rb
@@ -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 "application".
- 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 "application".
- 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
diff --git a/activesupport/test/error_reporter_test.rb b/activesupport/test/error_reporter_test.rb
index 822b0c4a009..07e9faa9d10 100644
--- a/activesupport/test/error_reporter_test.rb
+++ b/activesupport/test/error_reporter_test.rb
@@ -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
diff --git a/guides/source/error_reporting.md b/guides/source/error_reporting.md
index 4a3c495facc..08fce1da373 100644
--- a/guides/source/error_reporting.md
+++ b/guides/source/error_reporting.md
@@ -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
@@ -176,4 +176,4 @@ module MySdk
end
```
-If you register an error subscriber, but still have other error mechanisms like a Rack middleware, you may end up with errors reported multiple times. You should either remove your other mechanisms or adjust your report functionality so it skips reporting an exception it has seen before.
\ No newline at end of file
+If you register an error subscriber, but still have other error mechanisms like a Rack middleware, you may end up with errors reported multiple times. You should either remove your other mechanisms or adjust your report functionality so it skips reporting an exception it has seen before.