grape 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- data/CHANGELOG.md +29 -5
- data/Gemfile +1 -0
- data/README.md +169 -80
- data/Rakefile +19 -3
- data/lib/grape.rb +6 -3
- data/lib/grape/api.rb +24 -3
- data/lib/grape/endpoint.rb +34 -20
- data/lib/grape/http/request.rb +28 -0
- data/lib/grape/middleware/base.rb +1 -1
- data/lib/grape/middleware/filter.rb +1 -1
- data/lib/grape/middleware/formatter.rb +33 -22
- data/lib/grape/middleware/versioner.rb +2 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
- data/lib/grape/middleware/versioner/header.rb +3 -1
- data/lib/grape/util/hash_stack.rb +11 -0
- data/lib/grape/validations.rb +47 -11
- data/lib/grape/validations/default.rb +24 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +171 -11
- data/spec/grape/endpoint_spec.rb +41 -1
- data/spec/grape/entity_spec.rb +38 -0
- data/spec/grape/middleware/formatter_spec.rb +48 -0
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
- data/spec/grape/middleware/versioner_spec.rb +4 -1
- data/spec/grape/validations/default_spec.rb +67 -0
- data/spec/grape/validations_spec.rb +9 -0
- data/spec/shared/versioning_examples.rb +13 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/support/versioned_helpers.rb +6 -0
- metadata +11 -4
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,34 @@
|
|
1
|
+
0.5.0 (6/14/2013)
|
2
|
+
=================
|
3
|
+
|
4
|
+
#### Features
|
5
|
+
|
6
|
+
* [#344](https://github.com/intridea/grape/pull/344): Added `parser :type, nil` which disables input parsing for a given content-type - [@dblock](https://github.com/dblock).
|
7
|
+
* [#381](https://github.com/intridea/grape/issues/381): Added `cascade false` option at API level to remove the `X-Cascade: true` header from the API response - [@dblock](https://github.com/dblock).
|
8
|
+
* [#392](https://github.com/intridea/grape/pull/392): Extracted headers and params from `Endpoint` to `Grape::Request` - [@niedhui](https://github.com/niedhui).
|
9
|
+
* [#376](https://github.com/intridea/grape/pull/376): Added `route_param`, syntax sugar for quick declaration of route parameters - [@mbleigh](https://github.com/mbleigh).
|
10
|
+
* [#390](https://github.com/intridea/grape/pull/390): Added default value for an `optional` parameter - [@oivoodoo](https://github.com/oivoodoo).
|
11
|
+
* [#403](https://github.com/intridea/grape/pull/403): Added support for versioning using the `Accept-Version` header - [@politician](https://github.com/politician).
|
12
|
+
* [#407](https://github.com/intridea/grape/issues/407): Specifying `default_format` will also set the default POST/PUT data parser to the given format - [@dblock](https://github.com/dblock).
|
13
|
+
* [#241](https://github.com/intridea/grape/issues/241): Present with multiple entities using an optional Symbol - [@niedhui](https://github.com/niedhui).
|
14
|
+
|
15
|
+
#### Fixes
|
16
|
+
|
17
|
+
* [#378](https://github.com/intridea/grape/pull/378): Fix: stop rescuing all exceptions during formatting - [@kbarrette](https://github.com/kbarrette).
|
18
|
+
* [#380](https://github.com/intridea/grape/pull/380): Fix: `Formatter#read_body_input` when transfer encoding is chunked - [@paulnicholon](https://github.com/paulnicholson).
|
19
|
+
* [#347](https://github.com/intridea/grape/issues/347): Fix: handling non-hash body params - [@paulnicholon](https://github.com/paulnicholson).
|
20
|
+
* [#394](https://github.com/intridea/grape/pull/394): Fix: path version no longer overwrites a `version` parameter - [@tmornini](https://github.com/tmornini).
|
21
|
+
* [#412](https://github.com/intridea/grape/issues/412): Fix: specifying `content_type` will also override the selection of the data formatter - [@dblock](https://github.com/dblock).
|
22
|
+
* [#383](https://github.com/intridea/grape/issues/383): Fix: Mounted APIs aren't inheriting settings (including `before` and `after` filters) - [@seanmoon](https://github.com/seanmoon).
|
23
|
+
* [#408](https://github.com/intridea/grape/pull/408): Fix: Goliath passes request header keys as symbols not strings - [@bobek](https://github.com/bobek).
|
24
|
+
* [#417](https://github.com/intridea/grape/issues/417): Fix: Rails 4 does not rewind input, causes POSTed data to be empty - [@dblock](https://github.com/dblock).
|
25
|
+
* [#423](https://github.com/intridea/grape/pull/423): Fix: `Grape::Endpoint#declared` now correctly handles nested params (ie. declared with `group`) - [@jbarreneche](https://github.com/jbarreneche).
|
26
|
+
* [#427](https://github.com/intridea/grape/issues/427): Fix: `declared(params)` breaks when `params` contains array - [@timhabermaas](https://github.com/timhabermaas)
|
27
|
+
|
1
28
|
0.4.1 (4/1/2013)
|
2
29
|
================
|
3
30
|
|
4
31
|
* [#375](https://github.com/intridea/grape/pull/375): Fix: throwing an `:error` inside a middleware doesn't respect the `format` settings - [@dblock](https://github.com/dblock).
|
5
|
-
* Your contribution here.
|
6
32
|
|
7
33
|
0.4.0 (3/17/2013)
|
8
34
|
=================
|
@@ -101,8 +127,7 @@
|
|
101
127
|
0.2.2
|
102
128
|
=====
|
103
129
|
|
104
|
-
Features
|
105
|
-
--------
|
130
|
+
#### Features
|
106
131
|
|
107
132
|
* [#201](https://github.com/intridea/grape/pull/201), [#236](https://github.com/intridea/grape/pull/236), [#221](https://github.com/intridea/grape/pull/221): Added coercion and validations support to `params` DSL - [@schmurfy](https://github.com/schmurfy), [@tim-vandecasteele](https://github.com/tim-vandecasteele), [@adamgotterer](https://github.com/adamgotterer).
|
108
133
|
* [#204](https://github.com/intridea/grape/pull/204): Added ability to declare shared `params` at `namespace` level - [@tim-vandecasteele](https://github.com/tim-vandecasteele).
|
@@ -110,8 +135,7 @@ Features
|
|
110
135
|
* [#240](https://github.com/intridea/grape/pull/240): Define API response format from a query string `format` parameter, if specified - [@neetiraj](https://github.com/neetiraj).
|
111
136
|
* Adds Endpoint#declared to easily filter out unexpected params. - [@mbleigh](https://github.com/mbleigh)
|
112
137
|
|
113
|
-
Fixes
|
114
|
-
-----
|
138
|
+
#### Fixes
|
115
139
|
|
116
140
|
* [#248](https://github.com/intridea/grape/pull/248): Fix: API `version` returns last version set - [@narkoz](https://github.com/narkoz).
|
117
141
|
* [#242](https://github.com/intridea/grape/issues/242): Fix: permanent redirect status should be `301`, was `304` - [@adamgotterer](https://github.com/adamgotterer).
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -10,13 +10,9 @@ content negotiation, versioning and much more.
|
|
10
10
|
|
11
11
|
[![Build Status](https://travis-ci.org/intridea/grape.png?branch=master)](http://travis-ci.org/intridea/grape) [![Code Climate](https://codeclimate.com/github/intridea/grape.png)](https://codeclimate.com/github/intridea/grape)
|
12
12
|
|
13
|
-
##
|
13
|
+
## Project Resources
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
## Project Tracking
|
18
|
-
|
19
|
-
* [Grape Google Group](http://groups.google.com/group/ruby-grape)
|
15
|
+
* Need help? [Grape Google Group](http://groups.google.com/group/ruby-grape)
|
20
16
|
* [Grape Wiki](https://github.com/intridea/grape/wiki)
|
21
17
|
|
22
18
|
## Installation
|
@@ -41,7 +37,7 @@ the context of recreating parts of the Twitter API.
|
|
41
37
|
module Twitter
|
42
38
|
class API < Grape::API
|
43
39
|
|
44
|
-
version 'v1', :
|
40
|
+
version 'v1', using: :header, vendor: 'twitter'
|
45
41
|
format :json
|
46
42
|
|
47
43
|
helpers do
|
@@ -69,40 +65,42 @@ module Twitter
|
|
69
65
|
|
70
66
|
desc "Return a status."
|
71
67
|
params do
|
72
|
-
requires :id, :
|
68
|
+
requires :id, type: Integer, desc: "Status id."
|
73
69
|
end
|
74
|
-
|
75
|
-
|
70
|
+
route_param :id do
|
71
|
+
get do
|
72
|
+
Status.find(params[:id])
|
73
|
+
end
|
76
74
|
end
|
77
75
|
|
78
76
|
desc "Create a status."
|
79
77
|
params do
|
80
|
-
requires :status, :
|
78
|
+
requires :status, type: String, desc: "Your status."
|
81
79
|
end
|
82
80
|
post do
|
83
81
|
authenticate!
|
84
82
|
Status.create!({
|
85
|
-
:
|
86
|
-
:
|
83
|
+
user: current_user,
|
84
|
+
text: params[:status]
|
87
85
|
})
|
88
86
|
end
|
89
87
|
|
90
88
|
desc "Update a status."
|
91
89
|
params do
|
92
|
-
requires :id, :
|
93
|
-
requires :status, :
|
90
|
+
requires :id, type: String, desc: "Status ID."
|
91
|
+
requires :status, type: String, desc: "Your status."
|
94
92
|
end
|
95
93
|
put ':id' do
|
96
94
|
authenticate!
|
97
95
|
current_user.statuses.find(params[:id]).update({
|
98
|
-
:
|
99
|
-
:
|
96
|
+
user: current_user,
|
97
|
+
text: params[:status]
|
100
98
|
})
|
101
99
|
end
|
102
100
|
|
103
101
|
desc "Delete a status."
|
104
102
|
params do
|
105
|
-
requires :id, :
|
103
|
+
requires :id, type: String, desc: "Status ID."
|
106
104
|
end
|
107
105
|
delete ':id' do
|
108
106
|
authenticate!
|
@@ -136,12 +134,39 @@ And would respond to the following routes:
|
|
136
134
|
|
137
135
|
Grape will also automatically respond to HEAD and OPTIONS for all GET, and just OPTIONS for all other routes.
|
138
136
|
|
137
|
+
### Alongside Sinatra (or other frameworks)
|
138
|
+
|
139
|
+
If you wish to mount Grape alongside another Rack framework such as Sinatra, you can do so easily using
|
140
|
+
`Rack::Cascade`:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
# Example config.ru
|
144
|
+
|
145
|
+
require 'sinatra'
|
146
|
+
require 'grape'
|
147
|
+
|
148
|
+
class API < Grape::API
|
149
|
+
get :hello do
|
150
|
+
{hello: "world"}
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
class Web < Sinatra::Base
|
155
|
+
get '/' do
|
156
|
+
"Hello world."
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
use Rack::Session::Cookie
|
161
|
+
run Rack::Cascade.new [API, Web]
|
162
|
+
```
|
163
|
+
|
139
164
|
### Rails
|
140
165
|
|
141
166
|
Place API files into `app/api` and modify `application.rb`.
|
142
167
|
|
143
168
|
```ruby
|
144
|
-
config.paths.add "app/api", :
|
169
|
+
config.paths.add "app/api", glob: "**/*.rb"
|
145
170
|
config.autoload_paths += Dir["#{Rails.root}/app/api/*"]
|
146
171
|
```
|
147
172
|
|
@@ -175,13 +200,13 @@ end
|
|
175
200
|
|
176
201
|
## Versioning
|
177
202
|
|
178
|
-
There are
|
179
|
-
`:header` and `:param`. The default strategy is `:path`.
|
203
|
+
There are four strategies in which clients can reach your API's endpoints: `:path`,
|
204
|
+
`:header`, `:accept_version_header` and `:param`. The default strategy is `:path`.
|
180
205
|
|
181
206
|
### Path
|
182
207
|
|
183
208
|
```ruby
|
184
|
-
version 'v1', :
|
209
|
+
version 'v1', using: :path
|
185
210
|
```
|
186
211
|
|
187
212
|
Using this versioning strategy, clients should pass the desired version in the URL.
|
@@ -191,7 +216,7 @@ Using this versioning strategy, clients should pass the desired version in the U
|
|
191
216
|
### Header
|
192
217
|
|
193
218
|
```ruby
|
194
|
-
version 'v1', :
|
219
|
+
version 'v1', using: :header, vendor: 'twitter'
|
195
220
|
```
|
196
221
|
|
197
222
|
Using this versioning strategy, clients should pass the desired version in the HTTP `Accept` head.
|
@@ -203,10 +228,25 @@ supplied. This behavior is similar to routing in Rails. To circumvent this defau
|
|
203
228
|
one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
|
204
229
|
is returned when no correct `Accept` header is supplied.
|
205
230
|
|
231
|
+
### Accept-Version Header
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
version 'v1', using: :accept_version_header
|
235
|
+
```
|
236
|
+
|
237
|
+
Using this versioning strategy, clients should pass the desired version in the HTTP `Accept-Version` header.
|
238
|
+
|
239
|
+
curl -H "Accept-Version=v1" http://localhost:9292/statuses/public_timeline
|
240
|
+
|
241
|
+
By default, the first matching version is used when no `Accept-Version` header is
|
242
|
+
supplied. This behavior is similar to routing in Rails. To circumvent this default behavior,
|
243
|
+
one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
|
244
|
+
is returned when no correct `Accept` header is supplied.
|
245
|
+
|
206
246
|
### Param
|
207
247
|
|
208
248
|
```ruby
|
209
|
-
version 'v1', :
|
249
|
+
version 'v1', using: :param
|
210
250
|
```
|
211
251
|
|
212
252
|
Using this versioning strategy, clients should pass the desired version as a request parameter,
|
@@ -217,7 +257,7 @@ either in the URL query string or in the request body.
|
|
217
257
|
The default name for the query parameter is 'apiver' but can be specified using the `:parameter` option.
|
218
258
|
|
219
259
|
```ruby
|
220
|
-
version 'v1', :
|
260
|
+
version 'v1', using: :param, parameter: "v"
|
221
261
|
```
|
222
262
|
|
223
263
|
curl -H http://localhost:9292/statuses/public_timeline?v=v1
|
@@ -258,7 +298,7 @@ The Grape endpoint:
|
|
258
298
|
|
259
299
|
```ruby
|
260
300
|
post '/statuses' do
|
261
|
-
Status.create!({ :
|
301
|
+
Status.create!({ text: params[:text] })
|
262
302
|
end
|
263
303
|
```
|
264
304
|
|
@@ -298,6 +338,14 @@ end
|
|
298
338
|
When a type is specified an implicit validation is done after the coercion to ensure
|
299
339
|
the output type is the one declared.
|
300
340
|
|
341
|
+
Optional parameters can have a default value.
|
342
|
+
|
343
|
+
```ruby
|
344
|
+
params do
|
345
|
+
optional :color, type: String, default: 'blue'
|
346
|
+
end
|
347
|
+
```
|
348
|
+
|
301
349
|
Parameters can be nested using `group`. In the above example, this means
|
302
350
|
`params[:media][:url]` is required along with `params[:id]`.
|
303
351
|
|
@@ -331,7 +379,7 @@ The `namespace` method has a number of aliases, including: `group`, `resource`,
|
|
331
379
|
class AlphaNumeric < Grape::Validations::Validator
|
332
380
|
def validate_param!(attr_name, params)
|
333
381
|
unless params[attr_name] =~ /^[[:alnum:]]+$/
|
334
|
-
throw :error, :
|
382
|
+
throw :error, status: 400, message: "#{attr_name}: must consist of alpha-numeric characters"
|
335
383
|
end
|
336
384
|
end
|
337
385
|
end
|
@@ -339,7 +387,7 @@ end
|
|
339
387
|
|
340
388
|
```ruby
|
341
389
|
params do
|
342
|
-
requires :text, :
|
390
|
+
requires :text, alpha_numeric: true
|
343
391
|
end
|
344
392
|
```
|
345
393
|
|
@@ -349,7 +397,7 @@ You can also create custom classes that take parameters.
|
|
349
397
|
class Length < Grape::Validations::SingleOptionValidator
|
350
398
|
def validate_param!(attr_name, params)
|
351
399
|
unless params[attr_name].length <= @option
|
352
|
-
throw :error, :
|
400
|
+
throw :error, status: 400, message: "#{attr_name}: must be at the most #{@option} characters long"
|
353
401
|
end
|
354
402
|
end
|
355
403
|
end
|
@@ -357,7 +405,7 @@ end
|
|
357
405
|
|
358
406
|
```ruby
|
359
407
|
params do
|
360
|
-
requires :text, :
|
408
|
+
requires :text, length: 140
|
361
409
|
end
|
362
410
|
```
|
363
411
|
|
@@ -405,11 +453,11 @@ Optionally, you can define requirements for your named route parameters using re
|
|
405
453
|
expressions on namespace or endpoint. The route will match only if all requirements are met.
|
406
454
|
|
407
455
|
```ruby
|
408
|
-
get ':id', :
|
456
|
+
get ':id', requirements: { id: /[0-9]*/ } do
|
409
457
|
Status.find(params[:id])
|
410
458
|
end
|
411
459
|
|
412
|
-
namespace :outer, :
|
460
|
+
namespace :outer, requirements: { id: /[0-9]*/ } do
|
413
461
|
get :id do
|
414
462
|
end
|
415
463
|
|
@@ -458,11 +506,11 @@ class API < Grape::API
|
|
458
506
|
get 'status_count' do
|
459
507
|
cookies[:status_count] ||= 0
|
460
508
|
cookies[:status_count] += 1
|
461
|
-
{ :
|
509
|
+
{ status_count: cookies[:status_count] }
|
462
510
|
end
|
463
511
|
|
464
512
|
delete 'status_count' do
|
465
|
-
{ :
|
513
|
+
{ status_count: cookies.delete(:status_count) }
|
466
514
|
end
|
467
515
|
|
468
516
|
end
|
@@ -472,10 +520,10 @@ Use a hash-based syntax to set more than one value.
|
|
472
520
|
|
473
521
|
```ruby
|
474
522
|
cookies[:status_count] = {
|
475
|
-
:
|
476
|
-
:
|
477
|
-
:
|
478
|
-
:
|
523
|
+
value: 0,
|
524
|
+
expires: Time.tomorrow,
|
525
|
+
domain: '.twitter.com',
|
526
|
+
path: '/'
|
479
527
|
}
|
480
528
|
|
481
529
|
cookies[:status_count][:value] +=1
|
@@ -490,7 +538,7 @@ cookies.delete :status_count
|
|
490
538
|
Specify an optional path.
|
491
539
|
|
492
540
|
```ruby
|
493
|
-
cookies.delete :status_count, :
|
541
|
+
cookies.delete :status_count, path: '/'
|
494
542
|
```
|
495
543
|
|
496
544
|
## Redirecting
|
@@ -502,7 +550,7 @@ redirect "/statuses"
|
|
502
550
|
```
|
503
551
|
|
504
552
|
```ruby
|
505
|
-
redirect "/statuses", :
|
553
|
+
redirect "/statuses", permanent: true
|
506
554
|
```
|
507
555
|
|
508
556
|
## Allowed Methods
|
@@ -531,15 +579,15 @@ include an "Allow" header listing the supported methods.
|
|
531
579
|
class API < Grape::API
|
532
580
|
|
533
581
|
get '/rt_count' do
|
534
|
-
{ :
|
582
|
+
{ rt_count: current_user.rt_count }
|
535
583
|
end
|
536
584
|
|
537
585
|
params do
|
538
|
-
requires :value, :
|
586
|
+
requires :value, type: Integer, desc: 'Value to add to the rt count.'
|
539
587
|
end
|
540
588
|
put '/rt_count' do
|
541
589
|
current_user.rt_count += params[:value].to_i
|
542
|
-
{ :
|
590
|
+
{ rt_count: current_user.rt_count }
|
543
591
|
end
|
544
592
|
|
545
593
|
end
|
@@ -634,7 +682,7 @@ automatically sets the default error code and content-type.
|
|
634
682
|
```ruby
|
635
683
|
class Twitter::API < Grape::API
|
636
684
|
rescue_from :all do |e|
|
637
|
-
rack_response({ :
|
685
|
+
rack_response({ message: "rescued from #{e.class.name}" })
|
638
686
|
end
|
639
687
|
end
|
640
688
|
```
|
@@ -665,19 +713,23 @@ end
|
|
665
713
|
|
666
714
|
#### Rails 3.x
|
667
715
|
|
668
|
-
When mounted inside Rails 3.x, errors like "404 Not Found" or
|
669
|
-
handled and rendered by Rails handlers. For instance,
|
670
|
-
raises a 404, which inside rails will ultimately
|
671
|
-
which most likely will get rendered
|
716
|
+
When mounted inside containers, such as Rails 3.x, errors like "404 Not Found" or
|
717
|
+
"406 Not Acceptable" will likely be handled and rendered by Rails handlers. For instance,
|
718
|
+
accessing a nonexistent route "/api/foo" raises a 404, which inside rails will ultimately
|
719
|
+
be translated to an `ActionController::RoutingError`, which most likely will get rendered
|
720
|
+
to a HTML error page.
|
672
721
|
|
673
|
-
Most APIs will enjoy
|
674
|
-
|
722
|
+
Most APIs will enjoy preventing downstream handlers from handling errors. You may set the
|
723
|
+
`:cascade` option to `false` for the entire API or separately on specific `version` definitions,
|
724
|
+
which will remove the `X-Cascade: true` header from API responses.
|
675
725
|
|
676
726
|
```ruby
|
677
|
-
|
727
|
+
cascade false
|
678
728
|
```
|
679
729
|
|
680
|
-
|
730
|
+
```ruby
|
731
|
+
version 'v1', using: :header, vendor: 'twitter', cascade: false
|
732
|
+
```
|
681
733
|
|
682
734
|
## Logging
|
683
735
|
|
@@ -742,6 +794,16 @@ class Twitter::API < Grape::API
|
|
742
794
|
end
|
743
795
|
```
|
744
796
|
|
797
|
+
When the content-type is omitted, Grape will return a 406 error code unless `default_format` is specified.
|
798
|
+
The following API will try to parse any data without a content-type using a JSON parser.
|
799
|
+
|
800
|
+
```ruby
|
801
|
+
class Twitter::API < Grape::API
|
802
|
+
format :json
|
803
|
+
default_format :json
|
804
|
+
end
|
805
|
+
```
|
806
|
+
|
745
807
|
If you combine `format` with `rescue_from :all`, errors will be rendered using the same format.
|
746
808
|
If you do not want this behavior, set the default error formatter with `default_error_formatter`.
|
747
809
|
|
@@ -822,23 +884,21 @@ end
|
|
822
884
|
### CORS
|
823
885
|
|
824
886
|
Grape supports CORS via [Rack::CORS](https://github.com/cyu/rack-cors), part of the
|
825
|
-
[rack-cors](https://github.com/cyu/rack-cors) gem. Add `rack-cors` to your `Gemfile
|
887
|
+
[rack-cors](https://github.com/cyu/rack-cors) gem. Add `rack-cors` to your `Gemfile`,
|
888
|
+
then use the middleware in your config.ru file.
|
826
889
|
|
827
890
|
```ruby
|
828
891
|
require 'rack/cors'
|
829
892
|
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
resource '*', :headers => :any, :methods => :get
|
835
|
-
end
|
836
|
-
end
|
837
|
-
format :json
|
838
|
-
get '/' do
|
839
|
-
'Hello World'
|
893
|
+
use Rack::Cors do
|
894
|
+
allow do
|
895
|
+
origins '*'
|
896
|
+
resource '*', headers: :any, methods: :get
|
840
897
|
end
|
841
898
|
end
|
899
|
+
|
900
|
+
run Twitter::API
|
901
|
+
|
842
902
|
```
|
843
903
|
|
844
904
|
## Content-type
|
@@ -871,7 +931,7 @@ to `:value`. The parameter will be available via `params[:value]` inside the API
|
|
871
931
|
```ruby
|
872
932
|
module CustomParser
|
873
933
|
def self.call(object, env)
|
874
|
-
{ :
|
934
|
+
{ value: object.to_s }
|
875
935
|
end
|
876
936
|
end
|
877
937
|
```
|
@@ -892,6 +952,8 @@ You can invoke the above API as follows.
|
|
892
952
|
curl -X PUT -d 'data' 'http://localhost:9292/value' -H Content-Type:text/custom -v
|
893
953
|
```
|
894
954
|
|
955
|
+
You can disable parsing for a content-type with `nil`. For example, `parser :json, nil` will disable JSON parsing altogether. The request data is then available as-is in `env['api.request.body']`.
|
956
|
+
|
895
957
|
## RESTful Model Representations
|
896
958
|
|
897
959
|
Grape supports a range of ways to present your data with some help from a generic `present` method,
|
@@ -900,8 +962,8 @@ hash may include `:with`, which defines the entity to expose.
|
|
900
962
|
|
901
963
|
### Grape Entities
|
902
964
|
|
903
|
-
Add the [grape-entity](https://github.com/
|
904
|
-
Please refer to the [grape-entity documentation](https://github.com/
|
965
|
+
Add the [grape-entity](https://github.com/intridea/grape-entity) gem to your Gemfile.
|
966
|
+
Please refer to the [grape-entity documentation](https://github.com/intridea/grape-entity/blob/master/README.markdown)
|
905
967
|
for more details.
|
906
968
|
|
907
969
|
The following example exposes statuses.
|
@@ -912,11 +974,11 @@ module API
|
|
912
974
|
module Entities
|
913
975
|
class Status < Grape::Entity
|
914
976
|
expose :user_name
|
915
|
-
expose :text, :
|
916
|
-
expose :ip, :
|
917
|
-
expose :user_type, user_id, :
|
977
|
+
expose :text, documentation: { type: "string", desc: "Status update text." }
|
978
|
+
expose :ip, if: { type: :full }
|
979
|
+
expose :user_type, user_id, if: lambda{ |status, options| status.user.public? }
|
918
980
|
expose :digest { |status, options| Digest::MD5.hexdigest(status.txt) }
|
919
|
-
expose :replies, :
|
981
|
+
expose :replies, using: API::Status, as: :replies
|
920
982
|
end
|
921
983
|
end
|
922
984
|
|
@@ -924,17 +986,38 @@ module API
|
|
924
986
|
version 'v1'
|
925
987
|
|
926
988
|
desc 'Statuses index', {
|
927
|
-
:
|
989
|
+
object_fields: API::Entities::Status.documentation
|
928
990
|
}
|
929
991
|
get '/statuses' do
|
930
992
|
statuses = Status.all
|
931
993
|
type = current_user.admin? ? :full : :default
|
932
|
-
present statuses, with: API::Entities::Status, :
|
994
|
+
present statuses, with: API::Entities::Status, type: type
|
933
995
|
end
|
934
996
|
end
|
935
997
|
end
|
936
998
|
```
|
937
999
|
|
1000
|
+
You can present with multiple entities using an optional Symbol argument.
|
1001
|
+
|
1002
|
+
```ruby
|
1003
|
+
get '/statuses' do
|
1004
|
+
statuses = Status.all.page(1).per(20)
|
1005
|
+
present :total_page, 10
|
1006
|
+
present :per_page, 20
|
1007
|
+
present :statuses, statuses, with: API::Entities::Status
|
1008
|
+
end
|
1009
|
+
```
|
1010
|
+
|
1011
|
+
The response will be
|
1012
|
+
|
1013
|
+
```
|
1014
|
+
{
|
1015
|
+
total_page: 10,
|
1016
|
+
per_page: 20,
|
1017
|
+
statuses: []
|
1018
|
+
}
|
1019
|
+
```
|
1020
|
+
|
938
1021
|
In addition to separately organizing entities, it may be useful to put them as namespaced
|
939
1022
|
classes underneath the model they represent.
|
940
1023
|
|
@@ -982,7 +1065,7 @@ end
|
|
982
1065
|
```
|
983
1066
|
|
984
1067
|
```ruby
|
985
|
-
http_digest({ :
|
1068
|
+
http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
|
986
1069
|
# lookup the user's password here
|
987
1070
|
{ 'user1' => 'password1' }[username]
|
988
1071
|
end
|
@@ -1017,7 +1100,7 @@ call with `route`.
|
|
1017
1100
|
class MyAPI < Grape::API
|
1018
1101
|
desc "Returns a description of a parameter."
|
1019
1102
|
params do
|
1020
|
-
requires :id, :
|
1103
|
+
requires :id, type: Integer, desc: "Identity."
|
1021
1104
|
end
|
1022
1105
|
get "params/:id" do
|
1023
1106
|
route.route_params[params[:id]] # yields the parameter description
|
@@ -1057,7 +1140,7 @@ should match from start to end to match, otherwise a `404 Not Found` is
|
|
1057
1140
|
returned. However, this is sometimes not what you want, because it is not always
|
1058
1141
|
known upfront what can be expected from the call. This is because Rack-mount by
|
1059
1142
|
default anchors requests to match from the start to the end, or not at all.
|
1060
|
-
Rails solves this problem by using a
|
1143
|
+
Rails solves this problem by using a `anchor: false` option in your routes.
|
1061
1144
|
In Grape this option can be used as well when a method is defined.
|
1062
1145
|
|
1063
1146
|
For instance when you're API needs to get part of an URL, for instance:
|
@@ -1065,7 +1148,7 @@ For instance when you're API needs to get part of an URL, for instance:
|
|
1065
1148
|
```ruby
|
1066
1149
|
class TwitterAPI < Grape::API
|
1067
1150
|
namespace :statuses do
|
1068
|
-
get '/(*:status)', :
|
1151
|
+
get '/(*:status)', anchor: false do
|
1069
1152
|
|
1070
1153
|
end
|
1071
1154
|
end
|
@@ -1144,8 +1227,8 @@ In Rails, HTTP request tests would go into the `spec/requests` group. You may wa
|
|
1144
1227
|
|
1145
1228
|
```ruby
|
1146
1229
|
RSpec.configure do |config|
|
1147
|
-
config.include RSpec::Rails::RequestExampleGroup, :
|
1148
|
-
:
|
1230
|
+
config.include RSpec::Rails::RequestExampleGroup, type: :request, example_group: {
|
1231
|
+
file_path: /spec\/api/
|
1149
1232
|
}
|
1150
1233
|
end
|
1151
1234
|
```
|
@@ -1158,7 +1241,7 @@ Add API paths to `config/application.rb`.
|
|
1158
1241
|
|
1159
1242
|
```ruby
|
1160
1243
|
# Auto-load API and its subdirectories
|
1161
|
-
config.paths.add "app/api", :
|
1244
|
+
config.paths.add "app/api", glob: "**/*.rb"
|
1162
1245
|
config.autoload_paths += Dir["#{Rails.root}/app/api/*"]
|
1163
1246
|
```
|
1164
1247
|
|
@@ -1166,6 +1249,9 @@ Create `config/initializers/reload_api.rb`.
|
|
1166
1249
|
|
1167
1250
|
```ruby
|
1168
1251
|
if Rails.env.development?
|
1252
|
+
|
1253
|
+
ActiveSupport::Dependencies.explicitly_unloadable_constants << "Twitter::API"
|
1254
|
+
|
1169
1255
|
api_files = Dir["#{Rails.root}/app/api/**/*.rb"]
|
1170
1256
|
api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
|
1171
1257
|
Rails.application.reload_routes!
|
@@ -1173,6 +1259,7 @@ if Rails.env.development?
|
|
1173
1259
|
ActionDispatch::Callbacks.to_prepare do
|
1174
1260
|
api_reloader.execute_if_updated
|
1175
1261
|
end
|
1262
|
+
|
1176
1263
|
end
|
1177
1264
|
```
|
1178
1265
|
|
@@ -1181,7 +1268,9 @@ See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-
|
|
1181
1268
|
|
1182
1269
|
## Performance Monitoring
|
1183
1270
|
|
1184
|
-
Grape integrates with NewRelic via the
|
1271
|
+
Grape integrates with NewRelic via the
|
1272
|
+
[newrelic-grape](https://github.com/flyerhzm/newrelic-grape) gem, and
|
1273
|
+
with Librato Metrics with the [grape-librato](https://github.com/seanmoon/grape-librato) gem.
|
1185
1274
|
|
1186
1275
|
## Contributing to Grape
|
1187
1276
|
|