Skip to content

Commit d66159b

Browse files
author
Michael Bleigh
committed
Merge branch 'frontier' of github.com:intridea/grape into frontier
2 parents 97d8450 + e40fd4d commit d66159b

File tree

17 files changed

+432
-120
lines changed

17 files changed

+432
-120
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,7 @@ pkg
2727
dist
2828
Gemfile.lock
2929

30+
## Rubinius
31+
.rbx
32+
3033
## PROJECT::SPECIFIC

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ group :development, :test do
99
gem 'guard-bundler'
1010
gem 'rb-fsevent'
1111
gem 'growl'
12+
gem 'json'
1213
end

README.markdown

Lines changed: 96 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
1-
# Grape (Frontier) [![Build Status](http://travis-ci.org/intridea/grape.png)](http://travis-ci.org/intridea/grape)
1+
# Grape (Frontier) [![Build Status](http://travis-ci.org/intridea/grape.png?branch=frontier)](http://travis-ci.org/intridea/grape)
22

33
**Welcome to the `frontier` branch. This is where we're experimenting and building the next version of Grape. Things will be civilized here one day, but until then you best carry your revolver with you.**
44

5-
Grape is a REST-like API micro-framework for Ruby. It is built to complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily provide APIs. It has built-in support for common conventions such as multiple formats, subdomain/prefix restriction, and versioning.
5+
## What is Grape?
6+
7+
Grape is a REST-like API micro-framework for Ruby. It is built to complement
8+
existing web application frameworks such as Rails and Sinatra by providing a
9+
simple DSL to easily provide APIs. It has built-in support for common
10+
conventions such as multiple formats, subdomain/prefix restriction, and
11+
versioning.
12+
13+
## Project Tracking
14+
15+
* [Grape Google Group](http://groups.google.com/group/ruby-grape)
16+
* [Grape Wiki](https://github.com/intridea/grape/wiki)
617

718
## Installation
819

@@ -12,7 +23,9 @@ Grape is available as a gem, to install it just install the gem:
1223

1324
## Basic Usage
1425

15-
Grape APIs are Rack applications that are created by subclassing `Grape::API`. Below is a simple example showing some of the more common features of Grape in the context of recreating parts of the Twitter API.
26+
Grape APIs are Rack applications that are created by subclassing `Grape::API`.
27+
Below is a simple example showing some of the more common features of Grape in
28+
the context of recreating parts of the Twitter API.
1629

1730
```ruby
1831
class Twitter::API < Grape::API
@@ -61,7 +74,8 @@ class Twitter::API < Grape::API
6174
end
6275
```
6376

64-
This would create a Rack application that could be used like so (in a Rackup config.ru file):
77+
This would create a Rack application that could be used like so (in a Rackup
78+
config.ru file):
6579

6680
```ruby
6781
run Twitter::API
@@ -82,10 +96,14 @@ request:
8296

8397
curl -H Accept=application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
8498

85-
By default, the first matching version is used when no Accept header is supplied. This behavior is similar to routing in Rails.
86-
To circumvent this default behaviour, one could use the `:strict` option. When this option is set to `true`, a `404 Not found` error is returned when no correct Accept header is supplied.
99+
By default, the first matching version is used when no Accept header is
100+
supplied. This behavior is similar to routing in Rails.
101+
To circumvent this default behaviour, one could use the `:strict` option. When
102+
this option is set to `true`, a `404 Not found` error is returned when no
103+
correct Accept header is supplied.
87104

88-
Serialization takes place automatically. For more detailed usage information, please visit the [Grape Wiki](http://github.com/intridea/grape/wiki).
105+
Serialization takes place automatically. For more detailed usage information,
106+
please visit the [Grape Wiki](http://github.com/intridea/grape/wiki).
89107

90108
## Helpers
91109

@@ -117,39 +135,6 @@ class API < Grape::API
117135
end
118136
````
119137

120-
121-
## Working with Entities
122-
123-
A common problem in designing Ruby APIs is that you probably don't want
124-
the exact structure of your data models exposed. ActiveRecord, for
125-
instance, will dump all of its attributes. While you can override
126-
`#as_json` to alter this behavior somewhat, what is really needed is an
127-
intermediary layer between the model and the API. This is where the
128-
`Grape::Entity` class comes in.
129-
130-
```ruby
131-
module Entities
132-
class User < Grape::Entity
133-
expose :first_name, :last_name
134-
expose :email, :if => {:authenticated => true}
135-
expose :name, :id => {:version => 'v1'} # deprecated
136-
end
137-
end
138-
139-
class API < Grape::API
140-
version 'v1', 'v2'
141-
142-
get '/users/:id' do
143-
present User.find(params[:id]),
144-
:with => Entities::User,
145-
:authenticated => env.key?('api.token')
146-
end
147-
end
148-
```
149-
150-
For more information about Entities, view the project's YARD
151-
documentation.
152-
153138
## Raising Errors
154139

155140
You can raise errors explicitly.
@@ -158,7 +143,8 @@ You can raise errors explicitly.
158143
error!("Access Denied", 401)
159144
```
160145

161-
You can also return JSON formatted objects explicitly by raising error! and passing a hash instead of a message.
146+
You can also return JSON formatted objects explicitly by raising error! and
147+
passing a hash instead of a message.
162148

163149
```ruby
164150
error!({ "error" => "unexpected error", "detail" => "missing widget" }, 500)
@@ -183,15 +169,17 @@ class Twitter::API < Grape::API
183169
end
184170
```
185171

186-
The error format can be specified using `error_format`. Available formats are `:json` and `:txt` (default).
172+
The error format can be specified using `error_format`. Available formats are
173+
`:json` and `:txt` (default).
187174

188175
```ruby
189176
class Twitter::API < Grape::API
190177
error_format :json
191178
end
192179
```
193180

194-
You can rescue all exceptions with a code block. The `rack_response` wrapper automatically sets the default error code and content-type.
181+
You can rescue all exceptions with a code block. The `rack_response` wrapper
182+
automatically sets the default error code and content-type.
195183

196184
```ruby
197185
class Twitter::API < Grape::API
@@ -201,7 +189,8 @@ class Twitter::API < Grape::API
201189
end
202190
```
203191

204-
You can also rescue specific exceptions with a code block and handle the Rack response at the lowest level.
192+
You can also rescue specific exceptions with a code block and handle the Rack
193+
response at the lowest level.
205194

206195
```ruby
207196
class Twitter::API < Grape::API
@@ -220,9 +209,22 @@ end
220209
end
221210
end
222211

212+
## Content-Types
213+
214+
By default, Grape supports _XML_, _JSON_, _Atom_, _RSS_, and _text_ content-types. Your API can declare additional types to support. Response format is determined by the request's extension or `Accept` header.
215+
216+
```ruby
217+
class Twitter::API < Grape::API
218+
content_type :xls, "application/vnd.ms-excel"
219+
end
220+
```
221+
223222
## Writing Tests
224223

225-
You can test a Grape API with RSpec. Tests make HTTP requests, therefore they must go into the `spec/request` group. You may want your API code to go into `app/api` - you can match that layout under `spec` by adding the following in `spec/spec_helper.rb`.
224+
You can test a Grape API with RSpec. Tests make HTTP requests, therefore they
225+
must go into the `spec/request` group. You may want your API code to go into
226+
`app/api` - you can match that layout under `spec` by adding the following in
227+
`spec/spec_helper.rb`.
226228

227229
```ruby
228230
RSpec.configure do |config|
@@ -250,7 +252,8 @@ end
250252

251253
## Describing and Inspecting an API
252254

253-
Grape lets you add a description to an API along with any other optional elements that can also be inspected at runtime.
255+
Grape lets you add a description to an API along with any other optional
256+
elements that can also be inspected at runtime.
254257
This can be useful for generating documentation.
255258

256259
```ruby
@@ -272,7 +275,11 @@ class TwitterAPI < Grape::API
272275
end
273276
```
274277

275-
Grape then exposes arrays of API versions and compiled routes. Each route contains a `route_prefix`, `route_version`, `route_namespace`, `route_method`, `route_path` and `route_params`. The description and the optional hash that follows the API path may contain any number of keys and its values are also accessible via dynamically-generated `route_[name]` functions.
278+
Grape then exposes arrays of API versions and compiled routes. Each route
279+
contains a `route_prefix`, `route_version`, `route_namespace`, `route_method`,
280+
`route_path` and `route_params`. The description and the optional hash that
281+
follows the API path may contain any number of keys and its values are also
282+
accessible via dynamically-generated `route_[name]` functions.
276283

277284
```ruby
278285
TwitterAPI::versions # yields [ 'v1', 'v2' ]
@@ -294,14 +301,50 @@ StringAPI::routes[0].route_params # yields an array [ "string", "token" ]
294301
StringAPI::routes[0].route_optional_params # yields an array [ "limit" ]
295302
```
296303

304+
## Anchoring
305+
306+
Grape by default anchors all request paths, which means that the request URL
307+
should match from start to end to match, otherwise a `404 Not Found` is
308+
returned.
309+
However, this is sometimes not what you want, because it is not always known up
310+
front what can be expected from the call.
311+
This is because Rack-mount by default anchors requests to match from the start
312+
to the end, or not at all. Rails solves this problem by using a `:anchor =>
313+
false` option in your routes.
314+
In Grape this option can be used as well when a method is defined.
315+
316+
For instance when you're API needs to get part of an URL, for instance:
317+
318+
```ruby
319+
class UrlAPI < Grape::API
320+
namespace :urls do
321+
get '/(*:url)', :anchor => false do
322+
some_data
323+
end
324+
end
325+
end
326+
```
327+
328+
This will match all paths starting with '/urls/'. There is one caveat though:
329+
the `params[:url]` parameter only holds the first part of the request url.
330+
Luckily this can be circumvented by using the described above syntax for path
331+
specification and using the `PATH_INFO` Rack environment variable, using
332+
`env["PATH_INFO"]`. This will hold everyting that comes after the '/urls/'
333+
part.
334+
297335
## Note on Patches/Pull Requests
298336

299-
* Fork the project.
300-
* Make your feature addition or bug fix.
301-
* Add tests for it. This is important so I don't break it in a future version unintentionally.
302-
* Commit, do not mess with Rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
303-
* Send me a pull request. Bonus points for topic branches.
337+
* Fork the project
338+
* Write tests for your new feature or a test that reproduces a bug
339+
* Implement your feature or make a bug fix
340+
* Do not mess with Rakefile, version or history
341+
* Commit, push and make a pull request. Bonus points for topical branches.
342+
343+
## License
344+
345+
MIT License. See LICENSE for details.
304346

305347
## Copyright
306348

307-
Copyright (c) 2010 Michael Bleigh and Intridea, Inc. See LICENSE for details.
349+
Copyright (c) 2010-2012 Michael Bleigh and Intridea, Inc.
350+

grape.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Gem::Specification.new do |s|
1010
s.homepage = "https://github.com/intridea/grape"
1111
s.summary = %q{A simple Ruby framework for building REST-like APIs.}
1212
s.description = %q{A Ruby framework for rapid API development with great conventions.}
13+
s.license = "MIT"
1314

1415
s.rubyforge_project = "grape"
1516

lib/grape/api.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ def call(env)
4848
end
4949

5050
def call!(env)
51-
logger.info "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
5251
instance.call(env)
5352
end
5453

@@ -96,7 +95,7 @@ def version(*args, &block)
9695
if args.any?
9796
options = args.pop if args.last.is_a? Hash
9897
options ||= {}
99-
options = {:using => :header}.merge!(options)
98+
options = {:using => :path}.merge!(options)
10099
@versions = versions | args
101100
nest(block) do
102101
set(:version, args)
@@ -123,6 +122,12 @@ def error_format(new_format = nil)
123122
new_format ? set(:error_format, new_format.to_sym) : settings[:error_format]
124123
end
125124

125+
# Specify additional content-types, e.g.:
126+
# content_type :xls, 'application/vnd.ms-excel'
127+
def content_type(key, val)
128+
settings.imbue(:content_types, key.to_sym => val)
129+
end
130+
126131
# Specify the default status code for errors.
127132
def default_error_status(new_status = nil)
128133
new_status ? set(:default_error_status, new_status) : settings[:default_error_status]
@@ -279,7 +284,7 @@ def before(&block)
279284
imbue(:befores, [block])
280285
end
281286

282-
def after(&block)
287+
def after(&block)
283288
imbue(:afters, [block])
284289
end
285290

@@ -288,6 +293,7 @@ def post(paths = ['/'], options = {}, &block); route('POST', paths, options, &bl
288293
def put(paths = ['/'], options = {}, &block); route('PUT', paths, options, &block) end
289294
def head(paths = ['/'], options = {}, &block); route('HEAD', paths, options, &block) end
290295
def delete(paths = ['/'], options = {}, &block); route('DELETE', paths, options, &block) end
296+
def options(paths = ['/'], options = {}, &block); route('OPTIONS', paths, options, &block) end
291297

292298
def namespace(space = nil, &block)
293299
if space || block_given?
@@ -334,11 +340,11 @@ def middleware
334340
def routes
335341
@routes ||= prepare_routes
336342
end
337-
343+
338344
def versions
339345
@versions ||= []
340346
end
341-
347+
342348
protected
343349

344350
def prepare_routes

0 commit comments

Comments
 (0)