Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a DSL to declare 'error_formatter' in API settings. #284

Merged
merged 3 commits into from
Dec 1, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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).
* [#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
Expand Down
27 changes: 25 additions & 2 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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
Expand All @@ -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`
Expand Down
5 changes: 5 additions & 0 deletions lib/grape/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions lib/grape/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions lib/grape/error_formatter/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion lib/grape/middleware/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
23 changes: 21 additions & 2 deletions spec/grape/api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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]}\"}"
Expand Down
6 changes: 4 additions & 2 deletions spec/grape/middleware/exception_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down