From bdb99e79cd0c08c2c57b7e699be81efacc386384 Mon Sep 17 00:00:00 2001 From: dblock Date: Sat, 1 Dec 2012 17:16:43 -0500 Subject: [PATCH 1/3] Added a DSL to declare 'error_formatter' in API settings. --- CHANGELOG.markdown | 3 ++- README.markdown | 27 +++++++++++++++++++++++-- lib/grape/api.rb | 5 +++++ lib/grape/endpoint.rb | 1 + lib/grape/error_formatter/base.rb | 4 ++-- lib/grape/middleware/error.rb | 3 ++- spec/grape/api_spec.rb | 23 +++++++++++++++++++-- spec/grape/middleware/exception_spec.rb | 6 ++++-- spec/spec_helper.rb | 2 +- 9 files changed, 63 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index db6dc10f58..9bc44a80c2 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -4,8 +4,9 @@ * [#265](https://github.com/intridea/grape/issues/264): Fix: Moved `ValidationError` into `Grape::Exceptions` - [@thepumpkin1979](https://github.com/thepumpkin1979). * [#269](https://github.com/intridea/grape/pull/269): Fix: `LocalJumpError` will not be raised when using explict return in API methods - [@simulacre](https://github.com/simulacre). * [#86](https://github.com/intridea/grape/issues/275): Fix Path-based versioning not recognizing '/' route - [@walski](https://github.com/walski). -* [#277](https://github.com/intridea/grape/pull/277): Added a DSL to declare `formatter` in API settings - [@tim-vandecasteele](https://github.com/tim-vandecasteele). * [#273](https://github.com/intridea/grape/pull/273): Disabled formatting via `serializable_hash` and added support for `format :serializable_hash` in API settings - [@dblock](https://github.com/dblock). +* [#277](https://github.com/intridea/grape/pull/277): Added a DSL to declare `formatter` in API settings - [@tim-vandecasteele](https://github.com/tim-vandecasteele). +* [#274](https://github.com/intridea/grape/pull/274): Added a DSL to declare `error_formatter` in API settings - [@dblock](https://github.com/dblock). * Your contribution here. 0.2.2 diff --git a/README.markdown b/README.markdown index 1cdd1b44aa..58ebb836d1 100644 --- a/README.markdown +++ b/README.markdown @@ -501,6 +501,29 @@ class Twitter::API < Grape::API end ``` +Custom error formatters for existing and additional types can be defined with a proc. + +``` ruby +class Twitter::API < Grape::API + error_formatter :txt, lambda { |message, backtrace, options| "error: #{message} from #{backtrace}" } +end +``` + +You can also use a module or class. + +``` ruby +module CustomFormatter + def self.call(message, backtrace, options) + { message: message, backtrace: backtrace } + end +end + +class Twitter::API < Grape::API + error_format :custom + error_formatter :custom, CustomFormatter +end +``` + You can rescue all exceptions with a code block. The `rack_response` wrapper automatically sets the default error code and content-type. @@ -589,7 +612,7 @@ Serialization takes place automatically. Your API can declare additional types to support. Response format is determined by the request's extension, an explicit `format` parameter in the query string, or `Accept` header. -Custom formatters for additional types can be defined with a proc. +Custom formatters for existing and additional types can be defined with a proc. ``` ruby class Twitter::API < Grape::API @@ -613,7 +636,7 @@ class Twitter::API < Grape::API end ``` -You can also set the default format. Available formats are the following. +You can set the default format. Available formats are the following. * `:json`: use object's `to_json` when available, otherwise call `MultiJson.dump` * `:xml`: use object's `to_xml` when available, usually via `MultiXml`, otherwise call `to_s` diff --git a/lib/grape/api.rb b/lib/grape/api.rb index 527978dc63..41e196c770 100644 --- a/lib/grape/api.rb +++ b/lib/grape/api.rb @@ -130,10 +130,15 @@ def format(new_format = nil) new_format ? set(:format, new_format.to_sym) : settings[:format] end + # Specify a custom formatter for a contnet-type. def formatter(content_type, new_formatter) settings.imbue(:formatters, content_type.to_sym => new_formatter) end + def error_formatter(format, new_formatter) + settings.imbue(:error_formatters, format.to_sym => new_formatter) + end + # Specify the format for error messages. # May be `:json` or `:txt` (default). def error_format(new_format = nil) diff --git a/lib/grape/endpoint.rb b/lib/grape/endpoint.rb index 6765a730cf..5030714c5f 100644 --- a/lib/grape/endpoint.rb +++ b/lib/grape/endpoint.rb @@ -378,6 +378,7 @@ def build_middleware :rescue_all => settings[:rescue_all], :rescued_errors => aggregate_setting(:rescued_errors), :format => settings[:error_format] || :txt, + :error_formatters => settings[:error_formatters], :rescue_options => settings[:rescue_options], :rescue_handlers => merged_setting(:rescue_handlers) diff --git a/lib/grape/error_formatter/base.rb b/lib/grape/error_formatter/base.rb index 61dbc605f8..2e5efee4f8 100644 --- a/lib/grape/error_formatter/base.rb +++ b/lib/grape/error_formatter/base.rb @@ -11,14 +11,14 @@ class << self } def formatters(options) - FORMATTERS.merge(options[:formatters] || {}) + FORMATTERS.merge(options[:error_formatters] || {}) end def formatter_for(api_format, options = {}) spec = formatters(options)[api_format] case spec when nil - lambda { |obj| obj } + lambda { |message, backtrace, options| message } when Symbol method(spec) else diff --git a/lib/grape/middleware/error.rb b/lib/grape/middleware/error.rb index 73e34bb490..4380012595 100644 --- a/lib/grape/middleware/error.rb +++ b/lib/grape/middleware/error.rb @@ -11,6 +11,7 @@ def default_options :default_message => "", :format => :txt, :formatters => {}, + :error_formatters => {}, :rescue_all => false, # true to rescue all exceptions :rescue_options => { :backtrace => false }, # true to display backtrace :rescue_handlers => {}, # rescue handler blocks @@ -61,7 +62,7 @@ def rack_response(message, status = options[:default_status], headers = { 'Conte def format_message(message, backtrace, status) formatter = Grape::ErrorFormatter::Base.formatter_for(options[:format], options) throw :error, :status => 406, :message => "The requested format #{options[:format]} is not supported." unless formatter - formatter.send formatter.respond_to?(:encode) ? :encode : :call, message, backtrace, options + formatter.call(message, backtrace, options) end end diff --git a/spec/grape/api_spec.rb b/spec/grape/api_spec.rb index b0c5a35bed..a956e891df 100644 --- a/spec/grape/api_spec.rb +++ b/spec/grape/api_spec.rb @@ -879,7 +879,7 @@ class CommunicationError < RuntimeError; end last_response.body.should eql "rain!" end - it 'should rescue all errros and return :txt with backtrace' do + it 'should rescue all errors and return :txt with backtrace' do subject.rescue_from :all, :backtrace => true subject.error_format :txt subject.get '/exception' do @@ -889,6 +889,25 @@ class CommunicationError < RuntimeError; end last_response.body.start_with?("rain!\r\n").should be_true end + context "class" do + before :each do + class CustomErrorFormatter + def self.call(message, backtrace, options) + "message: #{message} @backtrace" + end + end + end + it 'should return a custom error format' do + subject.rescue_from :all, :backtrace => true + subject.error_formatter :txt, CustomErrorFormatter + subject.get '/exception' do + raise "rain!" + end + get '/exception' + last_response.body.should == "message: rain! @backtrace" + end + end + it 'should rescue all errors and return :json' do subject.rescue_from :all subject.error_format :json @@ -981,7 +1000,7 @@ class CommunicationError < RuntimeError; end last_response.body.should eql '{"custom_formatter":"hash"}' end end - context "custom formatter with a class" do + context "custom formatter class" do module CustomFormatter def self.call(object) "{\"custom_formatter\":\"#{object[:some]}\"}" diff --git a/spec/grape/middleware/exception_spec.rb b/spec/grape/middleware/exception_spec.rb index 403519917a..56a6e97076 100644 --- a/spec/grape/middleware/exception_spec.rb +++ b/spec/grape/middleware/exception_spec.rb @@ -109,8 +109,10 @@ def app use Grape::Middleware::Error, :rescue_all => true, :format => :custom, - :formatters => { - :custom => lambda { |message, backtrace, options| { :custom_formatter => message }.inspect } + :error_formatters => { + :custom => lambda { |message, backtrace, options| + { :custom_formatter => message }.inspect + } } run ExceptionApp end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4c5cdcadec..9b3e9444aa 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,7 +2,7 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'support')) -$stdout = StringIO.new +# $stdout = StringIO.new require 'grape' From b97b13d3ae8b7b8111e3bbfd792519391f75bd55 Mon Sep 17 00:00:00 2001 From: dblock Date: Sat, 1 Dec 2012 17:18:56 -0500 Subject: [PATCH 2/3] Fixed link in pull request. --- CHANGELOG.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index 9bc44a80c2..96c6665492 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -6,7 +6,7 @@ * [#86](https://github.com/intridea/grape/issues/275): Fix Path-based versioning not recognizing '/' route - [@walski](https://github.com/walski). * [#273](https://github.com/intridea/grape/pull/273): Disabled formatting via `serializable_hash` and added support for `format :serializable_hash` in API settings - [@dblock](https://github.com/dblock). * [#277](https://github.com/intridea/grape/pull/277): Added a DSL to declare `formatter` in API settings - [@tim-vandecasteele](https://github.com/tim-vandecasteele). -* [#274](https://github.com/intridea/grape/pull/274): Added a DSL to declare `error_formatter` in API settings - [@dblock](https://github.com/dblock). +* [#284](https://github.com/intridea/grape/pull/284): Added a DSL to declare `error_formatter` in API settings - [@dblock](https://github.com/dblock). * Your contribution here. 0.2.2 From 479e9312b7e6f021894e8b62960d4bda5388e274 Mon Sep 17 00:00:00 2001 From: dblock Date: Sat, 1 Dec 2012 17:20:11 -0500 Subject: [PATCH 3/3] Undo commented out stdout override. --- spec/spec_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9b3e9444aa..4c5cdcadec 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,7 +2,7 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'support')) -# $stdout = StringIO.new +$stdout = StringIO.new require 'grape'