diff --git a/CHANGELOG.md b/CHANGELOG.md index e4c9460669..c94600fadc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Next Release #### Fixes +* [#651](https://github.com/intridea/grape/pull/651): The `rescue_from` keyword now properly defaults to rescuing subclasses of exceptions - [@xevix](https://github.com/xevix). * [#614](https://github.com/intridea/grape/pull/614): Params with `nil` value are now refused by `RegexpValidator` - [@dm1try](https://github.com/dm1try). * [#494](https://github.com/intridea/grape/issues/494): Fixed performance issue with requests carrying a large payload - [@dblock](https://github.com/dblock). * [#619](https://github.com/intridea/grape/pull/619): Convert specs to RSpec 3 syntax with Transpec - [@danielspector](https://github.com/danielspector). diff --git a/lib/grape/api.rb b/lib/grape/api.rb index 353e608f5f..336c9266f0 100644 --- a/lib/grape/api.rb +++ b/lib/grape/api.rb @@ -214,12 +214,22 @@ def rescue_from(*args, &block) options = args.last.is_a?(Hash) ? args.pop : {} handler ||= proc { options[:with] } if options.key?(:with) - handler_type = !!options[:rescue_subclasses] ? :rescue_handlers : :base_only_rescue_handlers - imbue handler_type, Hash[args.map { |arg| [arg, handler] }] + if args.include?(:all) + set(:rescue_all, true) + imbue :all_rescue_handler, handler + else + handler_type = + case options[:rescue_subclasses] + when nil, true + :rescue_handlers + else + :base_only_rescue_handlers + end - imbue(:rescue_options, options) + imbue handler_type, Hash[args.map { |arg| [arg, handler] }] + end - set(:rescue_all, true) if args.include?(:all) + imbue(:rescue_options, options) end # Allows you to specify a default representation entity for a diff --git a/lib/grape/endpoint.rb b/lib/grape/endpoint.rb index 41d24212f0..1e063f520f 100644 --- a/lib/grape/endpoint.rb +++ b/lib/grape/endpoint.rb @@ -441,7 +441,8 @@ def build_middleware error_formatters: settings[:error_formatters], rescue_options: settings[:rescue_options], rescue_handlers: merged_setting(:rescue_handlers), - base_only_rescue_handlers: merged_setting(:base_only_rescue_handlers) + base_only_rescue_handlers: merged_setting(:base_only_rescue_handlers), + all_rescue_handler: settings[:all_rescue_handler] aggregate_setting(:middleware).each do |m| m = m.dup diff --git a/lib/grape/middleware/error.rb b/lib/grape/middleware/error.rb index d824aea426..ffe9704dd9 100644 --- a/lib/grape/middleware/error.rb +++ b/lib/grape/middleware/error.rb @@ -14,7 +14,8 @@ def default_options rescue_subclasses: true, # rescue subclasses of exceptions listed rescue_options: { backtrace: false }, # true to display backtrace rescue_handlers: {}, # rescue handler blocks - base_only_rescue_handlers: {} # rescue handler blocks rescuing only the base class + base_only_rescue_handlers: {}, # rescue handler blocks rescuing only the base class + all_rescue_handler: nil # rescue handler block to rescue from all exceptions } end @@ -40,7 +41,8 @@ def call!(env) def find_handler(klass) handler = options[:rescue_handlers].find(-> { [] }) { |error, _| klass <= error }[1] - handler = options[:base_only_rescue_handlers][klass] || options[:base_only_rescue_handlers][:all] unless handler + handler = options[:base_only_rescue_handlers][klass] unless handler + handler = options[:all_rescue_handler] unless handler handler end diff --git a/spec/grape/api_spec.rb b/spec/grape/api_spec.rb index 7c6c825467..1d32aa788d 100644 --- a/spec/grape/api_spec.rb +++ b/spec/grape/api_spec.rb @@ -1343,6 +1343,18 @@ class ChildError < ParentError; end expect { get '/uncaught_parent' }.to raise_error(StandardError) end + it 'sets rescue_subclasses to true by default' do + subject.rescue_from APIErrors::ParentError do |e| + rack_response("rescued from #{e.class.name}", 500) + end + subject.get '/caught_child' do + raise APIErrors::ChildError + end + + get '/caught_child' + expect(last_response.status).to eql 500 + end + it 'does not rescue child errors if rescue_subclasses is false' do subject.rescue_from APIErrors::ParentError, rescue_subclasses: false do |e| rack_response("rescued from #{e.class.name}", 500)