grape 0.12.0 → 0.14.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.
- checksums.yaml +4 -4
- data/Appraisals +9 -4
- data/CHANGELOG.md +265 -215
- data/CONTRIBUTING.md +4 -4
- data/Gemfile +0 -1
- data/Gemfile.lock +166 -0
- data/README.md +426 -161
- data/RELEASING.md +14 -6
- data/Rakefile +30 -33
- data/UPGRADING.md +54 -23
- data/benchmark/simple.rb +27 -0
- data/gemfiles/rack_1.5.2.gemfile +13 -0
- data/gemfiles/rails_3.gemfile +2 -2
- data/gemfiles/rails_4.gemfile +1 -2
- data/grape.gemspec +6 -7
- data/lib/grape/api.rb +24 -4
- data/lib/grape/dsl/callbacks.rb +20 -0
- data/lib/grape/dsl/configuration.rb +59 -2
- data/lib/grape/dsl/helpers.rb +8 -3
- data/lib/grape/dsl/inside_route.rb +100 -45
- data/lib/grape/dsl/parameters.rb +96 -7
- data/lib/grape/dsl/request_response.rb +1 -1
- data/lib/grape/dsl/routing.rb +17 -4
- data/lib/grape/dsl/settings.rb +36 -1
- data/lib/grape/dsl/validations.rb +7 -5
- data/lib/grape/endpoint.rb +102 -57
- data/lib/grape/error_formatter/base.rb +6 -6
- data/lib/grape/exceptions/base.rb +5 -5
- data/lib/grape/exceptions/invalid_version_header.rb +10 -0
- data/lib/grape/exceptions/unknown_parameter.rb +10 -0
- data/lib/grape/exceptions/validation_errors.rb +4 -3
- data/lib/grape/formatter/serializable_hash.rb +3 -2
- data/lib/grape/http/headers.rb +0 -1
- data/lib/grape/locale/en.yml +5 -1
- data/lib/grape/middleware/auth/base.rb +2 -2
- data/lib/grape/middleware/auth/dsl.rb +1 -1
- data/lib/grape/middleware/auth/strategies.rb +1 -1
- data/lib/grape/middleware/base.rb +8 -4
- data/lib/grape/middleware/error.rb +3 -2
- data/lib/grape/middleware/filter.rb +1 -1
- data/lib/grape/middleware/formatter.rb +64 -45
- data/lib/grape/middleware/globals.rb +3 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +5 -7
- data/lib/grape/middleware/versioner/header.rb +113 -50
- data/lib/grape/middleware/versioner/param.rb +5 -8
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +20 -0
- data/lib/grape/middleware/versioner/path.rb +3 -6
- data/lib/grape/namespace.rb +13 -2
- data/lib/grape/path.rb +4 -3
- data/lib/grape/request.rb +40 -0
- data/lib/grape/route.rb +5 -0
- data/lib/grape/util/content_types.rb +9 -9
- data/lib/grape/util/env.rb +22 -0
- data/lib/grape/util/file_response.rb +21 -0
- data/lib/grape/util/inheritable_setting.rb +23 -2
- data/lib/grape/util/inheritable_values.rb +1 -1
- data/lib/grape/util/stackable_values.rb +5 -2
- data/lib/grape/util/strict_hash_configuration.rb +2 -1
- data/lib/grape/validations/attributes_iterator.rb +8 -3
- data/lib/grape/validations/params_scope.rb +164 -22
- data/lib/grape/validations/types/build_coercer.rb +53 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +183 -0
- data/lib/grape/validations/types/file.rb +28 -0
- data/lib/grape/validations/types/json.rb +65 -0
- data/lib/grape/validations/types/multiple_type_coercer.rb +76 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +59 -0
- data/lib/grape/validations/types/virtus_collection_patch.rb +16 -0
- data/lib/grape/validations/types.rb +144 -0
- data/lib/grape/validations/validators/all_or_none.rb +1 -1
- data/lib/grape/validations/validators/allow_blank.rb +3 -3
- data/lib/grape/validations/validators/base.rb +7 -0
- data/lib/grape/validations/validators/coerce.rb +32 -34
- data/lib/grape/validations/validators/presence.rb +2 -3
- data/lib/grape/validations/validators/regexp.rb +2 -4
- data/lib/grape/validations/validators/values.rb +3 -3
- data/lib/grape/validations.rb +5 -0
- data/lib/grape/version.rb +2 -1
- data/lib/grape.rb +15 -12
- data/pkg/grape-0.13.0.gem +0 -0
- data/spec/grape/api/custom_validations_spec.rb +5 -4
- data/spec/grape/api/deeply_included_options_spec.rb +7 -7
- data/spec/grape/api/nested_helpers_spec.rb +4 -2
- data/spec/grape/api/shared_helpers_spec.rb +8 -8
- data/spec/grape/api_spec.rb +151 -54
- data/spec/grape/dsl/configuration_spec.rb +13 -0
- data/spec/grape/dsl/helpers_spec.rb +16 -2
- data/spec/grape/dsl/inside_route_spec.rb +40 -4
- data/spec/grape/dsl/parameters_spec.rb +0 -6
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/dsl/validations_spec.rb +18 -0
- data/spec/grape/endpoint_spec.rb +130 -6
- data/spec/grape/entity_spec.rb +10 -8
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +1 -15
- data/spec/grape/exceptions/validation_errors_spec.rb +28 -0
- data/spec/grape/integration/rack_spec.rb +3 -2
- data/spec/grape/middleware/base_spec.rb +40 -16
- data/spec/grape/middleware/error_spec.rb +16 -15
- data/spec/grape/middleware/exception_spec.rb +45 -43
- data/spec/grape/middleware/formatter_spec.rb +34 -5
- data/spec/grape/middleware/versioner/header_spec.rb +79 -47
- data/spec/grape/path_spec.rb +10 -10
- data/spec/grape/presenters/presenter_spec.rb +2 -2
- data/spec/grape/request_spec.rb +100 -0
- data/spec/grape/util/inheritable_values_spec.rb +14 -0
- data/spec/grape/util/stackable_values_spec.rb +10 -0
- data/spec/grape/validations/params_scope_spec.rb +86 -0
- data/spec/grape/validations/types_spec.rb +95 -0
- data/spec/grape/validations/validators/coerce_spec.rb +364 -10
- data/spec/grape/validations/validators/values_spec.rb +27 -15
- data/spec/grape/validations_spec.rb +53 -24
- data/spec/shared/versioning_examples.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/support/versioned_helpers.rb +2 -2
- metadata +55 -14
- data/.gitignore +0 -46
- data/.rspec +0 -2
- data/.rubocop.yml +0 -7
- data/.rubocop_todo.yml +0 -84
- data/.travis.yml +0 -20
- data/.yardopts +0 -2
- data/lib/backports/active_support/deep_dup.rb +0 -49
- data/lib/backports/active_support/duplicable.rb +0 -88
- data/lib/grape/http/request.rb +0 -27
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|

