grape 0.13.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 +28 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +166 -0
- data/README.md +305 -163
- data/Rakefile +30 -33
- data/UPGRADING.md +31 -0
- 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 +5 -4
- data/lib/grape.rb +9 -5
- data/lib/grape/dsl/configuration.rb +5 -2
- data/lib/grape/dsl/helpers.rb +8 -3
- data/lib/grape/dsl/inside_route.rb +67 -44
- data/lib/grape/dsl/parameters.rb +21 -12
- data/lib/grape/dsl/request_response.rb +1 -1
- data/lib/grape/dsl/routing.rb +3 -4
- data/lib/grape/endpoint.rb +63 -28
- 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/formatter/serializable_hash.rb +3 -2
- data/lib/grape/locale/en.yml +4 -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 +7 -4
- data/lib/grape/middleware/error.rb +3 -2
- data/lib/grape/middleware/filter.rb +1 -1
- data/lib/grape/middleware/formatter.rb +47 -44
- 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/path.rb +3 -3
- data/lib/grape/request.rb +40 -0
- data/lib/grape/util/content_types.rb +9 -9
- data/lib/grape/util/env.rb +22 -0
- 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 +83 -15
- data/lib/grape/validations/types.rb +144 -0
- 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/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 +31 -42
- 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/version.rb +1 -1
- 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 +88 -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 +3 -2
- data/spec/grape/dsl/parameters_spec.rb +0 -6
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/endpoint_spec.rb +61 -20
- data/spec/grape/entity_spec.rb +10 -8
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +1 -15
- data/spec/grape/integration/rack_spec.rb +3 -2
- data/spec/grape/middleware/base_spec.rb +7 -5
- 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 -0
- 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/validations/params_scope_spec.rb +11 -9
- data/spec/grape/validations/types_spec.rb +95 -0
- data/spec/grape/validations/validators/coerce_spec.rb +335 -2
- data/spec/grape/validations/validators/values_spec.rb +15 -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 +51 -13
- 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/grape/http/request.rb +0 -35
- data/lib/grape/util/parameter_types.rb +0 -58
- data/spec/grape/util/parameter_types_spec.rb +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3bc83c8aa5374d62068477b48daebd22f10c1761
|
4
|
+
data.tar.gz: a91b6bf2854b1476b9565e3303efa4a67e431b17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 555a5eb54fc665f51ae44d4e3103e2c9b4ce92c3fc594c20cbb4001efdbe6515e95200fabe30455085b0a7b3ccaf178dbc1bb2ba75c6bdb5100a75521341614c
|
7
|
+
data.tar.gz: e66803d3c8454744ea814d2d68e0e5fccaab9c544ddfeacf27c64b68aacd36821612875fa263b2d844c307e6dacc48655f98a594c4ecb1f87d595441abf56ee9
|
data/Appraisals
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
-
appraise
|
2
|
-
gem
|
1
|
+
appraise 'rails-3' do
|
2
|
+
gem 'rails', '3.2.19'
|
3
|
+
gem 'rack-cache', '<= 1.2' # Pin as next rack-cache version (1.3) removes Ruby1.9 support
|
3
4
|
end
|
4
5
|
|
5
|
-
appraise
|
6
|
-
gem
|
6
|
+
appraise 'rails-4' do
|
7
|
+
gem 'rails', '4.1.6'
|
8
|
+
end
|
9
|
+
|
10
|
+
appraise 'rack-1.5.2' do
|
11
|
+
gem 'rack', '1.5.2'
|
7
12
|
end
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
0.14.0 (12/17/2015)
|
2
|
+
===================
|
3
|
+
|
4
|
+
#### Features
|
5
|
+
|
6
|
+
* [#1218](https://github.com/ruby-grape/grape/pull/1218): Provide array index context in errors - [@towanda](https://github.com/towanda).
|
7
|
+
* [#1196](https://github.com/ruby-grape/grape/pull/1196): Allow multiple `before_each` blocks - [@huynhquancam](https://github.com/huynhquancam).
|
8
|
+
* [#1190](https://github.com/ruby-grape/grape/putt/1190): Bypass formatting for statuses with no entity-body - [@tylerdooling](https://github.com/tylerdooling).
|
9
|
+
* [#1188](https://github.com/ruby-grape/grape/putt/1188): Allow parameters with more than one type - [@dslh](https://github.com/dslh).
|
10
|
+
* [#1179](https://github.com/ruby-grape/grape/pull/1179): Allow all RFC6838 valid characters in header vendor - [@suan](https://github.com/suan).
|
11
|
+
* [#1170](https://github.com/ruby-grape/grape/pull/1170): Allow dashes and periods in header vendor - [@suan](https://github.com/suan).
|
12
|
+
* [#1167](https://github.com/ruby-grape/grape/pull/1167): Convenience wrapper `type: File` for validating multipart file parameters - [@dslh](https://github.com/dslh).
|
13
|
+
* [#1167](https://github.com/ruby-grape/grape/pull/1167): Refactor and extend coercion and type validation system - [@dslh](https://github.com/dslh).
|
14
|
+
* [#1163](https://github.com/ruby-grape/grape/pull/1163): First-class `JSON` parameter type - [@dslh](https://github.com/dslh).
|
15
|
+
* [#1161](https://github.com/ruby-grape/grape/pull/1161): Custom parameter coercion using `coerce_with` - [@dslh](https://github.com/dslh).
|
16
|
+
|
17
|
+
#### Fixes
|
18
|
+
|
19
|
+
* [#1194](https://github.com/ruby-grape/grape/pull/1194): Redirect as plain text with message - [@tylerdooling](https://github.com/tylerdooling).
|
20
|
+
* [#1185](https://github.com/ruby-grape/grape/pull/1185): Use formatters for custom vendored content types - [@tylerdooling](https://github.com/tylerdooling).
|
21
|
+
* [#1156](https://github.com/ruby-grape/grape/pull/1156): Fixed `no implicit conversion of Symbol into Integer` with nested `values` validation - [@quickpay](https://github.com/quickpay).
|
22
|
+
* [#1153](https://github.com/ruby-grape/grape/pull/1153): Fixes boolean declaration in an external file - [@towanda](https://github.com/towanda).
|
23
|
+
* [#1142](https://github.com/ruby-grape/grape/pull/1142): Makes #declared unavailable to before filters - [@jrforrest](https://github.com/jrforrest).
|
24
|
+
* [#1114](https://github.com/ruby-grape/grape/pull/1114): Fix regression which broke identical endpoints with different versions - [@suan](https://github.com/suan).
|
25
|
+
* [#1109](https://github.com/ruby-grape/grape/pull/1109): Memoize Virtus attribute and fix memory leak - [@marshall-lee](https://github.com/marshall-lee).
|
26
|
+
* [#1101](https://github.com/intridea/grape/pull/1101): Fix: Incorrect media-type `Accept` header now correctly returns 406 with `strict: true` - [@elliotlarson](https://github.com/elliotlarson).
|
27
|
+
* [#1108](https://github.com/ruby-grape/grape/pull/1039): Raise a warning when `desc` is called with options hash and block - [@rngtng](https://github.com/rngtng).
|
28
|
+
|
1
29
|
0.13.0 (8/10/2015)
|
2
30
|
==================
|
3
31
|
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
grape (0.14.0)
|
5
|
+
activesupport
|
6
|
+
builder
|
7
|
+
hashie (>= 2.1.0)
|
8
|
+
multi_json (>= 1.3.2)
|
9
|
+
multi_xml (>= 0.5.2)
|
10
|
+
rack (>= 1.3.0)
|
11
|
+
rack-accept
|
12
|
+
rack-mount
|
13
|
+
virtus (>= 1.0.0)
|
14
|
+
|
15
|
+
GEM
|
16
|
+
remote: https://rubygems.org/
|
17
|
+
specs:
|
18
|
+
activesupport (4.2.5)
|
19
|
+
i18n (~> 0.7)
|
20
|
+
json (~> 1.7, >= 1.7.7)
|
21
|
+
minitest (~> 5.1)
|
22
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
23
|
+
tzinfo (~> 1.1)
|
24
|
+
appraisal (2.1.0)
|
25
|
+
bundler
|
26
|
+
rake
|
27
|
+
thor (>= 0.14.0)
|
28
|
+
ast (2.1.0)
|
29
|
+
astrolabe (1.3.1)
|
30
|
+
parser (~> 2.2)
|
31
|
+
axiom-types (0.1.1)
|
32
|
+
descendants_tracker (~> 0.0.4)
|
33
|
+
ice_nine (~> 0.11.0)
|
34
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
35
|
+
benchmark-ips (2.3.0)
|
36
|
+
builder (3.2.2)
|
37
|
+
coderay (1.1.0)
|
38
|
+
coercible (1.0.0)
|
39
|
+
descendants_tracker (~> 0.0.1)
|
40
|
+
cookiejar (0.3.2)
|
41
|
+
descendants_tracker (0.0.4)
|
42
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
43
|
+
diff-lcs (1.2.5)
|
44
|
+
equalizer (0.0.11)
|
45
|
+
ffi (1.9.10)
|
46
|
+
formatador (0.2.5)
|
47
|
+
git-version-bump (0.15.1)
|
48
|
+
grape-entity (0.4.8)
|
49
|
+
activesupport
|
50
|
+
multi_json (>= 1.3.2)
|
51
|
+
guard (2.13.0)
|
52
|
+
formatador (>= 0.2.4)
|
53
|
+
listen (>= 2.7, <= 4.0)
|
54
|
+
lumberjack (~> 1.0)
|
55
|
+
nenv (~> 0.1)
|
56
|
+
notiffany (~> 0.0)
|
57
|
+
pry (>= 0.9.12)
|
58
|
+
shellany (~> 0.0)
|
59
|
+
thor (>= 0.18.1)
|
60
|
+
guard-compat (1.2.1)
|
61
|
+
guard-rspec (4.6.4)
|
62
|
+
guard (~> 2.1)
|
63
|
+
guard-compat (~> 1.1)
|
64
|
+
rspec (>= 2.99.0, < 4.0)
|
65
|
+
guard-rubocop (1.2.0)
|
66
|
+
guard (~> 2.0)
|
67
|
+
rubocop (~> 0.20)
|
68
|
+
hashie (3.4.3)
|
69
|
+
i18n (0.7.0)
|
70
|
+
ice_nine (0.11.1)
|
71
|
+
json (1.8.3)
|
72
|
+
listen (3.0.5)
|
73
|
+
rb-fsevent (>= 0.9.3)
|
74
|
+
rb-inotify (>= 0.9)
|
75
|
+
lumberjack (1.0.9)
|
76
|
+
maruku (0.7.2)
|
77
|
+
method_source (0.8.2)
|
78
|
+
mime-types (2.99)
|
79
|
+
minitest (5.8.3)
|
80
|
+
multi_json (1.11.2)
|
81
|
+
multi_xml (0.5.5)
|
82
|
+
nenv (0.2.0)
|
83
|
+
notiffany (0.0.8)
|
84
|
+
nenv (~> 0.1)
|
85
|
+
shellany (~> 0.0)
|
86
|
+
parser (2.2.3.0)
|
87
|
+
ast (>= 1.1, < 3.0)
|
88
|
+
powerpack (0.1.1)
|
89
|
+
pry (0.10.3)
|
90
|
+
coderay (~> 1.1.0)
|
91
|
+
method_source (~> 0.8.1)
|
92
|
+
slop (~> 3.4)
|
93
|
+
rack (1.6.4)
|
94
|
+
rack-accept (0.4.5)
|
95
|
+
rack (>= 0.4)
|
96
|
+
rack-contrib (1.4.0)
|
97
|
+
git-version-bump (~> 0.15)
|
98
|
+
rack (~> 1.4)
|
99
|
+
rack-mount (0.8.3)
|
100
|
+
rack (>= 1.0.0)
|
101
|
+
rack-test (0.6.3)
|
102
|
+
rack (>= 1.0)
|
103
|
+
rainbow (2.0.0)
|
104
|
+
rake (10.4.2)
|
105
|
+
rb-fsevent (0.9.6)
|
106
|
+
rb-inotify (0.9.5)
|
107
|
+
ffi (>= 0.5.0)
|
108
|
+
rspec (3.4.0)
|
109
|
+
rspec-core (~> 3.4.0)
|
110
|
+
rspec-expectations (~> 3.4.0)
|
111
|
+
rspec-mocks (~> 3.4.0)
|
112
|
+
rspec-core (3.4.1)
|
113
|
+
rspec-support (~> 3.4.0)
|
114
|
+
rspec-expectations (3.4.0)
|
115
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
116
|
+
rspec-support (~> 3.4.0)
|
117
|
+
rspec-mocks (3.4.0)
|
118
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
119
|
+
rspec-support (~> 3.4.0)
|
120
|
+
rspec-support (3.4.1)
|
121
|
+
rubocop (0.35.1)
|
122
|
+
astrolabe (~> 1.3)
|
123
|
+
parser (>= 2.2.3.0, < 3.0)
|
124
|
+
powerpack (~> 0.1)
|
125
|
+
rainbow (>= 1.99.1, < 3.0)
|
126
|
+
ruby-progressbar (~> 1.7)
|
127
|
+
tins (<= 1.6.0)
|
128
|
+
ruby-progressbar (1.7.5)
|
129
|
+
shellany (0.0.1)
|
130
|
+
slop (3.6.0)
|
131
|
+
thor (0.19.1)
|
132
|
+
thread_safe (0.3.5)
|
133
|
+
tins (1.6.0)
|
134
|
+
tzinfo (1.2.2)
|
135
|
+
thread_safe (~> 0.1)
|
136
|
+
virtus (1.0.5)
|
137
|
+
axiom-types (~> 0.1)
|
138
|
+
coercible (~> 1.0)
|
139
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
140
|
+
equalizer (~> 0.0, >= 0.0.9)
|
141
|
+
yard (0.8.7.6)
|
142
|
+
|
143
|
+
PLATFORMS
|
144
|
+
ruby
|
145
|
+
|
146
|
+
DEPENDENCIES
|
147
|
+
appraisal
|
148
|
+
benchmark-ips
|
149
|
+
bundler
|
150
|
+
cookiejar
|
151
|
+
grape!
|
152
|
+
grape-entity (>= 0.4.4)
|
153
|
+
guard
|
154
|
+
guard-rspec
|
155
|
+
guard-rubocop
|
156
|
+
maruku
|
157
|
+
mime-types (< 3.0)
|
158
|
+
rack-contrib
|
159
|
+
rack-test
|
160
|
+
rake
|
161
|
+
rspec (~> 3.0)
|
162
|
+
rubocop (= 0.35.1)
|
163
|
+
yard
|
164
|
+
|
165
|
+
BUNDLED WITH
|
166
|
+
1.10.6
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
![grape logo](grape.png)
|
2
2
|
|
3
|
-
[![Gem Version](
|
4
|
-
[![Build Status](
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/grape.svg)](http://badge.fury.io/rb/grape)
|
4
|
+
[![Build Status](https://travis-ci.org/ruby-grape/grape.svg?branch=master)](https://travis-ci.org/ruby-grape/grape)
|
5
5
|
[![Dependency Status](https://gemnasium.com/ruby-grape/grape.svg)](https://gemnasium.com/ruby-grape/grape)
|
6
6
|
[![Code Climate](https://codeclimate.com/github/ruby-grape/grape.svg)](https://codeclimate.com/github/ruby-grape/grape)
|
7
7
|
[![Inline docs](http://inch-ci.org/github/ruby-grape/grape.svg)](http://inch-ci.org/github/ruby-grape/grape)
|
@@ -30,7 +30,11 @@
|
|
30
30
|
- [Include Missing](#include-missing)
|
31
31
|
- [Parameter Validation and Coercion](#parameter-validation-and-coercion)
|
32
32
|
- [Supported Parameter Types](#supported-parameter-types)
|
33
|
-
- [Custom Types](#custom-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)
|
34
38
|
- [Dependent Parameters](#dependent-parameters)
|
35
39
|
- [Built-in Validators](#built-in-validators)
|
36
40
|
- [Namespace Validation and Coercion](#namespace-validation-and-coercion)
|
@@ -59,7 +63,7 @@
|
|
59
63
|
- [API Data Formats](#api-data-formats)
|
60
64
|
- [RESTful Model Representations](#restful-model-representations)
|
61
65
|
- [Grape Entities](#grape-entities)
|
62
|
-
- [Hypermedia](#hypermedia)
|
66
|
+
- [Hypermedia and Roar](#hypermedia-and-roar)
|
63
67
|
- [Rabl](#rabl)
|
64
68
|
- [Active Model Serializers](#active-model-serializers)
|
65
69
|
- [Sending Raw or No Data](#sending-raw-or-no-data)
|
@@ -70,7 +74,7 @@
|
|
70
74
|
- [Anchoring](#anchoring)
|
71
75
|
- [Using Custom Middleware](#using-custom-middleware)
|
72
76
|
- [Rails Middleware](#rails-middleware)
|
73
|
-
|
77
|
+
- [Remote IP](#remote-ip)
|
74
78
|
- [Writing Tests](#writing-tests)
|
75
79
|
- [Writing Tests with Rack](#writing-tests-with-rack)
|
76
80
|
- [Writing Tests with Rails](#writing-tests-with-rails)
|
@@ -82,7 +86,6 @@
|
|
82
86
|
- [Active Support Instrumentation](#active-support-instrumentation)
|
83
87
|
- [Monitoring Products](#monitoring-products)
|
84
88
|
- [Contributing to Grape](#contributing-to-grape)
|
85
|
-
- [Hacking on Grape](#hacking-on-grape)
|
86
89
|
- [License](#license)
|
87
90
|
- [Copyright](#copyright)
|
88
91
|
|
@@ -96,7 +99,7 @@ content negotiation, versioning and much more.
|
|
96
99
|
|
97
100
|
## Stable Release
|
98
101
|
|
99
|
-
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).
|
100
103
|
Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
|
101
104
|
|
102
105
|
## Project Resources
|
@@ -140,20 +143,20 @@ module Twitter
|
|
140
143
|
end
|
141
144
|
|
142
145
|
resource :statuses do
|
143
|
-
desc
|
146
|
+
desc 'Return a public timeline.'
|
144
147
|
get :public_timeline do
|
145
148
|
Status.limit(20)
|
146
149
|
end
|
147
150
|
|
148
|
-
desc
|
151
|
+
desc 'Return a personal timeline.'
|
149
152
|
get :home_timeline do
|
150
153
|
authenticate!
|
151
154
|
current_user.statuses.limit(20)
|
152
155
|
end
|
153
156
|
|
154
|
-
desc
|
157
|
+
desc 'Return a status.'
|
155
158
|
params do
|
156
|
-
requires :id, type: Integer, desc:
|
159
|
+
requires :id, type: Integer, desc: 'Status id.'
|
157
160
|
end
|
158
161
|
route_param :id do
|
159
162
|
get do
|
@@ -161,9 +164,9 @@ module Twitter
|
|
161
164
|
end
|
162
165
|
end
|
163
166
|
|
164
|
-
desc
|
167
|
+
desc 'Create a status.'
|
165
168
|
params do
|
166
|
-
requires :status, type: String, desc:
|
169
|
+
requires :status, type: String, desc: 'Your status.'
|
167
170
|
end
|
168
171
|
post do
|
169
172
|
authenticate!
|
@@ -173,10 +176,10 @@ module Twitter
|
|
173
176
|
})
|
174
177
|
end
|
175
178
|
|
176
|
-
desc
|
179
|
+
desc 'Update a status.'
|
177
180
|
params do
|
178
|
-
requires :id, type: String, desc:
|
179
|
-
requires :status, type: String, desc:
|
181
|
+
requires :id, type: String, desc: 'Status ID.'
|
182
|
+
requires :status, type: String, desc: 'Your status.'
|
180
183
|
end
|
181
184
|
put ':id' do
|
182
185
|
authenticate!
|
@@ -186,9 +189,9 @@ module Twitter
|
|
186
189
|
})
|
187
190
|
end
|
188
191
|
|
189
|
-
desc
|
192
|
+
desc 'Delete a status.'
|
190
193
|
params do
|
191
|
-
requires :id, type: String, desc:
|
194
|
+
requires :id, type: String, desc: 'Status ID.'
|
192
195
|
end
|
193
196
|
delete ':id' do
|
194
197
|
authenticate!
|
@@ -248,13 +251,13 @@ require 'grape'
|
|
248
251
|
|
249
252
|
class API < Grape::API
|
250
253
|
get :hello do
|
251
|
-
{ hello:
|
254
|
+
{ hello: 'world' }
|
252
255
|
end
|
253
256
|
end
|
254
257
|
|
255
258
|
class Web < Sinatra::Base
|
256
259
|
get '/' do
|
257
|
-
|
260
|
+
'Hello world.'
|
258
261
|
end
|
259
262
|
end
|
260
263
|
|
@@ -283,7 +286,7 @@ Additionally, if the version of your Rails is 4.0+ and the application uses the
|
|
283
286
|
|
284
287
|
```ruby
|
285
288
|
# Gemfile
|
286
|
-
gem
|
289
|
+
gem 'hashie-forbidden_attributes'
|
287
290
|
```
|
288
291
|
|
289
292
|
See [below](#reloading-api-changes-in-development) for additional code that enables reloading of API changes in development.
|
@@ -329,6 +332,14 @@ Using this versioning strategy, clients should pass the desired version in the U
|
|
329
332
|
version 'v1', using: :header, vendor: 'twitter'
|
330
333
|
```
|
331
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
|
+
|
332
343
|
Using this versioning strategy, clients should pass the desired version in the HTTP `Accept` head.
|
333
344
|
|
334
345
|
curl -H Accept:application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
|
@@ -342,29 +353,6 @@ When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is ret
|
|
342
353
|
option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route
|
343
354
|
matches.
|
344
355
|
|
345
|
-
### HTTP Status Code
|
346
|
-
|
347
|
-
By default Grape returns a 200 status code for `GET`-Requests and 201 for `POST`-Requests.
|
348
|
-
You can use `status` to query and set the actual HTTP Status Code
|
349
|
-
|
350
|
-
```ruby
|
351
|
-
post do
|
352
|
-
status 202
|
353
|
-
|
354
|
-
if status == 200
|
355
|
-
# do some thing
|
356
|
-
end
|
357
|
-
end
|
358
|
-
```
|
359
|
-
|
360
|
-
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)
|
361
|
-
|
362
|
-
```ruby
|
363
|
-
post do
|
364
|
-
status :no_content
|
365
|
-
end
|
366
|
-
```
|
367
|
-
|
368
356
|
### Accept-Version Header
|
369
357
|
|
370
358
|
```ruby
|
@@ -394,7 +382,7 @@ either in the URL query string or in the request body.
|
|
394
382
|
The default name for the query parameter is 'apiver' but can be specified using the `:parameter` option.
|
395
383
|
|
396
384
|
```ruby
|
397
|
-
version 'v1', using: :param, parameter:
|
385
|
+
version 'v1', using: :param, parameter: 'v'
|
398
386
|
```
|
399
387
|
|
400
388
|
curl http://localhost:9292/statuses/public_timeline?v=v1
|
@@ -405,21 +393,21 @@ version 'v1', using: :param, parameter: "v"
|
|
405
393
|
You can add a description to API methods and namespaces.
|
406
394
|
|
407
395
|
```ruby
|
408
|
-
desc
|
396
|
+
desc 'Returns your public timeline.' do
|
409
397
|
detail 'more details'
|
410
398
|
params API::Entities::Status.documentation
|
411
399
|
success API::Entities::Entity
|
412
|
-
failure [[401, 'Unauthorized',
|
400
|
+
failure [[401, 'Unauthorized', 'Entities::Error']]
|
413
401
|
named 'My named route'
|
414
|
-
headers
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
402
|
+
headers XAuthToken: {
|
403
|
+
description: 'Valdates your identity',
|
404
|
+
required: true
|
405
|
+
},
|
406
|
+
XOptionalHeader: {
|
407
|
+
description: 'Not really needed',
|
420
408
|
required: false
|
421
|
-
|
422
|
-
|
409
|
+
}
|
410
|
+
|
423
411
|
end
|
424
412
|
get :public_timeline do
|
425
413
|
Status.limit(20)
|
@@ -472,7 +460,7 @@ curl --form image_file='@image.jpg;type=image/jpg' http://localhost:9292/upload
|
|
472
460
|
The Grape endpoint:
|
473
461
|
|
474
462
|
```ruby
|
475
|
-
post
|
463
|
+
post 'upload' do
|
476
464
|
# file in params[:image_file]
|
477
465
|
end
|
478
466
|
```
|
@@ -485,19 +473,19 @@ In the case of conflict between either of:
|
|
485
473
|
|
486
474
|
route string parameters will have precedence.
|
487
475
|
|
488
|
-
|
476
|
+
### Declared
|
489
477
|
|
490
|
-
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:
|
491
479
|
|
492
480
|
````ruby
|
493
481
|
format :json
|
494
482
|
|
495
483
|
post 'users/signup' do
|
496
|
-
{
|
484
|
+
{ 'declared_params' => declared(params) }
|
497
485
|
end
|
498
486
|
````
|
499
487
|
|
500
|
-
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.
|
501
489
|
|
502
490
|
**Request**
|
503
491
|
|
@@ -527,7 +515,7 @@ params do
|
|
527
515
|
end
|
528
516
|
|
529
517
|
post 'users/signup' do
|
530
|
-
{
|
518
|
+
{ 'declared_params' => declared(params) }
|
531
519
|
end
|
532
520
|
````
|
533
521
|
|
@@ -550,15 +538,19 @@ curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d
|
|
550
538
|
}
|
551
539
|
````
|
552
540
|
|
553
|
-
|
541
|
+
The returned hash is a `Hashie::Mash` instance, allowing you to access parameters via dot notation:
|
554
542
|
|
555
543
|
```ruby
|
556
|
-
declared(params).user == declared(params)[
|
544
|
+
declared(params).user == declared(params)['user']
|
557
545
|
```
|
558
546
|
|
559
|
-
#### Include missing
|
560
547
|
|
561
|
-
|
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:
|
562
554
|
|
563
555
|
````ruby
|
564
556
|
format :json
|
@@ -569,7 +561,7 @@ params do
|
|
569
561
|
end
|
570
562
|
|
571
563
|
post 'users/signup' do
|
572
|
-
{
|
564
|
+
{ 'declared_params' => declared(params, include_missing: false) }
|
573
565
|
end
|
574
566
|
````
|
575
567
|
|
@@ -619,7 +611,7 @@ params do
|
|
619
611
|
end
|
620
612
|
|
621
613
|
post 'users/signup' do
|
622
|
-
{
|
614
|
+
{ 'declared_params' => declared(params, include_missing: false) }
|
623
615
|
end
|
624
616
|
````
|
625
617
|
|
@@ -691,7 +683,7 @@ You can define validations and coercion options for your parameters using a `par
|
|
691
683
|
```ruby
|
692
684
|
params do
|
693
685
|
requires :id, type: Integer
|
694
|
-
optional :text, type: String, regexp:
|
686
|
+
optional :text, type: String, regexp: /\A[a-z]+\z/
|
695
687
|
group :media do
|
696
688
|
requires :url
|
697
689
|
end
|
@@ -735,7 +727,7 @@ params do
|
|
735
727
|
end
|
736
728
|
```
|
737
729
|
|
738
|
-
|
730
|
+
### Supported Parameter Types
|
739
731
|
|
740
732
|
The following are all valid types, supported out of the box by Grape:
|
741
733
|
|
@@ -749,14 +741,16 @@ The following are all valid types, supported out of the box by Grape:
|
|
749
741
|
* Boolean
|
750
742
|
* String
|
751
743
|
* Symbol
|
752
|
-
* Rack::Multipart::UploadedFile
|
744
|
+
* Rack::Multipart::UploadedFile (alias `File`)
|
745
|
+
* JSON
|
753
746
|
|
754
|
-
|
747
|
+
### Custom Types and Coercions
|
755
748
|
|
756
749
|
Aside from the default set of supported types listed above, any class can be
|
757
|
-
used as a type so long as
|
758
|
-
|
759
|
-
|
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.,
|
760
754
|
|
761
755
|
```ruby
|
762
756
|
class Color
|
@@ -783,7 +777,117 @@ get '/stuff' do
|
|
783
777
|
end
|
784
778
|
```
|
785
779
|
|
786
|
-
|
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
|
787
891
|
|
788
892
|
Parameters can be nested using `group` or by calling `requires` or `optional` with a block.
|
789
893
|
In the above example, this means `params[:media][:url]` is required along with `params[:id]`,
|
@@ -806,7 +910,7 @@ params do
|
|
806
910
|
end
|
807
911
|
```
|
808
912
|
|
809
|
-
|
913
|
+
### Dependent Parameters
|
810
914
|
|
811
915
|
Suppose some of your parameters are only relevant if another parameter is given;
|
812
916
|
Grape allows you to express this relationship through the `given` method in your
|
@@ -1008,14 +1112,14 @@ Namespaces allow parameter definitions and apply to every method within the name
|
|
1008
1112
|
```ruby
|
1009
1113
|
namespace :statuses do
|
1010
1114
|
params do
|
1011
|
-
requires :user_id, type: Integer, desc:
|
1115
|
+
requires :user_id, type: Integer, desc: 'A user ID.'
|
1012
1116
|
end
|
1013
|
-
namespace
|
1117
|
+
namespace ':user_id' do
|
1014
1118
|
desc "Retrieve a user's status."
|
1015
1119
|
params do
|
1016
|
-
requires :status_id, type: Integer, desc:
|
1120
|
+
requires :status_id, type: Integer, desc: 'A status ID.'
|
1017
1121
|
end
|
1018
|
-
get
|
1122
|
+
get ':status_id' do
|
1019
1123
|
User.find(params[:user_id]).statuses.find(params[:status_id])
|
1020
1124
|
end
|
1021
1125
|
end
|
@@ -1030,11 +1134,11 @@ You can conveniently define a route parameter as a namespace using `route_param`
|
|
1030
1134
|
```ruby
|
1031
1135
|
namespace :statuses do
|
1032
1136
|
route_param :id do
|
1033
|
-
desc
|
1137
|
+
desc 'Returns all replies for a status.'
|
1034
1138
|
get 'replies' do
|
1035
1139
|
Status.find(params[:id]).replies
|
1036
1140
|
end
|
1037
|
-
desc
|
1141
|
+
desc 'Returns a status.'
|
1038
1142
|
get do
|
1039
1143
|
Status.find(params[:id])
|
1040
1144
|
end
|
@@ -1047,8 +1151,8 @@ end
|
|
1047
1151
|
```ruby
|
1048
1152
|
class AlphaNumeric < Grape::Validations::Base
|
1049
1153
|
def validate_param!(attr_name, params)
|
1050
|
-
unless params[attr_name] =~
|
1051
|
-
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'
|
1052
1156
|
end
|
1053
1157
|
end
|
1054
1158
|
end
|
@@ -1160,7 +1264,7 @@ namespace :outer, requirements: { id: /[0-9]*/ } do
|
|
1160
1264
|
get :id do
|
1161
1265
|
end
|
1162
1266
|
|
1163
|
-
get
|
1267
|
+
get ':id/edit' do
|
1164
1268
|
end
|
1165
1269
|
end
|
1166
1270
|
```
|
@@ -1206,7 +1310,7 @@ class API < Grape::API
|
|
1206
1310
|
end
|
1207
1311
|
end
|
1208
1312
|
|
1209
|
-
desc
|
1313
|
+
desc 'Get collection'
|
1210
1314
|
params do
|
1211
1315
|
use :pagination # aliases: includes, use_scope
|
1212
1316
|
end
|
@@ -1236,7 +1340,7 @@ end
|
|
1236
1340
|
class API < Grape::API
|
1237
1341
|
helpers SharedParams
|
1238
1342
|
|
1239
|
-
desc
|
1343
|
+
desc 'Get collection.'
|
1240
1344
|
params do
|
1241
1345
|
use :period, :pagination
|
1242
1346
|
end
|
@@ -1266,7 +1370,7 @@ end
|
|
1266
1370
|
class API < Grape::API
|
1267
1371
|
helpers SharedParams
|
1268
1372
|
|
1269
|
-
desc
|
1373
|
+
desc 'Get a sorted collection.'
|
1270
1374
|
params do
|
1271
1375
|
use :order, order_by:%i(id created_at), default_order_by: :created_at, default_order: :asc
|
1272
1376
|
end
|
@@ -1335,6 +1439,29 @@ Specify an optional path.
|
|
1335
1439
|
cookies.delete :status_count, path: '/'
|
1336
1440
|
```
|
1337
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
|
+
|
1338
1465
|
## Redirecting
|
1339
1466
|
|
1340
1467
|
You can redirect to a new url temporarily (302) or permanently (301).
|
@@ -1419,7 +1546,7 @@ You can also return JSON formatted objects by raising error! and passing a hash
|
|
1419
1546
|
instead of a message.
|
1420
1547
|
|
1421
1548
|
```ruby
|
1422
|
-
error!({ error:
|
1549
|
+
error!({ error: 'unexpected error', detail: 'missing widget' }, 500)
|
1423
1550
|
```
|
1424
1551
|
|
1425
1552
|
You can present documented errors with a Grape entity using the the [grape-entity](https://github.com/ruby-grape/grape-entity) gem.
|
@@ -1459,7 +1586,7 @@ By default Grape returns a 500 status code from `error!`. You can change this wi
|
|
1459
1586
|
class API < Grape::API
|
1460
1587
|
default_error_status 400
|
1461
1588
|
get '/example' do
|
1462
|
-
error!
|
1589
|
+
error! 'This should have http status code 400'
|
1463
1590
|
end
|
1464
1591
|
end
|
1465
1592
|
```
|
@@ -1504,7 +1631,7 @@ Custom error formatters for existing and additional types can be defined with a
|
|
1504
1631
|
|
1505
1632
|
```ruby
|
1506
1633
|
class Twitter::API < Grape::API
|
1507
|
-
error_formatter :txt,
|
1634
|
+
error_formatter :txt, ->(message, backtrace, options, env) {
|
1508
1635
|
"error: #{message} from #{backtrace}"
|
1509
1636
|
}
|
1510
1637
|
end
|
@@ -1524,8 +1651,7 @@ class Twitter::API < Grape::API
|
|
1524
1651
|
end
|
1525
1652
|
```
|
1526
1653
|
|
1527
|
-
You can rescue all exceptions with a code block. The `error!` wrapper
|
1528
|
-
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.
|
1529
1655
|
|
1530
1656
|
```ruby
|
1531
1657
|
class Twitter::API < Grape::API
|
@@ -1541,19 +1667,17 @@ Optionally, you can set the format, status code and headers.
|
|
1541
1667
|
class Twitter::API < Grape::API
|
1542
1668
|
format :json
|
1543
1669
|
rescue_from :all do |e|
|
1544
|
-
error!({ error:
|
1670
|
+
error!({ error: 'Server error.' }, 500, { 'Content-Type' => 'text/error' })
|
1545
1671
|
end
|
1546
1672
|
end
|
1547
1673
|
```
|
1548
1674
|
|
1549
|
-
|
1550
|
-
You can also rescue specific exceptions with a code block and handle the Rack
|
1551
|
-
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.
|
1552
1676
|
|
1553
1677
|
```ruby
|
1554
1678
|
class Twitter::API < Grape::API
|
1555
1679
|
rescue_from :all do |e|
|
1556
|
-
Rack::Response.new([ e.message ], 500, {
|
1680
|
+
Rack::Response.new([ e.message ], 500, { 'Content-type' => 'text/error' }).finish
|
1557
1681
|
end
|
1558
1682
|
end
|
1559
1683
|
```
|
@@ -1607,9 +1731,15 @@ rescue_from RuntimeError, rescue_subclasses: false do |e|
|
|
1607
1731
|
end
|
1608
1732
|
```
|
1609
1733
|
|
1610
|
-
|
1734
|
+
The `rescue_from` block must return a `Rack::Response` object, call `error!` or re-raise an exception.
|
1735
|
+
|
1736
|
+
#### Unrescuable Exceptions
|
1737
|
+
|
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
|
1611
1741
|
|
1612
|
-
When mounted inside containers, such as Rails 3.x, errors
|
1742
|
+
When mounted inside containers, such as Rails 3.x, errors such as "404 Not Found" or
|
1613
1743
|
"406 Not Acceptable" will likely be handled and rendered by Rails handlers. For instance,
|
1614
1744
|
accessing a nonexistent route "/api/foo" raises a 404, which inside rails will ultimately
|
1615
1745
|
be translated to an `ActionController::RoutingError`, which most likely will get rendered
|
@@ -1751,11 +1881,11 @@ For example, the following API will let you upload arbitrary files and return th
|
|
1751
1881
|
|
1752
1882
|
```ruby
|
1753
1883
|
class Twitter::API < Grape::API
|
1754
|
-
post
|
1884
|
+
post 'attachment' do
|
1755
1885
|
filename = params[:file][:filename]
|
1756
1886
|
content_type MIME::Types.type_for(filename)[0].to_s
|
1757
1887
|
env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
|
1758
|
-
header
|
1888
|
+
header 'Content-Disposition', "attachment; filename*=UTF-8''#{URI.escape(filename)}"
|
1759
1889
|
params[:file][:tempfile].read
|
1760
1890
|
end
|
1761
1891
|
end
|
@@ -1808,7 +1938,7 @@ If you do not want this behavior, set the default error formatter with `default_
|
|
1808
1938
|
```ruby
|
1809
1939
|
class Twitter::API < Grape::API
|
1810
1940
|
format :json
|
1811
|
-
content_type :txt,
|
1941
|
+
content_type :txt, 'text/plain'
|
1812
1942
|
default_error_formatter :txt
|
1813
1943
|
end
|
1814
1944
|
```
|
@@ -1817,8 +1947,8 @@ Custom formatters for existing and additional types can be defined with a proc.
|
|
1817
1947
|
|
1818
1948
|
```ruby
|
1819
1949
|
class Twitter::API < Grape::API
|
1820
|
-
content_type :xls,
|
1821
|
-
formatter :xls,
|
1950
|
+
content_type :xls, 'application/vnd.ms-excel'
|
1951
|
+
formatter :xls, ->(object, env) { object.to_xls }
|
1822
1952
|
end
|
1823
1953
|
```
|
1824
1954
|
|
@@ -1832,7 +1962,7 @@ module XlsFormatter
|
|
1832
1962
|
end
|
1833
1963
|
|
1834
1964
|
class Twitter::API < Grape::API
|
1835
|
-
content_type :xls,
|
1965
|
+
content_type :xls, 'application/vnd.ms-excel'
|
1836
1966
|
formatter :xls, XlsFormatter
|
1837
1967
|
end
|
1838
1968
|
```
|
@@ -1845,6 +1975,11 @@ Built-in formatters are the following.
|
|
1845
1975
|
* `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`
|
1846
1976
|
* `:binary`: data will be returned "as is"
|
1847
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
|
+
|
1848
1983
|
### JSONP
|
1849
1984
|
|
1850
1985
|
Grape supports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the
|
@@ -1890,7 +2025,7 @@ by setting the `Content-Type` header.
|
|
1890
2025
|
```ruby
|
1891
2026
|
class API < Grape::API
|
1892
2027
|
get '/home_timeline_js' do
|
1893
|
-
content_type
|
2028
|
+
content_type 'application/javascript'
|
1894
2029
|
"var statuses = ...;"
|
1895
2030
|
end
|
1896
2031
|
end
|
@@ -1918,11 +2053,11 @@ end
|
|
1918
2053
|
```
|
1919
2054
|
|
1920
2055
|
```ruby
|
1921
|
-
content_type :txt,
|
1922
|
-
content_type :custom,
|
2056
|
+
content_type :txt, 'text/plain'
|
2057
|
+
content_type :custom, 'text/custom'
|
1923
2058
|
parser :custom, CustomParser
|
1924
2059
|
|
1925
|
-
put
|
2060
|
+
put 'value' do
|
1926
2061
|
params[:value]
|
1927
2062
|
end
|
1928
2063
|
```
|
@@ -1954,9 +2089,9 @@ module API
|
|
1954
2089
|
module Entities
|
1955
2090
|
class Status < Grape::Entity
|
1956
2091
|
expose :user_name
|
1957
|
-
expose :text, documentation: { type:
|
2092
|
+
expose :text, documentation: { type: 'string', desc: 'Status update text.' }
|
1958
2093
|
expose :ip, if: { type: :full }
|
1959
|
-
expose :user_type, :user_id, if:
|
2094
|
+
expose :user_type, :user_id, if: ->(status, options) { status.user.public? }
|
1960
2095
|
expose :digest { |status, options| Digest::MD5.hexdigest(status.txt) }
|
1961
2096
|
expose :replies, using: API::Status, as: :replies
|
1962
2097
|
end
|
@@ -2064,12 +2199,12 @@ end
|
|
2064
2199
|
|
2065
2200
|
### Hypermedia and Roar
|
2066
2201
|
|
2067
|
-
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.
|
2068
2203
|
|
2069
2204
|
### Rabl
|
2070
2205
|
|
2071
2206
|
You can use [Rabl](https://github.com/nesquena/rabl) templates with the help of the
|
2072
|
-
[grape-rabl](https://github.com/
|
2207
|
+
[grape-rabl](https://github.com/ruby-grape/grape-rabl) gem, which defines a custom Grape Rabl
|
2073
2208
|
formatter.
|
2074
2209
|
|
2075
2210
|
### Active Model Serializers
|
@@ -2172,7 +2307,7 @@ For registering a Middleware you need the following options:
|
|
2172
2307
|
* `label` - the name for your authenticator to use it later
|
2173
2308
|
* `MiddlewareClass` - the MiddlewareClass to use for authentication
|
2174
2309
|
* `option_lookup_proc` - A Proc with one Argument to lookup the options at
|
2175
|
-
runtime (return value is an `Array` as
|
2310
|
+
runtime (return value is an `Array` as Parameter for the Middleware).
|
2176
2311
|
|
2177
2312
|
Example:
|
2178
2313
|
|
@@ -2199,7 +2334,7 @@ Grape exposes arrays of API versions and compiled routes. Each route contains a
|
|
2199
2334
|
```ruby
|
2200
2335
|
class TwitterAPI < Grape::API
|
2201
2336
|
version 'v1'
|
2202
|
-
desc
|
2337
|
+
desc 'Includes custom settings.'
|
2203
2338
|
route_setting :custom, key: 'value'
|
2204
2339
|
get do
|
2205
2340
|
|
@@ -2223,11 +2358,11 @@ It's possible to retrieve the information about the current route from within an
|
|
2223
2358
|
|
2224
2359
|
```ruby
|
2225
2360
|
class MyAPI < Grape::API
|
2226
|
-
desc
|
2361
|
+
desc 'Returns a description of a parameter.'
|
2227
2362
|
params do
|
2228
|
-
requires :id, type: Integer, desc:
|
2363
|
+
requires :id, type: Integer, desc: 'Identity.'
|
2229
2364
|
end
|
2230
|
-
get
|
2365
|
+
get 'params/:id' do
|
2231
2366
|
route.route_params[params[:id]] # yields the parameter description
|
2232
2367
|
end
|
2233
2368
|
end
|
@@ -2268,7 +2403,7 @@ E.g. using `before`:
|
|
2268
2403
|
|
2269
2404
|
```ruby
|
2270
2405
|
before do
|
2271
|
-
header
|
2406
|
+
header 'X-Robots-Tag', 'noindex'
|
2272
2407
|
end
|
2273
2408
|
```
|
2274
2409
|
|
@@ -2393,12 +2528,12 @@ This will match all paths starting with '/statuses/'. There is one caveat though
|
|
2393
2528
|
the `params[:status]` parameter only holds the first part of the request url.
|
2394
2529
|
Luckily this can be circumvented by using the described above syntax for path
|
2395
2530
|
specification and using the `PATH_INFO` Rack environment variable, using
|
2396
|
-
`env[
|
2531
|
+
`env['PATH_INFO']`. This will hold everything that comes after the '/statuses/'
|
2397
2532
|
part.
|
2398
2533
|
|
2399
|
-
|
2534
|
+
## Using Custom Middleware
|
2400
2535
|
|
2401
|
-
|
2536
|
+
### Rails Middleware
|
2402
2537
|
|
2403
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.
|
2404
2539
|
You only have to implement the helpers to access the specific `env` variable.
|
@@ -2415,7 +2550,7 @@ class API < Grape::API
|
|
2415
2550
|
|
2416
2551
|
helpers do
|
2417
2552
|
def client_ip
|
2418
|
-
env[
|
2553
|
+
env['action_dispatch.remote_ip'].to_s
|
2419
2554
|
end
|
2420
2555
|
end
|
2421
2556
|
|
@@ -2445,20 +2580,32 @@ describe Twitter::API do
|
|
2445
2580
|
Twitter::API
|
2446
2581
|
end
|
2447
2582
|
|
2448
|
-
|
2449
|
-
|
2450
|
-
|
2451
|
-
|
2452
|
-
|
2453
|
-
expect(JSON.parse(last_response.body)).to eq []
|
2454
|
-
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 []
|
2455
2588
|
end
|
2456
|
-
|
2457
|
-
|
2458
|
-
|
2459
|
-
|
2460
|
-
|
2461
|
-
|
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
|
2462
2609
|
end
|
2463
2610
|
end
|
2464
2611
|
end
|
@@ -2476,8 +2623,8 @@ Airborne.configure do |config|
|
|
2476
2623
|
end
|
2477
2624
|
|
2478
2625
|
describe Twitter::API do
|
2479
|
-
|
2480
|
-
it
|
2626
|
+
context 'GET /api/statuses/:id' do
|
2627
|
+
it 'returns a status by id' do
|
2481
2628
|
status = Status.create!
|
2482
2629
|
get "/api/statuses/#{status.id}"
|
2483
2630
|
expect_json(status.as_json)
|
@@ -2489,7 +2636,7 @@ end
|
|
2489
2636
|
#### MiniTest
|
2490
2637
|
|
2491
2638
|
```ruby
|
2492
|
-
require
|
2639
|
+
require 'test_helper'
|
2493
2640
|
|
2494
2641
|
class Twitter::APITest < MiniTest::Test
|
2495
2642
|
include Rack::Test::Methods
|
@@ -2499,7 +2646,7 @@ class Twitter::APITest < MiniTest::Test
|
|
2499
2646
|
end
|
2500
2647
|
|
2501
2648
|
def test_get_api_statuses_public_timeline_returns_an_empty_array_of_statuses
|
2502
|
-
get
|
2649
|
+
get '/api/statuses/public_timeline'
|
2503
2650
|
assert last_response.ok?
|
2504
2651
|
assert_equal [], JSON.parse(last_response.body)
|
2505
2652
|
end
|
@@ -2518,15 +2665,15 @@ end
|
|
2518
2665
|
|
2519
2666
|
```ruby
|
2520
2667
|
describe Twitter::API do
|
2521
|
-
|
2522
|
-
it
|
2523
|
-
get
|
2668
|
+
context 'GET /api/statuses/public_timeline' do
|
2669
|
+
it 'returns an empty array of statuses' do
|
2670
|
+
get '/api/statuses/public_timeline'
|
2524
2671
|
expect(response.status).to eq(200)
|
2525
2672
|
expect(JSON.parse(response.body)).to eq []
|
2526
2673
|
end
|
2527
2674
|
end
|
2528
|
-
|
2529
|
-
it
|
2675
|
+
context 'GET /api/statuses/:id' do
|
2676
|
+
it 'returns a status by id' do
|
2530
2677
|
status = Status.create!
|
2531
2678
|
get "/api/statuses/#{status.id}"
|
2532
2679
|
expect(response.body).to eq status.to_json
|
@@ -2554,13 +2701,13 @@ class Twitter::APITest < ActiveSupport::TestCase
|
|
2554
2701
|
Rails.application
|
2555
2702
|
end
|
2556
2703
|
|
2557
|
-
test
|
2558
|
-
get
|
2704
|
+
test 'GET /api/statuses/public_timeline returns an empty array of statuses' do
|
2705
|
+
get '/api/statuses/public_timeline'
|
2559
2706
|
assert last_response.ok?
|
2560
2707
|
assert_equal [], JSON.parse(last_response.body)
|
2561
2708
|
end
|
2562
2709
|
|
2563
|
-
test
|
2710
|
+
test 'GET /api/statuses/:id returns a status by id' do
|
2564
2711
|
status = Status.create!
|
2565
2712
|
get "/api/statuses/#{status.id}"
|
2566
2713
|
assert_equal status.to_json, last_response.body
|
@@ -2605,15 +2752,15 @@ Add API paths to `config/application.rb`.
|
|
2605
2752
|
|
2606
2753
|
```ruby
|
2607
2754
|
# Auto-load API and its subdirectories
|
2608
|
-
config.paths.add File.join(
|
2609
|
-
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', '*')]
|
2610
2757
|
```
|
2611
2758
|
|
2612
2759
|
Create `config/initializers/reload_api.rb`.
|
2613
2760
|
|
2614
2761
|
```ruby
|
2615
2762
|
if Rails.env.development?
|
2616
|
-
ActiveSupport::Dependencies.explicitly_unloadable_constants <<
|
2763
|
+
ActiveSupport::Dependencies.explicitly_unloadable_constants << 'Twitter::API'
|
2617
2764
|
|
2618
2765
|
api_files = Dir[Rails.root.join('app', 'api', '**', '*.rb')]
|
2619
2766
|
api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
|
@@ -2653,13 +2800,15 @@ The execution of the main content block of the endpoint.
|
|
2653
2800
|
* *filters* - The filters being executed
|
2654
2801
|
* *type* - The type of filters (before, before_validation, after_validation, after)
|
2655
2802
|
|
2656
|
-
See the [ActiveSupport::Notifications documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html
|
2803
|
+
See the [ActiveSupport::Notifications documentation](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) for information on how to subscribe to these events.
|
2657
2804
|
|
2658
2805
|
### Monitoring Products
|
2659
2806
|
|
2660
|
-
Grape integrates with
|
2661
|
-
|
2662
|
-
|
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/)
|
2663
2812
|
|
2664
2813
|
## Contributing to Grape
|
2665
2814
|
|
@@ -2668,13 +2817,6 @@ features and discuss issues.
|
|
2668
2817
|
|
2669
2818
|
See [CONTRIBUTING](CONTRIBUTING.md).
|
2670
2819
|
|
2671
|
-
## Hacking on Grape
|
2672
|
-
|
2673
|
-
You can start hacking on Grape on
|
2674
|
-
[Nitrous.IO](https://www.nitrous.io/?utm_source=github.com&utm_campaign=grape&utm_medium=hackonnitrous) in a matter of seconds:
|
2675
|
-
|
2676
|
-
[![Hack ruby-grape/grape on Nitrous.IO](https://d3o0mnbgv6k92a.cloudfront.net/assets/hack-l-v1-3cc067e71372f6045e1949af9d96095b.png)](https://www.nitrous.io/hack_button?source=embed&runtime=rails&repo=intridea%2Fgrape&file_to_open=README.md)
|
2677
|
-
|
2678
2820
|
## License
|
2679
2821
|
|
2680
2822
|
MIT License. See LICENSE for details.
|