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

Disabled formatting via and added support for :serializable_hash in API settings. #282

Merged
merged 6 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
1 change: 1 addition & 0 deletions CHANGELOG.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* [#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).
* Your contribution here.

0.2.2
Expand Down
72 changes: 47 additions & 25 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
## What is Grape?

Grape is a REST-like API micro-framework for Ruby. It's designed to run on Rack
or complement existing web application frameworks such as Rails and Sinatra by
providing a simple DSL to easily develop RESTful APIs. It has built-in support
for common conventions, including multiple formats, subdomain/prefix restriction,
or complement existing web application frameworks such as Rails and Sinatra by
providing a simple DSL to easily develop RESTful APIs. It has built-in support
for common conventions, including multiple formats, subdomain/prefix restriction,
content negotiation, versioning and much more.

[![Build Status](https://travis-ci.org/intridea/grape.png?branch=master)](http://travis-ci.org/intridea/grape)
Expand Down Expand Up @@ -54,7 +54,7 @@ class Twitter::API < Grape::API
end

resource :statuses do

desc "Returns a public timeline."
get :public_timeline do
Tweet.limit(20)
Expand All @@ -73,7 +73,7 @@ class Twitter::API < Grape::API
get '/show/:id' do
Tweet.find(params[:id])
end

desc "Creates a tweet."
params do
requires :status, :type => String, :desc => "Your status."
Expand Down Expand Up @@ -133,7 +133,7 @@ end

## Versioning

There are three strategies in which clients can reach your API's endpoints: `:header`,
There are three strategies in which clients can reach your API's endpoints: `:header`,
`:path` and `:param`. The default strategy is `:path`.

### Header
Expand Down Expand Up @@ -167,7 +167,7 @@ Using this versioning strategy, clients should pass the desired version in the U
version 'v1', :using => :param
```

Using this versioning strategy, clients should pass the desired version as a request parameter,
Using this versioning strategy, clients should pass the desired version as a request parameter,
either in the URL query string or in the request body.

curl -H http://localhost:9292/events?apiver=v1
Expand Down Expand Up @@ -235,7 +235,7 @@ get ':id' do
end
```

When a type is specified an implicit validation is done after the coercion to ensure
When a type is specified an implicit validation is done after the coercion to ensure
the output type is the one declared.

Parameters can be nested using `group`. In the above example, this means both
Expand Down Expand Up @@ -268,9 +268,9 @@ class AlphaNumeric < Grape::Validations::Validator
def validate_param!(attr_name, params)
unless params[attr_name] =~ /^[[:alnum:]]+$/
throw :error, :status => 400, :message => "#{attr_name}: must consist of alpha-numeric characters"
end
end
end
end
end
```

```ruby
Expand All @@ -286,10 +286,10 @@ class Length < Grape::Validations::SingleOptionValidator
def validate_param!(attr_name, params)
unless params[attr_name].length == @option
throw :error, :status => 400, :message => "#{attr_name}: must be #{@option} characters long"
end
end
end
end
```
end
```

```ruby
params do
Expand All @@ -309,8 +309,8 @@ rescue_from Grape::Exceptions::ValidationError do |e|
'status' => e.status,
'message' => e.message,
'param' => e.param
}.to_json, e.status)
end
}.to_json, e.status)
end
```

## Headers
Expand Down Expand Up @@ -436,10 +436,10 @@ end
```

``` shell
curl -v -X OPTIONS http://localhost:3000/counter
curl -v -X OPTIONS http://localhost:3000/counter

> OPTIONS /counter HTTP/1.1
>
>
< HTTP/1.1 204 No Content
< Allow: OPTIONS, GET, PUT
```
Expand All @@ -453,7 +453,7 @@ curl -X DELETE -v http://localhost:3000/counter/

> DELETE /counter/ HTTP/1.1
> Host: localhost:3000
>
>
< HTTP/1.1 405 Method Not Allowed
< Allow: OPTIONS, GET, PUT
```
Expand Down Expand Up @@ -493,7 +493,7 @@ end
```

The error format can be specified using `error_format`. Available formats are
`:json` and `:txt` (default).
`:json`, `:xml` and `:txt` (default).

``` ruby
class Twitter::API < Grape::API
Expand Down Expand Up @@ -589,7 +589,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 or by method pointer.
Custom formatters for additional types can be defined with a proc.

``` ruby
class Twitter::API < Grape::API
Expand All @@ -598,7 +598,29 @@ class Twitter::API < Grape::API
end
```

You can also set the default format. The order for choosing the format is the following.
You can also use a module or class.

``` ruby
module XlsFormatter
def self.call(object)
object.to_fancy_xls
end
end

class Twitter::API < Grape::API
content_type :xls, "application/vnd.ms-excel"
formatter :xls, XlsFormatter
end
```

You can also 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`
* `:txt`: use object's `to_txt` when available, otherwise `to_s`
* `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`

The order for choosing the format is the following.

* Use the file extension, if specified. If the file is .json, choose the JSON format.
* Use the value of the `format` parameter in the query string, if specified.
Expand Down Expand Up @@ -789,7 +811,7 @@ end

## Describing and Inspecting an API

Grape routes can be reflected at runtime. This can notably be useful for generating
Grape routes can be reflected at runtime. This can notably be useful for generating
documentation.

Grape exposes arrays of API versions and compiled routes. Each route
Expand All @@ -805,7 +827,7 @@ TwitterAPI::routes[0].route_version # yields 'v1'
TwitterAPI::routes[0].route_description # etc.
```

It's possible to retrieve the information about the current route from within an API
It's possible to retrieve the information about the current route from within an API
call with `route`.

``` ruby
Expand All @@ -824,9 +846,9 @@ end

Grape by default anchors all request paths, which means that the request URL
should match from start to end to match, otherwise a `404 Not Found` is
returned. However, this is sometimes not what you want, because it is not always
returned. However, this is sometimes not what you want, because it is not always
known upfront what can be expected from the call. This is because Rack-mount by
default anchors requests to match from the start to the end, or not at all.
default anchors requests to match from the start to the end, or not at all.
Rails solves this problem by using a `:anchor => false` option in your routes.
In Grape this option can be used as well when a method is defined.

Expand Down
65 changes: 43 additions & 22 deletions lib/grape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,63 @@
require 'rack/builder'

module Grape
autoload :API, 'grape/api'
autoload :Endpoint, 'grape/endpoint'
autoload :MiddlewareStack, 'grape/middleware_stack'
autoload :Client, 'grape/client'
autoload :Route, 'grape/route'
autoload :Entity, 'grape/entity'
autoload :Cookies, 'grape/cookies'
autoload :Validations, 'grape/validations'
autoload :API, 'grape/api'
autoload :Endpoint, 'grape/endpoint'
autoload :MiddlewareStack, 'grape/middleware_stack'
autoload :Client, 'grape/client'
autoload :Route, 'grape/route'
autoload :Entity, 'grape/entity'
autoload :Cookies, 'grape/cookies'
autoload :Validations, 'grape/validations'

module Exceptions
autoload :Base, 'grape/exceptions/base'
autoload :ValidationError, 'grape/exceptions/validation_error'
autoload :Base, 'grape/exceptions/base'
autoload :ValidationError, 'grape/exceptions/validation_error'
end

module ErrorFormatter
autoload :Base, 'grape/error_formatter/base'
autoload :Json, 'grape/error_formatter/json'
autoload :Txt, 'grape/error_formatter/txt'
autoload :Xml, 'grape/error_formatter/xml'
end

module Formatter
autoload :Base, 'grape/formatter/base'
autoload :Json, 'grape/formatter/json'
autoload :SerializableHash, 'grape/formatter/serializable_hash'
autoload :Txt, 'grape/formatter/txt'
autoload :Xml, 'grape/formatter/xml'
end

module Parser
autoload :Base, 'grape/parser/base'
autoload :Json, 'grape/parser/json'
autoload :Xml, 'grape/parser/xml'
end

module Middleware
autoload :Base, 'grape/middleware/base'
autoload :Prefixer, 'grape/middleware/prefixer'
autoload :Versioner, 'grape/middleware/versioner'
autoload :Formatter, 'grape/middleware/formatter'
autoload :Error, 'grape/middleware/error'
autoload :Base, 'grape/middleware/base'
autoload :Prefixer, 'grape/middleware/prefixer'
autoload :Versioner, 'grape/middleware/versioner'
autoload :Formatter, 'grape/middleware/formatter'
autoload :Error, 'grape/middleware/error'

module Auth
autoload :OAuth2, 'grape/middleware/auth/oauth2'
autoload :Basic, 'grape/middleware/auth/basic'
autoload :Digest, 'grape/middleware/auth/digest'
autoload :OAuth2, 'grape/middleware/auth/oauth2'
autoload :Basic, 'grape/middleware/auth/basic'
autoload :Digest, 'grape/middleware/auth/digest'
end

module Versioner
autoload :Path, 'grape/middleware/versioner/path'
autoload :Header, 'grape/middleware/versioner/header'
autoload :Param, 'grape/middleware/versioner/param'
autoload :Path, 'grape/middleware/versioner/path'
autoload :Header, 'grape/middleware/versioner/header'
autoload :Param, 'grape/middleware/versioner/param'
end
end

module Util
autoload :HashStack, 'grape/util/hash_stack'
autoload :HashStack, 'grape/util/hash_stack'
end
end

Expand Down
32 changes: 32 additions & 0 deletions lib/grape/error_formatter/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
module Grape
module ErrorFormatter
module Base

class << self

FORMATTERS = {
:json => Grape::ErrorFormatter::Json,
:txt => Grape::ErrorFormatter::Txt,
:xml => Grape::ErrorFormatter::Xml
}

def formatters(options)
FORMATTERS.merge(options[:formatters] || {})
end

def formatter_for(api_format, options = {})
spec = formatters(options)[api_format]
case spec
when nil
lambda { |obj| obj }
when Symbol
method(spec)
else
spec
end
end

end
end
end
end
17 changes: 17 additions & 0 deletions lib/grape/error_formatter/json.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Grape
module ErrorFormatter
module Json
class << self

def call(message, backtrace, options = {})
result = message.is_a?(Hash) ? message : { :error => message }
if (options[:rescue_options] || {})[:backtrace] && backtrace && ! backtrace.empty?
result = result.merge({ :backtrace => backtrace })
end
MultiJson.dump(result)
end

end
end
end
end
18 changes: 18 additions & 0 deletions lib/grape/error_formatter/txt.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Grape
module ErrorFormatter
module Txt
class << self

def call(message, backtrace, options = {})
result = message.is_a?(Hash) ? MultiJson.dump(message) : message
if (options[:rescue_options] || {})[:backtrace] && backtrace && ! backtrace.empty?
result += "\r\n "
result += backtrace.join("\r\n ")
end
result
end

end
end
end
end
17 changes: 17 additions & 0 deletions lib/grape/error_formatter/xml.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Grape
module ErrorFormatter
module Xml
class << self

def call(message, backtrace, options = {})
result = message.is_a?(Hash) ? message : { :error => message }
if (options[:rescue_options] || {})[:backtrace] && backtrace && ! backtrace.empty?
result = result.merge({ :backtrace => backtrace })
end
result.respond_to?(:to_xml) ? result.to_xml : result.to_s
end

end
end
end
end
Loading