|
2
2
|
|
3
|
-
[](http://badge.fury.io/rb/grape)
|
4
|
+
[](https://travis-ci.org/ruby-grape/grape)
|
5
|
+
[](https://gemnasium.com/ruby-grape/grape)
|
6
|
+
[](https://codeclimate.com/github/ruby-grape/grape)
|
7
|
+
[](http://inch-ci.org/github/ruby-grape/grape)
|
8
8
|
|
9
9
|
## Table of Contents
|
10
10
|
|
@@ -29,6 +29,13 @@
|
|
29
29
|
- [Declared](#declared)
|
30
30
|
- [Include Missing](#include-missing)
|
31
31
|
- [Parameter Validation and Coercion](#parameter-validation-and-coercion)
|
32
|
+
- [Supported Parameter Types](#supported-parameter-types)
|
33
|
+
- [Custom Types and Coercions](#custom-types-and-coercions)
|
34
|
+
- [Multipart File Parameters](#multipart-file-parameters)
|
35
|
+
- [First-Class `JSON` Types](#first-class-json-types)
|
36
|
+
- [Multiple Allowed Types](#multiple-allowed-types)
|
37
|
+
- [Validation of Nested Parameters](#validation-of-nested-parameters)
|
38
|
+
- [Dependent Parameters](#dependent-parameters)
|
32
39
|
- [Built-in Validators](#built-in-validators)
|
33
40
|
- [Namespace Validation and Coercion](#namespace-validation-and-coercion)
|
34
41
|
- [Custom Validators](#custom-validators)
|
@@ -37,6 +44,7 @@
|
|
37
44
|
- [Headers](#headers)
|
38
45
|
- [Routes](#routes)
|
39
46
|
- [Helpers](#helpers)
|
47
|
+
- [Path Helpers](#path-helpers)
|
40
48
|
- [Parameter Documentation](#parameter-documentation)
|
41
49
|
- [Cookies](#cookies)
|
42
50
|
- [HTTP Status Code](#http-status-code)
|
@@ -55,7 +63,7 @@
|
|
55
63
|
- [API Data Formats](#api-data-formats)
|
56
64
|
- [RESTful Model Representations](#restful-model-representations)
|
57
65
|
- [Grape Entities](#grape-entities)
|
58
|
-
- [Hypermedia](#hypermedia)
|
66
|
+
- [Hypermedia and Roar](#hypermedia-and-roar)
|
59
67
|
- [Rabl](#rabl)
|
60
68
|
- [Active Model Serializers](#active-model-serializers)
|
61
69
|
- [Sending Raw or No Data](#sending-raw-or-no-data)
|
@@ -66,7 +74,7 @@
|
|
66
74
|
- [Anchoring](#anchoring)
|
67
75
|
- [Using Custom Middleware](#using-custom-middleware)
|
68
76
|
- [Rails Middleware](#rails-middleware)
|
69
|
-
|
77
|
+
- [Remote IP](#remote-ip)
|
70
78
|
- [Writing Tests](#writing-tests)
|
71
79
|
- [Writing Tests with Rack](#writing-tests-with-rack)
|
72
80
|
- [Writing Tests with Rails](#writing-tests-with-rails)
|
@@ -75,8 +83,9 @@
|
|
75
83
|
- [Reloading in Rack Applications](#reloading-in-rack-applications)
|
76
84
|
- [Reloading in Rails Applications](#reloading-in-rails-applications)
|
77
85
|
- [Performance Monitoring](#performance-monitoring)
|
86
|
+
- [Active Support Instrumentation](#active-support-instrumentation)
|
87
|
+
- [Monitoring Products](#monitoring-products)
|
78
88
|
- [Contributing to Grape](#contributing-to-grape)
|
79
|
-
- [Hacking on Grape](#hacking-on-grape)
|
80
89
|
- [License](#license)
|
81
90
|
- [Copyright](#copyright)
|
82
91
|
|
@@ -90,13 +99,13 @@ content negotiation, versioning and much more.
|
|
90
99
|
|
91
100
|
## Stable Release
|
92
101
|
|
93
|
-
You're reading the documentation for the stable release of
|
102
|
+
You're reading the documentation for the stable release of Grae [0.14.0](https://github.com/ruby-grape/grape/blob/v0.14.0/README.md).
|
94
103
|
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
95
104
|
|
96
105
|
## Project Resources
|
97
106
|
|
107
|
+
* [Grape Website](http://www.ruby-grape.org)
|
98
108
|
* Need help? [Grape Google Group](http://groups.google.com/group/ruby-grape)
|
99
|
-
* [Grape Wiki](https://github.com/intridea/grape/wiki)
|
100
109
|
|
101
110
|
## Installation
|
102
111
|
|
@@ -134,20 +143,20 @@ module Twitter
|
|
134
143
|
end
|
135
144
|
|
136
145
|
resource :statuses do
|
137
|
-
desc
|
146
|
+
desc 'Return a public timeline.'
|
138
147
|
get :public_timeline do
|
139
148
|
Status.limit(20)
|
140
149
|
end
|
141
150
|
|
142
|
-
desc
|
151
|
+
desc 'Return a personal timeline.'
|
143
152
|
get :home_timeline do
|
144
153
|
authenticate!
|
145
154
|
current_user.statuses.limit(20)
|
146
155
|
end
|
147
156
|
|
148
|
-
desc
|
157
|
+
desc 'Return a status.'
|
149
158
|
params do
|
150
|
-
requires :id, type: Integer, desc:
|
159
|
+
requires :id, type: Integer, desc: 'Status id.'
|
151
160
|
end
|
152
161
|
route_param :id do
|
153
162
|
get do
|
@@ -155,9 +164,9 @@ module Twitter
|
|
155
164
|
end
|
156
165
|
end
|
157
166
|
|
158
|
-
desc
|
167
|
+
desc 'Create a status.'
|
159
168
|
params do
|
160
|
-
requires :status, type: String, desc:
|
169
|
+
requires :status, type: String, desc: 'Your status.'
|
161
170
|
end
|
162
171
|
post do
|
163
172
|
authenticate!
|
@@ -167,10 +176,10 @@ module Twitter
|
|
167
176
|
})
|
168
177
|
end
|
169
178
|
|
170
|
-
desc
|
179
|
+
desc 'Update a status.'
|
171
180
|
params do
|
172
|
-
requires :id, type: String, desc:
|
173
|
-
requires :status, type: String, desc:
|
181
|
+
requires :id, type: String, desc: 'Status ID.'
|
182
|
+
requires :status, type: String, desc: 'Your status.'
|
174
183
|
end
|
175
184
|
put ':id' do
|
176
185
|
authenticate!
|
@@ -180,9 +189,9 @@ module Twitter
|
|
180
189
|
})
|
181
190
|
end
|
182
191
|
|
183
|
-
desc
|
192
|
+
desc 'Delete a status.'
|
184
193
|
params do
|
185
|
-
requires :id, type: String, desc:
|
194
|
+
requires :id, type: String, desc: 'Status ID.'
|
186
195
|
end
|
187
196
|
delete ':id' do
|
188
197
|
authenticate!
|
@@ -242,13 +251,13 @@ require 'grape'
|
|
242
251
|
|
243
252
|
class API < Grape::API
|
244
253
|
get :hello do
|
245
|
-
{ hello:
|
254
|
+
{ hello: 'world' }
|
246
255
|
end
|
247
256
|
end
|
248
257
|
|
249
258
|
class Web < Sinatra::Base
|
250
259
|
get '/' do
|
251
|
-
|
260
|
+
'Hello world.'
|
252
261
|
end
|
253
262
|
end
|
254
263
|
|
@@ -277,7 +286,7 @@ Additionally, if the version of your Rails is 4.0+ and the application uses the
|
|
277
286
|
|
278
287
|
```ruby
|
279
288
|
# Gemfile
|
280
|
-
gem
|
289
|
+
gem 'hashie-forbidden_attributes'
|
281
290
|
```
|
282
291
|
|
283
292
|
See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
|
@@ -323,6 +332,14 @@ Using this versioning strategy, clients should pass the desired version in the U
|
|
323
332
|
version 'v1', using: :header, vendor: 'twitter'
|
324
333
|
```
|
325
334
|
|
335
|
+
Currently, Grape only supports versioned media types in the following format:
|
336
|
+
|
337
|
+
```
|
338
|
+
vnd.vendor-and-or-resource-v1234+format
|
339
|
+
```
|
340
|
+
|
341
|
+
Basically all tokens between the final `-` and the `+` will be interpreted as the version.
|
342
|
+
|
326
343
|
Using this versioning strategy, clients should pass the desired version in the HTTP `Accept` head.
|
327
344
|
|
328
345
|
curl -H Accept:application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
|
@@ -336,29 +353,6 @@ When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is ret
|
|
336
353
|
option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route
|
337
354
|
matches.
|
338
355
|
|
339
|
-
### HTTP Status Code
|
340
|
-
|
341
|
-
By default Grape returns a 200 status code for `GET`-Requests and 201 for `POST`-Requests.
|
342
|
-
You can use `status` to query and set the actual HTTP Status Code
|
343
|
-
|
344
|
-
```ruby
|
345
|
-
post do
|
346
|
-
status 202
|
347
|
-
|
348
|
-
if status == 200
|
349
|
-
# do some thing
|
350
|
-
end
|
351
|
-
end
|
352
|
-
```
|
353
|
-
|
354
|
-
You can also use one of status codes symbols that are provided by [Rack utils](http://www.rubydoc.info/github/rack/rack/Rack/Utils#HTTP_STATUS_CODES-constant)
|
355
|
-
|
356
|
-
```ruby
|
357
|
-
post do
|
358
|
-
status :no_content
|
359
|
-
end
|
360
|
-
```
|
361
|
-
|
362
356
|
### Accept-Version Header
|
363
357
|
|
364
358
|
```ruby
|
@@ -388,7 +382,7 @@ either in the URL query string or in the request body.
|
|
388
382
|
The default name for the query parameter is 'apiver' but can be specified using the `:parameter` option.
|
389
383
|
|
390
384
|
```ruby
|
391
|
-
version 'v1', using: :param, parameter:
|
385
|
+
version 'v1', using: :param, parameter: 'v'
|
392
386
|
```
|
393
387
|
|
394
388
|
curl http://localhost:9292/statuses/public_timeline?v=v1
|
@@ -399,21 +393,21 @@ version 'v1', using: :param, parameter: "v"
|
|
399
393
|
You can add a description to API methods and namespaces.
|
400
394
|
|
401
395
|
```ruby
|
402
|
-
desc
|
396
|
+
desc 'Returns your public timeline.' do
|
403
397
|
detail 'more details'
|
404
398
|
params API::Entities::Status.documentation
|
405
399
|
success API::Entities::Entity
|
406
|
-
failure [[401, 'Unauthorized',
|
400
|
+
failure [[401, 'Unauthorized', 'Entities::Error']]
|
407
401
|
named 'My named route'
|
408
|
-
headers
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
402
|
+
headers XAuthToken: {
|
403
|
+
description: 'Valdates your identity',
|
404
|
+
required: true
|
405
|
+
},
|
406
|
+
XOptionalHeader: {
|
407
|
+
description: 'Not really needed',
|
414
408
|
required: false
|
415
|
-
|
416
|
-
|
409
|
+
}
|
410
|
+
|
417
411
|
end
|
418
412
|
get :public_timeline do
|
419
413
|
Status.limit(20)
|
@@ -466,7 +460,7 @@ curl --form image_file='@image.jpg;type=image/jpg' http://localhost:9292/upload
|
|
466
460
|
The Grape endpoint:
|
467
461
|
|
468
462
|
```ruby
|
469
|
-
post
|
463
|
+
post 'upload' do
|
470
464
|
# file in params[:image_file]
|
471
465
|
end
|
472
466
|
```
|
@@ -479,19 +473,19 @@ In the case of conflict between either of:
|
|
479
473
|
|
480
474
|
route string parameters will have precedence.
|
481
475
|
|
482
|
-
|
476
|
+
### Declared
|
483
477
|
|
484
|
-
Grape allows you to access only the parameters that have been declared by your `params` block. It filters out the params that have been passed, but are not allowed.
|
478
|
+
Grape allows you to access only the parameters that have been declared by your `params` block. It filters out the params that have been passed, but are not allowed. Consider the following API endpoint:
|
485
479
|
|
486
480
|
````ruby
|
487
481
|
format :json
|
488
482
|
|
489
483
|
post 'users/signup' do
|
490
|
-
{
|
484
|
+
{ 'declared_params' => declared(params) }
|
491
485
|
end
|
492
486
|
````
|
493
487
|
|
494
|
-
If we do not specify any params, declared will return an empty Hashie::Mash instance.
|
488
|
+
If we do not specify any params, `declared` will return an empty `Hashie::Mash` instance.
|
495
489
|
|
496
490
|
**Request**
|
497
491
|
|
@@ -521,7 +515,7 @@ params do
|
|
521
515
|
end
|
522
516
|
|
523
517
|
post 'users/signup' do
|
524
|
-
{
|
518
|
+
{ 'declared_params' => declared(params) }
|
525
519
|
end
|
526
520
|
````
|
527
521
|
|
@@ -544,15 +538,19 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
|
|
544
538
|
}
|
545
539
|
````
|
546
540
|
|
547
|
-
|
541
|
+
The returned hash is a `Hashie::Mash` instance, allowing you to access parameters via dot notation:
|
548
542
|
|
549
543
|
```ruby
|
550
|
-
declared(params).user == declared(params)[
|
544
|
+
declared(params).user == declared(params)['user']
|
551
545
|
```
|
552
546
|
|
553
|
-
#### Include missing
|
554
547
|
|
555
|
-
|
548
|
+
The `#declared` method is not available to `before` filters, as those are evaluated prior
|
549
|
+
to parameter coercion.
|
550
|
+
|
551
|
+
### Include missing
|
552
|
+
|
553
|
+
By default `declared(params)` includes parameters that have `nil` values. If you want to return only the parameters that are not `nil`, you can use the `include_missing` option. By default, `include_missing` is set to `true`. Consider the following API:
|
556
554
|
|
557
555
|
````ruby
|
558
556
|
format :json
|
@@ -563,7 +561,7 @@ params do
|
|
563
561
|
end
|
564
562
|
|
565
563
|
post 'users/signup' do
|
566
|
-
{
|
564
|
+
{ 'declared_params' => declared(params, include_missing: false) }
|
567
565
|
end
|
568
566
|
````
|
569
567
|
|
@@ -613,7 +611,7 @@ params do
|
|
613
611
|
end
|
614
612
|
|
615
613
|
post 'users/signup' do
|
616
|
-
{
|
614
|
+
{ 'declared_params' => declared(params, include_missing: false) }
|
617
615
|
end
|
618
616
|
````
|
619
617
|
|
@@ -685,7 +683,7 @@ You can define validations and coercion options for your parameters using a `par
|
|
685
683
|
```ruby
|
686
684
|
params do
|
687
685
|
requires :id, type: Integer
|
688
|
-
optional :text, type: String, regexp:
|
686
|
+
optional :text, type: String, regexp: /\A[a-z]+\z/
|
689
687
|
group :media do
|
690
688
|
requires :url
|
691
689
|
end
|
@@ -729,7 +727,167 @@ params do
|
|
729
727
|
end
|
730
728
|
```
|
731
729
|
|
732
|
-
|
730
|
+
### Supported Parameter Types
|
731
|
+
|
732
|
+
The following are all valid types, supported out of the box by Grape:
|
733
|
+
|
734
|
+
* Integer
|
735
|
+
* Float
|
736
|
+
* BigDecimal
|
737
|
+
* Numeric
|
738
|
+
* Date
|
739
|
+
* DateTime
|
740
|
+
* Time
|
741
|
+
* Boolean
|
742
|
+
* String
|
743
|
+
* Symbol
|
744
|
+
* Rack::Multipart::UploadedFile (alias `File`)
|
745
|
+
* JSON
|
746
|
+
|
747
|
+
### Custom Types and Coercions
|
748
|
+
|
749
|
+
Aside from the default set of supported types listed above, any class can be
|
750
|
+
used as a type so long as an explicit coercion method is supplied. If the type
|
751
|
+
implements a class-level `parse` method, Grape will use it automatically.
|
752
|
+
This method must take one string argument and return an instance of the correct
|
753
|
+
type, or raise an exception to indicate the value was invalid. E.g.,
|
754
|
+
|
755
|
+
```ruby
|
756
|
+
class Color
|
757
|
+
attr_reader :value
|
758
|
+
def initialize(color)
|
759
|
+
@value = color
|
760
|
+
end
|
761
|
+
|
762
|
+
def self.parse(value)
|
763
|
+
fail 'Invalid color' unless %w(blue red green).include?(value)
|
764
|
+
new(value)
|
765
|
+
end
|
766
|
+
end
|
767
|
+
|
768
|
+
# ...
|
769
|
+
|
770
|
+
params do
|
771
|
+
requires :color, type: Color, default: Color.new('blue')
|
772
|
+
end
|
773
|
+
|
774
|
+
get '/stuff' do
|
775
|
+
# params[:color] is already a Color.
|
776
|
+
params[:color].value
|
777
|
+
end
|
778
|
+
```
|
779
|
+
|
780
|
+
Alternatively, a custom coercion method may be supplied for any type of parameter
|
781
|
+
using `coerce_with`. Any class or object may be given that implements a `parse` or
|
782
|
+
`call` method, in that order of precedence. The method must accept a single string
|
783
|
+
parameter, and the return value must match the given `type`.
|
784
|
+
|
785
|
+
```ruby
|
786
|
+
params do
|
787
|
+
requires :passwd, type: String, coerce_with: Base64.method(:decode)
|
788
|
+
requires :loud_color, type: Color, coerce_with: ->(c) { Color.parse(c.downcase) }
|
789
|
+
|
790
|
+
requires :obj, type: Hash, coerce_with: JSON do
|
791
|
+
requires :words, type: Array[String], coerce_with: ->(val) { val.split(/\s+/) }
|
792
|
+
optional :time, type: Time, coerce_with: Chronic
|
793
|
+
end
|
794
|
+
end
|
795
|
+
```
|
796
|
+
|
797
|
+
### Multipart File Parameters
|
798
|
+
|
799
|
+
Grape makes use of `Rack::Request`'s built-in support for multipart
|
800
|
+
file parameters. Such parameters can be declared with `type: File`:
|
801
|
+
|
802
|
+
```ruby
|
803
|
+
params do
|
804
|
+
requires :avatar, type: File
|
805
|
+
end
|
806
|
+
post '/' do
|
807
|
+
# Parameter will be wrapped using Hashie:
|
808
|
+
params.avatar.filename # => 'avatar.png'
|
809
|
+
params.avatar.type # => 'image/png'
|
810
|
+
params.avatar.tempfile # => #<File>
|
811
|
+
end
|
812
|
+
```
|
813
|
+
|
814
|
+
### First-Class `JSON` Types
|
815
|
+
|
816
|
+
Grape supports complex parameters given as JSON-formatted strings using the special `type: JSON`
|
817
|
+
declaration. JSON objects and arrays of objects are accepted equally, with nested validation
|
818
|
+
rules applied to all objects in either case:
|
819
|
+
|
820
|
+
```ruby
|
821
|
+
params do
|
822
|
+
requires :json, type: JSON do
|
823
|
+
requires :int, type: Integer, values: [1, 2, 3]
|
824
|
+
end
|
825
|
+
end
|
826
|
+
get '/' do
|
827
|
+
params[:json].inspect
|
828
|
+
end
|
829
|
+
|
830
|
+
# ...
|
831
|
+
|
832
|
+
client.get('/', json: '{"int":1}') # => "{:int=>1}"
|
833
|
+
client.get('/', json: '[{"int":"1"}]') # => "[{:int=>1}]"
|
834
|
+
|
835
|
+
client.get('/', json: '{"int":4}') # => HTTP 400
|
836
|
+
client.get('/', json: '[{"int":4}]') # => HTTP 400
|
837
|
+
```
|
838
|
+
|
839
|
+
Additionally `type: Array[JSON]` may be used, which explicitly marks the parameter as an array
|
840
|
+
of objects. If a single object is supplied it will be wrapped.
|
841
|
+
|
842
|
+
```ruby
|
843
|
+
params do
|
844
|
+
requires :json, type: Array[JSON] do
|
845
|
+
requires :int, type: Integer
|
846
|
+
end
|
847
|
+
end
|
848
|
+
get '/' do
|
849
|
+
params[:json].each { |obj| ... } # always works
|
850
|
+
end
|
851
|
+
```
|
852
|
+
For stricter control over the type of JSON structure which may be supplied,
|
853
|
+
use `type: Array, coerce_with: JSON` or `type: Hash, coerce_with: JSON`.
|
854
|
+
|
855
|
+
### Multiple Allowed Types
|
856
|
+
|
857
|
+
Variant-type parameters can be declared using the `types` option rather than `type`:
|
858
|
+
|
859
|
+
```ruby
|
860
|
+
params do
|
861
|
+
requires :status_code, types: [Integer, String, Array[Integer, String]]
|
862
|
+
end
|
863
|
+
get '/' do
|
864
|
+
params[:status_code].inspect
|
865
|
+
end
|
866
|
+
|
867
|
+
# ...
|
868
|
+
|
869
|
+
client.get('/', status_code: 'OK_GOOD') # => "OK_GOOD"
|
870
|
+
client.get('/', status_code: 300) # => 300
|
871
|
+
client.get('/', status_code: %w(404 NOT FOUND)) # => [404, "NOT", "FOUND"]
|
872
|
+
```
|
873
|
+
|
874
|
+
As a special case, variant-member-type collections may also be declared, by
|
875
|
+
passing a `Set` or `Array` with more than one member to `type`:
|
876
|
+
|
877
|
+
```ruby
|
878
|
+
params do
|
879
|
+
requires :status_codes, type: Array[Integer,String]
|
880
|
+
end
|
881
|
+
get '/' do
|
882
|
+
params[:status_codes].inspect
|
883
|
+
end
|
884
|
+
|
885
|
+
# ...
|
886
|
+
|
887
|
+
client.get('/', status_codes: %w(1 two)) # => [1, "two"]
|
888
|
+
```
|
889
|
+
|
890
|
+
### Validation of Nested Parameters
|
733
891
|
|
734
892
|
Parameters can be nested using `group` or by calling `requires` or `optional` with a block.
|
735
893
|
In the above example, this means `params[:media][:url]` is required along with `params[:id]`,
|
@@ -752,6 +910,21 @@ params do
|
|
752
910
|
end
|
753
911
|
```
|
754
912
|
|
913
|
+
### Dependent Parameters
|
914
|
+
|
915
|
+
Suppose some of your parameters are only relevant if another parameter is given;
|
916
|
+
Grape allows you to express this relationship through the `given` method in your
|
917
|
+
parameters block, like so:
|
918
|
+
|
919
|
+
```ruby
|
920
|
+
params do
|
921
|
+
optional :shelf_id, type: Integer
|
922
|
+
given :shelf_id do
|
923
|
+
requires :bin_id, type: Integer
|
924
|
+
end
|
925
|
+
end
|
926
|
+
```
|
927
|
+
|
755
928
|
### Built-in Validators
|
756
929
|
|
757
930
|
#### `allow_blank`
|
@@ -939,14 +1112,14 @@ Namespaces allow parameter definitions and apply to every method within the name
|
|
939
1112
|
```ruby
|
940
1113
|
namespace :statuses do
|
941
1114
|
params do
|
942
|
-
requires :user_id, type: Integer, desc:
|
1115
|
+
requires :user_id, type: Integer, desc: 'A user ID.'
|
943
1116
|
end
|
944
|
-
namespace
|
1117
|
+
namespace ':user_id' do
|
945
1118
|
desc "Retrieve a user's status."
|
946
1119
|
params do
|
947
|
-
requires :status_id, type: Integer, desc:
|
1120
|
+
requires :status_id, type: Integer, desc: 'A status ID.'
|
948
1121
|
end
|
949
|
-
get
|
1122
|
+
get ':status_id' do
|
950
1123
|
User.find(params[:user_id]).statuses.find(params[:status_id])
|
951
1124
|
end
|
952
1125
|
end
|
@@ -961,11 +1134,11 @@ You can conveniently define a route parameter as a namespace using `route_param`
|
|
961
1134
|
```ruby
|
962
1135
|
namespace :statuses do
|
963
1136
|
route_param :id do
|
964
|
-
desc
|
1137
|
+
desc 'Returns all replies for a status.'
|
965
1138
|
get 'replies' do
|
966
1139
|
Status.find(params[:id]).replies
|
967
1140
|
end
|
968
|
-
desc
|
1141
|
+
desc 'Returns a status.'
|
969
1142
|
get do
|
970
1143
|
Status.find(params[:id])
|
971
1144
|
end
|
@@ -978,8 +1151,8 @@ end
|
|
978
1151
|
```ruby
|
979
1152
|
class AlphaNumeric < Grape::Validations::Base
|
980
1153
|
def validate_param!(attr_name, params)
|
981
|
-
unless params[attr_name] =~
|
982
|
-
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message:
|
1154
|
+
unless params[attr_name] =~ /\A[[:alnum:]]+\z/
|
1155
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: 'must consist of alpha-numeric characters'
|
983
1156
|
end
|
984
1157
|
end
|
985
1158
|
end
|
@@ -1034,6 +1207,16 @@ subject.rescue_from Grape::Exceptions::ValidationErrors do |e|
|
|
1034
1207
|
end
|
1035
1208
|
```
|
1036
1209
|
|
1210
|
+
`Grape::Exceptions::ValidationErrors#full_messages` returns the validation messages as an array. `Grape::Exceptions::ValidationErrors#message` joins the messages to one string.
|
1211
|
+
|
1212
|
+
For responding with an array of validation messages, you can use `Grape::Exceptions::ValidationErrors#full_messages`.
|
1213
|
+
```ruby
|
1214
|
+
format :json
|
1215
|
+
subject.rescue_from Grape::Exceptions::ValidationErrors do |e|
|
1216
|
+
error!({ messages: e.full_messages }, 400)
|
1217
|
+
end
|
1218
|
+
```
|
1219
|
+
|
1037
1220
|
### I18n
|
1038
1221
|
|
1039
1222
|
Grape supports I18n for parameter-related error messages, but will fallback to English if
|
@@ -1081,7 +1264,7 @@ namespace :outer, requirements: { id: /[0-9]*/ } do
|
|
1081
1264
|
get :id do
|
1082
1265
|
end
|
1083
1266
|
|
1084
|
-
get
|
1267
|
+
get ':id/edit' do
|
1085
1268
|
end
|
1086
1269
|
end
|
1087
1270
|
```
|
@@ -1127,7 +1310,7 @@ class API < Grape::API
|
|
1127
1310
|
end
|
1128
1311
|
end
|
1129
1312
|
|
1130
|
-
desc
|
1313
|
+
desc 'Get collection'
|
1131
1314
|
params do
|
1132
1315
|
use :pagination # aliases: includes, use_scope
|
1133
1316
|
end
|
@@ -1157,7 +1340,7 @@ end
|
|
1157
1340
|
class API < Grape::API
|
1158
1341
|
helpers SharedParams
|
1159
1342
|
|
1160
|
-
desc
|
1343
|
+
desc 'Get collection.'
|
1161
1344
|
params do
|
1162
1345
|
use :period, :pagination
|
1163
1346
|
end
|
@@ -1187,7 +1370,7 @@ end
|
|
1187
1370
|
class API < Grape::API
|
1188
1371
|
helpers SharedParams
|
1189
1372
|
|
1190
|
-
desc
|
1373
|
+
desc 'Get a sorted collection.'
|
1191
1374
|
params do
|
1192
1375
|
use :order, order_by:%i(id created_at), default_order_by: :created_at, default_order: :asc
|
1193
1376
|
end
|
@@ -1198,6 +1381,10 @@ class API < Grape::API
|
|
1198
1381
|
end
|
1199
1382
|
```
|
1200
1383
|
|
1384
|
+
## Path Helpers
|
1385
|
+
|
1386
|
+
If you need methods for generating paths inside your endpoints, please see the [grape-route-helpers](https://github.com/reprah/grape-route-helpers) gem.
|
1387
|
+
|
1201
1388
|
## Parameter Documentation
|
1202
1389
|
|
1203
1390
|
You can attach additional documentation to `params` using a `documentation` hash.
|
@@ -1252,6 +1439,29 @@ Specify an optional path.
|
|
1252
1439
|
cookies.delete :status_count, path: '/'
|
1253
1440
|
```
|
1254
1441
|
|
1442
|
+
## HTTP Status Code
|
1443
|
+
|
1444
|
+
By default Grape returns a 200 status code for `GET`-Requests and 201 for `POST`-Requests.
|
1445
|
+
You can use `status` to query and set the actual HTTP Status Code
|
1446
|
+
|
1447
|
+
```ruby
|
1448
|
+
post do
|
1449
|
+
status 202
|
1450
|
+
|
1451
|
+
if status == 200
|
1452
|
+
# do some thing
|
1453
|
+
end
|
1454
|
+
end
|
1455
|
+
```
|
1456
|
+
|
1457
|
+
You can also use one of status codes symbols that are provided by [Rack utils](http://www.rubydoc.info/github/rack/rack/Rack/Utils#HTTP_STATUS_CODES-constant)
|
1458
|
+
|
1459
|
+
```ruby
|
1460
|
+
post do
|
1461
|
+
status :no_content
|
1462
|
+
end
|
1463
|
+
```
|
1464
|
+
|
1255
1465
|
## Redirecting
|
1256
1466
|
|
1257
1467
|
You can redirect to a new url temporarily (302) or permanently (301).
|
@@ -1336,10 +1546,10 @@ You can also return JSON formatted objects by raising error! and passing a hash
|
|
1336
1546
|
instead of a message.
|
1337
1547
|
|
1338
1548
|
```ruby
|
1339
|
-
error!({ error:
|
1549
|
+
error!({ error: 'unexpected error', detail: 'missing widget' }, 500)
|
1340
1550
|
```
|
1341
1551
|
|
1342
|
-
You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/
|
1552
|
+
You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
|
1343
1553
|
|
1344
1554
|
```ruby
|
1345
1555
|
module API
|
@@ -1376,7 +1586,7 @@ By default Grape returns a 500 status code from `error!`. You can change this wi
|
|
1376
1586
|
class API < Grape::API
|
1377
1587
|
default_error_status 400
|
1378
1588
|
get '/example' do
|
1379
|
-
error!
|
1589
|
+
error! 'This should have http status code 400'
|
1380
1590
|
end
|
1381
1591
|
end
|
1382
1592
|
```
|
@@ -1421,7 +1631,7 @@ Custom error formatters for existing and additional types can be defined with a
|
|
1421
1631
|
|
1422
1632
|
```ruby
|
1423
1633
|
class Twitter::API < Grape::API
|
1424
|
-
error_formatter :txt,
|
1634
|
+
error_formatter :txt, ->(message, backtrace, options, env) {
|
1425
1635
|
"error: #{message} from #{backtrace}"
|
1426
1636
|
}
|
1427
1637
|
end
|
@@ -1441,8 +1651,7 @@ class Twitter::API < Grape::API
|
|
1441
1651
|
end
|
1442
1652
|
```
|
1443
1653
|
|
1444
|
-
You can rescue all exceptions with a code block. The `error!` wrapper
|
1445
|
-
automatically sets the default error code and content-type.
|
1654
|
+
You can rescue all exceptions with a code block. The `error!` wrapper automatically sets the default error code and content-type.
|
1446
1655
|
|
1447
1656
|
```ruby
|
1448
1657
|
class Twitter::API < Grape::API
|
@@ -1458,19 +1667,17 @@ Optionally, you can set the format, status code and headers.
|
|
1458
1667
|
class Twitter::API < Grape::API
|
1459
1668
|
format :json
|
1460
1669
|
rescue_from :all do |e|
|
1461
|
-
error!({ error:
|
1670
|
+
error!({ error: 'Server error.' }, 500, { 'Content-Type' => 'text/error' })
|
1462
1671
|
end
|
1463
1672
|
end
|
1464
1673
|
```
|
1465
1674
|
|
1466
|
-
|
1467
|
-
You can also rescue specific exceptions with a code block and handle the Rack
|
1468
|
-
response at the lowest level.
|
1675
|
+
You can also rescue specific exceptions with a code block and handle the Rack response at the lowest level.
|
1469
1676
|
|
1470
1677
|
```ruby
|
1471
1678
|
class Twitter::API < Grape::API
|
1472
1679
|
rescue_from :all do |e|
|
1473
|
-
Rack::Response.new([ e.message ], 500, {
|
1680
|
+
Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' }).finish
|
1474
1681
|
end
|
1475
1682
|
end
|
1476
1683
|
```
|
@@ -1524,9 +1731,15 @@ rescue_from RuntimeError, rescue_subclasses: false do |e|
|
|
1524
1731
|
end
|
1525
1732
|
```
|
1526
1733
|
|
1527
|
-
|
1734
|
+
The `rescue_from` block must return a `Rack::Response` object, call `error!` or re-raise an exception.
|
1735
|
+
|
1736
|
+
#### Unrescuable Exceptions
|
1528
1737
|
|
1529
|
-
|
1738
|
+
`Grape::Exceptions::InvalidVersionHeader`, which is raised when the version in the request header doesn't match the currently evaluated version for the endpoint, will _never_ be rescued from a `rescue_from` block (even a `rescue_from :all`) This is because Grape relies on Rack to catch that error and try the next versioned-route for cases where there exist identical Grape endpoints with different versions.
|
1739
|
+
|
1740
|
+
### Rails 3.x
|
1741
|
+
|
1742
|
+
When mounted inside containers, such as Rails 3.x, errors such as "404 Not Found" or
|
1530
1743
|
"406 Not Acceptable" will likely be handled and rendered by Rails handlers. For instance,
|
1531
1744
|
accessing a nonexistent route "/api/foo" raises a 404, which inside rails will ultimately
|
1532
1745
|
be translated to an `ActionController::RoutingError`, which most likely will get rendered
|
@@ -1668,11 +1881,11 @@ For example, the following API will let you upload arbitrary files and return th
|
|
1668
1881
|
|
1669
1882
|
```ruby
|
1670
1883
|
class Twitter::API < Grape::API
|
1671
|
-
post
|
1884
|
+
post 'attachment' do
|
1672
1885
|
filename = params[:file][:filename]
|
1673
1886
|
content_type MIME::Types.type_for(filename)[0].to_s
|
1674
1887
|
env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
|
1675
|
-
header
|
1888
|
+
header 'Content-Disposition', "attachment; filename*=UTF-8''#{URI.escape(filename)}"
|
1676
1889
|
params[:file][:tempfile].read
|
1677
1890
|
end
|
1678
1891
|
end
|
@@ -1725,7 +1938,7 @@ If you do not want this behavior, set the default error formatter with `default_
|
|
1725
1938
|
```ruby
|
1726
1939
|
class Twitter::API < Grape::API
|
1727
1940
|
format :json
|
1728
|
-
content_type :txt,
|
1941
|
+
content_type :txt, 'text/plain'
|
1729
1942
|
default_error_formatter :txt
|
1730
1943
|
end
|
1731
1944
|
```
|
@@ -1734,8 +1947,8 @@ Custom formatters for existing and additional types can be defined with a proc.
|
|
1734
1947
|
|
1735
1948
|
```ruby
|
1736
1949
|
class Twitter::API < Grape::API
|
1737
|
-
content_type :xls,
|
1738
|
-
formatter :xls,
|
1950
|
+
content_type :xls, 'application/vnd.ms-excel'
|
1951
|
+
formatter :xls, ->(object, env) { object.to_xls }
|
1739
1952
|
end
|
1740
1953
|
```
|
1741
1954
|
|
@@ -1749,7 +1962,7 @@ module XlsFormatter
|
|
1749
1962
|
end
|
1750
1963
|
|
1751
1964
|
class Twitter::API < Grape::API
|
1752
|
-
content_type :xls,
|
1965
|
+
content_type :xls, 'application/vnd.ms-excel'
|
1753
1966
|
formatter :xls, XlsFormatter
|
1754
1967
|
end
|
1755
1968
|
```
|
@@ -1762,6 +1975,11 @@ Built-in formatters are the following.
|
|
1762
1975
|
* `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`
|
1763
1976
|
* `:binary`: data will be returned "as is"
|
1764
1977
|
|
1978
|
+
Response statuses that indicate no content as defined by [Rack](https://github.com/rack)
|
1979
|
+
[here](https://github.com/rack/rack/blob/master/lib/rack/utils.rb#L567)
|
1980
|
+
will bypass serialization and the body entity - though there should be none -
|
1981
|
+
will not be modified.
|
1982
|
+
|
1765
1983
|
### JSONP
|
1766
1984
|
|
1767
1985
|
Grape supports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the
|
@@ -1807,7 +2025,7 @@ by setting the `Content-Type` header.
|
|
1807
2025
|
```ruby
|
1808
2026
|
class API < Grape::API
|
1809
2027
|
get '/home_timeline_js' do
|
1810
|
-
content_type
|
2028
|
+
content_type 'application/javascript'
|
1811
2029
|
"var statuses = ...;"
|
1812
2030
|
end
|
1813
2031
|
end
|
@@ -1835,11 +2053,11 @@ end
|
|
1835
2053
|
```
|
1836
2054
|
|
1837
2055
|
```ruby
|
1838
|
-
content_type :txt,
|
1839
|
-
content_type :custom,
|
2056
|
+
content_type :txt, 'text/plain'
|
2057
|
+
content_type :custom, 'text/custom'
|
1840
2058
|
parser :custom, CustomParser
|
1841
2059
|
|
1842
|
-
put
|
2060
|
+
put 'value' do
|
1843
2061
|
params[:value]
|
1844
2062
|
end
|
1845
2063
|
```
|
@@ -1860,8 +2078,8 @@ hash may include `:with`, which defines the entity to expose.
|
|
1860
2078
|
|
1861
2079
|
### Grape Entities
|
1862
2080
|
|
1863
|
-
Add the [grape-entity](https://github.com/
|
1864
|
-
Please refer to the [grape-entity documentation](https://github.com/
|
2081
|
+
Add the [grape-entity](https://github.com/ruby-grape/grape-entity) gem to your Gemfile.
|
2082
|
+
Please refer to the [grape-entity documentation](https://github.com/ruby-grape/grape-entity/blob/master/README.md)
|
1865
2083
|
for more details.
|
1866
2084
|
|
1867
2085
|
The following example exposes statuses.
|
@@ -1871,9 +2089,9 @@ module API
|
|
1871
2089
|
module Entities
|
1872
2090
|
class Status < Grape::Entity
|
1873
2091
|
expose :user_name
|
1874
|
-
expose :text, documentation: { type:
|
2092
|
+
expose :text, documentation: { type: 'string', desc: 'Status update text.' }
|
1875
2093
|
expose :ip, if: { type: :full }
|
1876
|
-
expose :user_type, :user_id, if:
|
2094
|
+
expose :user_type, :user_id, if: ->(status, options) { status.user.public? }
|
1877
2095
|
expose :digest { |status, options| Digest::MD5.hexdigest(status.txt) }
|
1878
2096
|
expose :replies, using: API::Status, as: :replies
|
1879
2097
|
end
|
@@ -1981,12 +2199,12 @@ end
|
|
1981
2199
|
|
1982
2200
|
### Hypermedia and Roar
|
1983
2201
|
|
1984
|
-
You can use [Roar](https://github.com/apotonick/roar) to render HAL or Collection+JSON with the help of [grape-roar](https://github.com/
|
2202
|
+
You can use [Roar](https://github.com/apotonick/roar) to render HAL or Collection+JSON with the help of [grape-roar](https://github.com/ruby-grape/grape-roar), which defines a custom JSON formatter and enables presenting entities with Grape's `present` keyword.
|
1985
2203
|
|
1986
2204
|
### Rabl
|
1987
2205
|
|
1988
2206
|
You can use [Rabl](https://github.com/nesquena/rabl) templates with the help of the
|
1989
|
-
[grape-rabl](https://github.com/
|
2207
|
+
[grape-rabl](https://github.com/ruby-grape/grape-rabl) gem, which defines a custom Grape Rabl
|
1990
2208
|
formatter.
|
1991
2209
|
|
1992
2210
|
### Active Model Serializers
|
@@ -2023,6 +2241,8 @@ end
|
|
2023
2241
|
Use `body false` to return `204 No Content` without any data or content-type.
|
2024
2242
|
|
2025
2243
|
You can also set the response to a file-like object with `file`.
|
2244
|
+
Note: Rack will read your entire Enumerable before returning a response. If
|
2245
|
+
you would like to stream the response, see `stream`.
|
2026
2246
|
|
2027
2247
|
```ruby
|
2028
2248
|
class FileStreamer
|
@@ -2044,6 +2264,16 @@ class API < Grape::API
|
|
2044
2264
|
end
|
2045
2265
|
```
|
2046
2266
|
|
2267
|
+
If you want a file-like object to be streamed using Rack::Chunked, use `stream`.
|
2268
|
+
|
2269
|
+
```ruby
|
2270
|
+
class API < Grape::API
|
2271
|
+
get '/' do
|
2272
|
+
stream FileStreamer.new('file.bin')
|
2273
|
+
end
|
2274
|
+
end
|
2275
|
+
```
|
2276
|
+
|
2047
2277
|
## Authentication
|
2048
2278
|
|
2049
2279
|
### Basic and Digest Auth
|
@@ -2077,7 +2307,7 @@ For registering a Middleware you need the following options:
|
|
2077
2307
|
* `label` - the name for your authenticator to use it later
|
2078
2308
|
* `MiddlewareClass` - the MiddlewareClass to use for authentication
|
2079
2309
|
* `option_lookup_proc` - A Proc with one Argument to lookup the options at
|
2080
|
-
runtime (return value is an `Array` as
|
2310
|
+
runtime (return value is an `Array` as Parameter for the Middleware).
|
2081
2311
|
|
2082
2312
|
Example:
|
2083
2313
|
|
@@ -2104,7 +2334,7 @@ Grape exposes arrays of API versions and compiled routes. Each route contains a
|
|
2104
2334
|
```ruby
|
2105
2335
|
class TwitterAPI < Grape::API
|
2106
2336
|
version 'v1'
|
2107
|
-
desc
|
2337
|
+
desc 'Includes custom settings.'
|
2108
2338
|
route_setting :custom, key: 'value'
|
2109
2339
|
get do
|
2110
2340
|
|
@@ -2128,11 +2358,11 @@ It's possible to retrieve the information about the current route from within an
|
|
2128
2358
|
|
2129
2359
|
```ruby
|
2130
2360
|
class MyAPI < Grape::API
|
2131
|
-
desc
|
2361
|
+
desc 'Returns a description of a parameter.'
|
2132
2362
|
params do
|
2133
|
-
requires :id, type: Integer, desc:
|
2363
|
+
requires :id, type: Integer, desc: 'Identity.'
|
2134
2364
|
end
|
2135
|
-
get
|
2365
|
+
get 'params/:id' do
|
2136
2366
|
route.route_params[params[:id]] # yields the parameter description
|
2137
2367
|
end
|
2138
2368
|
end
|
@@ -2173,7 +2403,7 @@ E.g. using `before`:
|
|
2173
2403
|
|
2174
2404
|
```ruby
|
2175
2405
|
before do
|
2176
|
-
header
|
2406
|
+
header 'X-Robots-Tag', 'noindex'
|
2177
2407
|
end
|
2178
2408
|
```
|
2179
2409
|
|
@@ -2298,12 +2528,12 @@ This will match all paths starting with '/statuses/'. There is one caveat though
|
|
2298
2528
|
the `params[:status]` parameter only holds the first part of the request url.
|
2299
2529
|
Luckily this can be circumvented by using the described above syntax for path
|
2300
2530
|
specification and using the `PATH_INFO` Rack environment variable, using
|
2301
|
-
`env[
|
2531
|
+
`env['PATH_INFO']`. This will hold everything that comes after the '/statuses/'
|
2302
2532
|
part.
|
2303
2533
|
|
2304
|
-
|
2534
|
+
## Using Custom Middleware
|
2305
2535
|
|
2306
|
-
|
2536
|
+
### Rails Middleware
|
2307
2537
|
|
2308
2538
|
Note that when you're using Grape mounted on Rails you don't have to use Rails middleware because it's already included into your middleware stack.
|
2309
2539
|
You only have to implement the helpers to access the specific `env` variable.
|
@@ -2320,7 +2550,7 @@ class API < Grape::API
|
|
2320
2550
|
|
2321
2551
|
helpers do
|
2322
2552
|
def client_ip
|
2323
|
-
env[
|
2553
|
+
env['action_dispatch.remote_ip'].to_s
|
2324
2554
|
end
|
2325
2555
|
end
|
2326
2556
|
|
@@ -2350,20 +2580,32 @@ describe Twitter::API do
|
|
2350
2580
|
Twitter::API
|
2351
2581
|
end
|
2352
2582
|
|
2353
|
-
|
2354
|
-
|
2355
|
-
|
2356
|
-
|
2357
|
-
|
2358
|
-
expect(JSON.parse(last_response.body)).to eq []
|
2359
|
-
end
|
2583
|
+
context 'GET /api/statuses/public_timeline' do
|
2584
|
+
it 'returns an empty array of statuses' do
|
2585
|
+
get '/api/statuses/public_timeline'
|
2586
|
+
expect(last_response.status).to eq(200)
|
2587
|
+
expect(JSON.parse(last_response.body)).to eq []
|
2360
2588
|
end
|
2361
|
-
|
2362
|
-
|
2363
|
-
|
2364
|
-
|
2365
|
-
|
2366
|
-
|
2589
|
+
end
|
2590
|
+
context 'GET /api/statuses/:id' do
|
2591
|
+
it 'returns a status by id' do
|
2592
|
+
status = Status.create!
|
2593
|
+
get "/api/statuses/#{status.id}"
|
2594
|
+
expect(last_response.body).to eq status.to_json
|
2595
|
+
end
|
2596
|
+
end
|
2597
|
+
end
|
2598
|
+
```
|
2599
|
+
|
2600
|
+
There's no standard way of sending arrays of objects via an HTTP GET, so POST JSON data and specify the correct content-type.
|
2601
|
+
|
2602
|
+
```ruby
|
2603
|
+
describe Twitter::API do
|
2604
|
+
context 'POST /api/statuses' do
|
2605
|
+
it 'creates many statuses' do
|
2606
|
+
statuses = [{ text: '...' }, { text: '...'}]
|
2607
|
+
post '/api/statuses', statuses.to_json, 'CONTENT_TYPE' => 'application/json'
|
2608
|
+
expect(last_response.body).to eq 201
|
2367
2609
|
end
|
2368
2610
|
end
|
2369
2611
|
end
|
@@ -2381,8 +2623,8 @@ Airborne.configure do |config|
|
|
2381
2623
|
end
|
2382
2624
|
|
2383
2625
|
describe Twitter::API do
|
2384
|
-
|
2385
|
-
it
|
2626
|
+
context 'GET /api/statuses/:id' do
|
2627
|
+
it 'returns a status by id' do
|
2386
2628
|
status = Status.create!
|
2387
2629
|
get "/api/statuses/#{status.id}"
|
2388
2630
|
expect_json(status.as_json)
|
@@ -2394,7 +2636,7 @@ end
|
|
2394
2636
|
#### MiniTest
|
2395
2637
|
|
2396
2638
|
```ruby
|
2397
|
-
require
|
2639
|
+
require 'test_helper'
|
2398
2640
|
|
2399
2641
|
class Twitter::APITest < MiniTest::Test
|
2400
2642
|
include Rack::Test::Methods
|
@@ -2404,7 +2646,7 @@ class Twitter::APITest < MiniTest::Test
|
|
2404
2646
|
end
|
2405
2647
|
|
2406
2648
|
def test_get_api_statuses_public_timeline_returns_an_empty_array_of_statuses
|
2407
|
-
get
|
2649
|
+
get '/api/statuses/public_timeline'
|
2408
2650
|
assert last_response.ok?
|
2409
2651
|
assert_equal [], JSON.parse(last_response.body)
|
2410
2652
|
end
|
@@ -2423,15 +2665,15 @@ end
|
|
2423
2665
|
|
2424
2666
|
```ruby
|
2425
2667
|
describe Twitter::API do
|
2426
|
-
|
2427
|
-
it
|
2428
|
-
get
|
2668
|
+
context 'GET /api/statuses/public_timeline' do
|
2669
|
+
it 'returns an empty array of statuses' do
|
2670
|
+
get '/api/statuses/public_timeline'
|
2429
2671
|
expect(response.status).to eq(200)
|
2430
2672
|
expect(JSON.parse(response.body)).to eq []
|
2431
2673
|
end
|
2432
2674
|
end
|
2433
|
-
|
2434
|
-
it
|
2675
|
+
context 'GET /api/statuses/:id' do
|
2676
|
+
it 'returns a status by id' do
|
2435
2677
|
status = Status.create!
|
2436
2678
|
get "/api/statuses/#{status.id}"
|
2437
2679
|
expect(response.body).to eq status.to_json
|
@@ -2459,13 +2701,13 @@ class Twitter::APITest < ActiveSupport::TestCase
|
|
2459
2701
|
Rails.application
|
2460
2702
|
end
|
2461
2703
|
|
2462
|
-
test
|
2463
|
-
get
|
2704
|
+
test 'GET /api/statuses/public_timeline returns an empty array of statuses' do
|
2705
|
+
get '/api/statuses/public_timeline'
|
2464
2706
|
assert last_response.ok?
|
2465
2707
|
assert_equal [], JSON.parse(last_response.body)
|
2466
2708
|
end
|
2467
2709
|
|
2468
|
-
test
|
2710
|
+
test 'GET /api/statuses/:id returns a status by id' do
|
2469
2711
|
status = Status.create!
|
2470
2712
|
get "/api/statuses/#{status.id}"
|
2471
2713
|
assert_equal status.to_json, last_response.body
|
@@ -2510,15 +2752,15 @@ Add API paths to `config/application.rb`.
|
|
2510
2752
|
|
2511
2753
|
```ruby
|
2512
2754
|
# Auto-load API and its subdirectories
|
2513
|
-
config.paths.add File.join(
|
2514
|
-
config.autoload_paths += Dir[Rails.root.join(
|
2755
|
+
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
|
2756
|
+
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
|
2515
2757
|
```
|
2516
2758
|
|
2517
2759
|
Create `config/initializers/reload_api.rb`.
|
2518
2760
|
|
2519
2761
|
```ruby
|
2520
2762
|
if Rails.env.development?
|
2521
|
-
ActiveSupport::Dependencies.explicitly_unloadable_constants <<
|
2763
|
+
ActiveSupport::Dependencies.explicitly_unloadable_constants << 'Twitter::API'
|
2522
2764
|
|
2523
2765
|
api_files = Dir[Rails.root.join('app', 'api', '**', '*.rb')]
|
2524
2766
|
api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
|
@@ -2534,9 +2776,39 @@ See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-
|
|
2534
2776
|
|
2535
2777
|
## Performance Monitoring
|
2536
2778
|
|
2537
|
-
|
2538
|
-
|
2539
|
-
|
2779
|
+
### Active Support Instrumentation
|
2780
|
+
|
2781
|
+
Grape has built-in support for [ActiveSupport::Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) which provides simple hook points to instrument key parts of your application.
|
2782
|
+
|
2783
|
+
The following are currently supported:
|
2784
|
+
|
2785
|
+
#### endpoint_run.grape
|
2786
|
+
|
2787
|
+
The main execution of an endpoint, includes filters and rendering.
|
2788
|
+
|
2789
|
+
* *endpoint* - The endpoint instance
|
2790
|
+
|
2791
|
+
#### endpoint_render.grape
|
2792
|
+
|
2793
|
+
The execution of the main content block of the endpoint.
|
2794
|
+
|
2795
|
+
* *endpoint* - The endpoint instance
|
2796
|
+
|
2797
|
+
#### endpoint_run_filters.grape
|
2798
|
+
|
2799
|
+
* *endpoint* - The endpoint instance
|
2800
|
+
* *filters* - The filters being executed
|
2801
|
+
* *type* - The type of filters (before, before_validation, after_validation, after)
|
2802
|
+
|
2803
|
+
See the [ActiveSupport::Notifications documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) for information on how to subscribe to these events.
|
2804
|
+
|
2805
|
+
### Monitoring Products
|
2806
|
+
|
2807
|
+
Grape integrates with following third-party tools:
|
2808
|
+
|
2809
|
+
* **New Relic** - [built-in support](https://docs.newrelic.com/docs/agents/ruby-agent/frameworks/grape-instrumentation) from v3.10.0 of the official [newrelic_rpm](https://github.com/newrelic/rpm) gem, also [newrelic-grape](https://github.com/xinminlabs/newrelic-grape) gem
|
2810
|
+
* **Librato Metrics** - [grape-librato](https://github.com/seanmoon/grape-librato) gem
|
2811
|
+
* **[Skylight](https://www.skylight.io/)** - [skylight](https://github.com/skylightio/skylight-ruby) gem, [documentation](https://docs.skylight.io/grape/)
|
2540
2812
|
|
2541
2813
|
## Contributing to Grape
|
2542
2814
|
|
@@ -2545,13 +2817,6 @@ features and discuss issues.
|
|
2545
2817
|
|
2546
2818
|
See [CONTRIBUTING](CONTRIBUTING.md).
|
2547
2819
|
|
2548
|
-
## Hacking on Grape
|
2549
|
-
|
2550
|
-
You can start hacking on Grape on
|
2551
|
-
[Nitrous.IO](https://www.nitrous.io/?utm_source=github.com&utm_campaign=grape&utm_medium=hackonnitrous) in a matter of seconds:
|
2552
|
-
|
2553
|
-
[](https://www.nitrous.io/hack_button?source=embed&runtime=rails&repo=intridea%2Fgrape&file_to_open=README.md)
|
2554
|
-
|
2555
2820
|
## License
|
2556
2821
|
|
2557
2822
|
MIT License. See LICENSE for details.
|