From c20a9ad3ff81865898258d8bf92271048f1ff2b0 Mon Sep 17 00:00:00 2001 From: Sunny Juneja Date: Fri, 27 Mar 2015 15:34:45 -0700 Subject: [PATCH] Add error! to rescue_from. Fixes #889. --- CHANGELOG.md | 1 + README.md | 31 ++++++++++---- UPGRADING.md | 41 +++++++++++++++++++ lib/grape/error_formatter/json.rb | 2 +- lib/grape/exceptions/validation_errors.rb | 4 +- lib/grape/middleware/error.rb | 6 +++ .../exceptions/validation_errors_spec.rb | 2 +- 7 files changed, 74 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84be08394e..b66d0b8460 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * [#952](https://github.com/intridea/grape/pull/952): Status method now raises error when called with invalid status code - [@dabrorius](https://github.com/dabrorius). * [#957](https://github.com/intridea/grape/pull/957): Regexp validator now supports `allow_blank`, `nil` value behavior changed - [@calfzhou](https://giihub.com/calfzhou). * [#962](https://github.com/intridea/grape/pull/962): The `default` attribute with `false` value is documented now - [@ajvondrak](https://github.com/ajvondrak). +* [#974](https://github.com/intridea/grape/pull/974): Add error! to rescue_from blocks - [@whatasunnyday](https://github.com/whatasunnyday). * Your contribution here. 0.11.0 (2/23/2015) diff --git a/README.md b/README.md index b0c167cbd9..4d2d7dacd1 100644 --- a/README.md +++ b/README.md @@ -1031,7 +1031,7 @@ You can rescue a `Grape::Exceptions::ValidationErrors` and respond with a custom ```ruby format :json subject.rescue_from Grape::Exceptions::ValidationErrors do |e| - rack_response e.to_json, 400 + error! e, 400 end ``` @@ -1442,17 +1442,29 @@ class Twitter::API < Grape::API end ``` -You can rescue all exceptions with a code block. The `error_response` wrapper +You can rescue all exceptions with a code block. The `error!` wrapper automatically sets the default error code and content-type. ```ruby class Twitter::API < Grape::API rescue_from :all do |e| - error_response({ message: "rescued from #{e.class.name}" }) + error!("rescued from #{e.class.name}") end end ``` +Optionally, you can set the format, status code and headers. + +```ruby +class Twitter::API < Grape::API + format :json + rescue_from :all do |e| + error!({ error: "Server error.", 500, { 'Content-Type' => 'text/error' } }) + end +end +``` + + You can also rescue specific exceptions with a code block and handle the Rack response at the lowest level. @@ -1469,10 +1481,11 @@ Or rescue specific exceptions. ```ruby class Twitter::API < Grape::API rescue_from ArgumentError do |e| - Rack::Response.new([ "ArgumentError: #{e.message}" ], 500).finish + error!("ArgumentError: #{e.message}") end + rescue_from NotImplementedError do |e| - Rack::Response.new([ "NotImplementedError: #{e.message}" ], 500).finish + error!("NotImplementedError: #{e.message}") end end ``` @@ -1492,10 +1505,10 @@ Then the following `rescue_from` clause will rescue exceptions of type `APIError ```ruby rescue_from APIErrors::ParentError do |e| - Rack::Response.new({ + error!({ error: "#{e.class} error", message: e.message - }.to_json, e.status).finish + }, e.status) end ``` @@ -1504,11 +1517,11 @@ The code below will rescue exceptions of type `RuntimeError` but _not_ its subcl ```ruby rescue_from RuntimeError, rescue_subclasses: false do |e| - Rack::Response.new({ + error!({ status: e.status, message: e.message, errors: e.errors - }.to_json, e.status).finish + }, e.status) end ``` diff --git a/UPGRADING.md b/UPGRADING.md index 74db721a34..734aa950dc 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -15,6 +15,47 @@ end See [#957](https://github.com/intridea/grape/pull/957) for more information. +#### Replace error_response with error! in rescue_from blocks + +Note: `error_response` is being deprecated, not removed. + +```ruby +def error!(message, status = options[:default_status], headers = {}, backtrace = []) + headers = { 'Content-Type' => content_type }.merge(headers) + rack_response(format_message(message, backtrace), status, headers) +end +``` + +For example, + +``` +error_response({ message: { message: 'No such page.', id: 'missing_page' }, status: 404, headers: { 'Content-Type' => 'api/error' }) +``` + +becomes + +``` +error!({ message: 'No such page.', id: 'missing_page' }, 404, { 'Content-Type' => 'api/error' }) +``` + +`error!` also supports just passing a message. `error!('Server error.')` and `format: :json` returns the following JSON response + +``` +{ 'error': 'Server error. } +``` + +with a status code of 500 and a Content Type of text/error. + +Optionally, also replace `Rack::Response.new` with `error!.` +The following are equivalent: + +``` +Rack::Response.new([ e.message ], 500, { "Content-type" => "text/error" }).finish +error!(e) +``` + +See [#889](https://github.com/intridea/grape/issues/889) for more information. + ### Upgrading to >= 0.11.0 #### Added Rack 1.6.0 support diff --git a/lib/grape/error_formatter/json.rb b/lib/grape/error_formatter/json.rb index 3ba6eec02f..609f8d5866 100644 --- a/lib/grape/error_formatter/json.rb +++ b/lib/grape/error_formatter/json.rb @@ -5,7 +5,7 @@ class << self def call(message, backtrace, options = {}, env = nil) message = Grape::ErrorFormatter::Base.present(message, env) - result = message.is_a?(Hash) ? message : { error: message } + result = message.is_a?(String) ? { error: message } : message if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty? result = result.merge(backtrace: backtrace) end diff --git a/lib/grape/exceptions/validation_errors.rb b/lib/grape/exceptions/validation_errors.rb index bfb7045153..76feeb3160 100644 --- a/lib/grape/exceptions/validation_errors.rb +++ b/lib/grape/exceptions/validation_errors.rb @@ -24,7 +24,7 @@ def each end end - def as_json + def as_json(_opts = {}) errors.map do |k, v| { params: k, @@ -33,7 +33,7 @@ def as_json end end - def to_json + def to_json(_opts = {}) as_json.to_json end diff --git a/lib/grape/middleware/error.rb b/lib/grape/middleware/error.rb index a77db42c13..4dccb491da 100644 --- a/lib/grape/middleware/error.rb +++ b/lib/grape/middleware/error.rb @@ -58,10 +58,16 @@ def exec_handler(e, &handler) end end + def error!(message, status = options[:default_status], headers = {}, backtrace = []) + headers = { 'Content-Type' => content_type }.merge(headers) + rack_response(format_message(message, backtrace), status, headers) + end + def handle_error(e) error_response(message: e.message, backtrace: e.backtrace) end + # TODO: This method is deprecated. Refactor out. def error_response(error = {}) status = error[:status] || options[:default_status] message = error[:message] || options[:default_message] diff --git a/spec/grape/exceptions/validation_errors_spec.rb b/spec/grape/exceptions/validation_errors_spec.rb index 08eea37f2f..a3fc2fccbf 100644 --- a/spec/grape/exceptions/validation_errors_spec.rb +++ b/spec/grape/exceptions/validation_errors_spec.rb @@ -27,7 +27,7 @@ def app it 'can return structured json with separate fields' do subject.format :json subject.rescue_from Grape::Exceptions::ValidationErrors do |e| - rack_response e.to_json, 400 + error!(e, 400) end subject.params do optional :beer