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.

Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +9 -4
  3. data/CHANGELOG.md +28 -0
  4. data/Gemfile +0 -1
  5. data/Gemfile.lock +166 -0
  6. data/README.md +305 -163
  7. data/Rakefile +30 -33
  8. data/UPGRADING.md +31 -0
  9. data/benchmark/simple.rb +27 -0
  10. data/gemfiles/rack_1.5.2.gemfile +13 -0
  11. data/gemfiles/rails_3.gemfile +2 -2
  12. data/gemfiles/rails_4.gemfile +1 -2
  13. data/grape.gemspec +5 -4
  14. data/lib/grape.rb +9 -5
  15. data/lib/grape/dsl/configuration.rb +5 -2
  16. data/lib/grape/dsl/helpers.rb +8 -3
  17. data/lib/grape/dsl/inside_route.rb +67 -44
  18. data/lib/grape/dsl/parameters.rb +21 -12
  19. data/lib/grape/dsl/request_response.rb +1 -1
  20. data/lib/grape/dsl/routing.rb +3 -4
  21. data/lib/grape/endpoint.rb +63 -28
  22. data/lib/grape/error_formatter/base.rb +6 -6
  23. data/lib/grape/exceptions/base.rb +5 -5
  24. data/lib/grape/exceptions/invalid_version_header.rb +10 -0
  25. data/lib/grape/formatter/serializable_hash.rb +3 -2
  26. data/lib/grape/locale/en.yml +4 -1
  27. data/lib/grape/middleware/auth/base.rb +2 -2
  28. data/lib/grape/middleware/auth/dsl.rb +1 -1
  29. data/lib/grape/middleware/auth/strategies.rb +1 -1
  30. data/lib/grape/middleware/base.rb +7 -4
  31. data/lib/grape/middleware/error.rb +3 -2
  32. data/lib/grape/middleware/filter.rb +1 -1
  33. data/lib/grape/middleware/formatter.rb +47 -44
  34. data/lib/grape/middleware/globals.rb +3 -3
  35. data/lib/grape/middleware/versioner/accept_version_header.rb +5 -7
  36. data/lib/grape/middleware/versioner/header.rb +113 -50
  37. data/lib/grape/middleware/versioner/param.rb +5 -8
  38. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +20 -0
  39. data/lib/grape/middleware/versioner/path.rb +3 -6
  40. data/lib/grape/path.rb +3 -3
  41. data/lib/grape/request.rb +40 -0
  42. data/lib/grape/util/content_types.rb +9 -9
  43. data/lib/grape/util/env.rb +22 -0
  44. data/lib/grape/util/strict_hash_configuration.rb +2 -1
  45. data/lib/grape/validations/attributes_iterator.rb +8 -3
  46. data/lib/grape/validations/params_scope.rb +83 -15
  47. data/lib/grape/validations/types.rb +144 -0
  48. data/lib/grape/validations/types/build_coercer.rb +53 -0
  49. data/lib/grape/validations/types/custom_type_coercer.rb +183 -0
  50. data/lib/grape/validations/types/file.rb +28 -0
  51. data/lib/grape/validations/types/json.rb +65 -0
  52. data/lib/grape/validations/types/multiple_type_coercer.rb +76 -0
  53. data/lib/grape/validations/types/variant_collection_coercer.rb +59 -0
  54. data/lib/grape/validations/types/virtus_collection_patch.rb +16 -0
  55. data/lib/grape/validations/validators/all_or_none.rb +1 -1
  56. data/lib/grape/validations/validators/allow_blank.rb +3 -3
  57. data/lib/grape/validations/validators/base.rb +7 -0
  58. data/lib/grape/validations/validators/coerce.rb +31 -42
  59. data/lib/grape/validations/validators/presence.rb +2 -3
  60. data/lib/grape/validations/validators/regexp.rb +2 -4
  61. data/lib/grape/validations/validators/values.rb +3 -3
  62. data/lib/grape/version.rb +1 -1
  63. data/pkg/grape-0.13.0.gem +0 -0
  64. data/spec/grape/api/custom_validations_spec.rb +5 -4
  65. data/spec/grape/api/deeply_included_options_spec.rb +7 -7
  66. data/spec/grape/api/nested_helpers_spec.rb +4 -2
  67. data/spec/grape/api/shared_helpers_spec.rb +8 -8
  68. data/spec/grape/api_spec.rb +88 -54
  69. data/spec/grape/dsl/configuration_spec.rb +13 -0
  70. data/spec/grape/dsl/helpers_spec.rb +16 -2
  71. data/spec/grape/dsl/inside_route_spec.rb +3 -2
  72. data/spec/grape/dsl/parameters_spec.rb +0 -6
  73. data/spec/grape/dsl/routing_spec.rb +1 -1
  74. data/spec/grape/endpoint_spec.rb +61 -20
  75. data/spec/grape/entity_spec.rb +10 -8
  76. data/spec/grape/exceptions/invalid_accept_header_spec.rb +1 -15
  77. data/spec/grape/integration/rack_spec.rb +3 -2
  78. data/spec/grape/middleware/base_spec.rb +7 -5
  79. data/spec/grape/middleware/error_spec.rb +16 -15
  80. data/spec/grape/middleware/exception_spec.rb +45 -43
  81. data/spec/grape/middleware/formatter_spec.rb +34 -0
  82. data/spec/grape/middleware/versioner/header_spec.rb +79 -47
  83. data/spec/grape/path_spec.rb +10 -10
  84. data/spec/grape/presenters/presenter_spec.rb +2 -2
  85. data/spec/grape/request_spec.rb +100 -0
  86. data/spec/grape/validations/params_scope_spec.rb +11 -9
  87. data/spec/grape/validations/types_spec.rb +95 -0
  88. data/spec/grape/validations/validators/coerce_spec.rb +335 -2
  89. data/spec/grape/validations/validators/values_spec.rb +15 -15
  90. data/spec/grape/validations_spec.rb +53 -24
  91. data/spec/shared/versioning_examples.rb +2 -2
  92. data/spec/spec_helper.rb +0 -1
  93. data/spec/support/versioned_helpers.rb +2 -2
  94. metadata +51 -13
  95. data/.gitignore +0 -46
  96. data/.rspec +0 -2
  97. data/.rubocop.yml +0 -7
  98. data/.rubocop_todo.yml +0 -84
  99. data/.travis.yml +0 -20
  100. data/.yardopts +0 -2
  101. data/lib/grape/http/request.rb +0 -35
  102. data/lib/grape/util/parameter_types.rb +0 -58
  103. 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: d107e00ab83d74d77a9860e54a64c918c1825b4a
4
- data.tar.gz: 3c98df9a1d4a2cca9db63c73215d5def5420e4e0
3
+ metadata.gz: 3bc83c8aa5374d62068477b48daebd22f10c1761
4
+ data.tar.gz: a91b6bf2854b1476b9565e3303efa4a67e431b17
5
5
  SHA512:
6
- metadata.gz: 1da0c2dea94229bf87671394b43d2d6c903024d5c8ab41a27eb93ef1c304dd5e253c2bc4b48374c87292335ede93ba91e034ee039f1ede1abd0b068d21f796af
7
- data.tar.gz: a0ae9bba790064e8a98df12e6575efa130494b7c50b7a40ae3f32cfd95db14fb7f7049d07bf3afbf4f090f20c1a069989c4dada45f41aa1d99d09b7b6e47ab65
6
+ metadata.gz: 555a5eb54fc665f51ae44d4e3103e2c9b4ce92c3fc594c20cbb4001efdbe6515e95200fabe30455085b0a7b3ccaf178dbc1bb2ba75c6bdb5100a75521341614c
7
+ data.tar.gz: e66803d3c8454744ea814d2d68e0e5fccaab9c544ddfeacf27c64b68aacd36821612875fa263b2d844c307e6dacc48655f98a594c4ecb1f87d595441abf56ee9
data/Appraisals CHANGED
@@ -1,7 +1,12 @@
1
- appraise "rails-3" do
2
- gem "rails", "3.2.19"
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 "rails-4" do
6
- gem "rails", "4.1.6"
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
@@ -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
@@ -3,7 +3,6 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  group :development, :test do
6
- gem 'rubocop', '~> 0.31.0'
7
6
  gem 'guard'
8
7
  gem 'guard-rspec'
9
8
  gem 'guard-rubocop'
@@ -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](http://img.shields.io/gem/v/grape.svg)](http://badge.fury.io/rb/grape)
4
- [![Build Status](http://img.shields.io/travis/ruby-grape/grape.svg)](https://travis-ci.org/ruby-grape/grape)
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
- - [Remote IP](#remote-ip)
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 Grape, 0.13.0.
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 "Return a public timeline."
146
+ desc 'Return a public timeline.'
144
147
  get :public_timeline do
145
148
  Status.limit(20)
146
149
  end
147
150
 
148
- desc "Return a personal timeline."
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 "Return a status."
157
+ desc 'Return a status.'
155
158
  params do
156
- requires :id, type: Integer, desc: "Status id."
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 "Create a status."
167
+ desc 'Create a status.'
165
168
  params do
166
- requires :status, type: String, desc: "Your status."
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 "Update a status."
179
+ desc 'Update a status.'
177
180
  params do
178
- requires :id, type: String, desc: "Status ID."
179
- requires :status, type: String, desc: "Your status."
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 "Delete a status."
192
+ desc 'Delete a status.'
190
193
  params do
191
- requires :id, type: String, desc: "Status ID."
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: "world" }
254
+ { hello: 'world' }
252
255
  end
253
256
  end
254
257
 
255
258
  class Web < Sinatra::Base
256
259
  get '/' do
257
- "Hello world."
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 "hashie-forbidden_attributes"
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: "v"
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 "Returns your public timeline." do
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', "Entities::Error"]]
400
+ failure [[401, 'Unauthorized', 'Entities::Error']]
413
401
  named 'My named route'
414
- headers [XAuthToken: {
415
- description: 'Valdates your identity',
416
- required: true
417
- },
418
- XOptionalHeader: {
419
- description: 'Not really needed',
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 "upload" do
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
- #### Declared
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. Let's have the following api:
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
- { "declared_params" => declared(params) }
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
- { "declared_params" => declared(params) }
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
- Returned hash is a Hashie::Mash instance so you can access parameters via dot notation:
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)["user"]
544
+ declared(params).user == declared(params)['user']
557
545
  ```
558
546
 
559
- #### Include missing
560
547
 
561
- By default `declared(params)` returns parameters that has `nil` value. If you want to return only the parameters that have any value, you can use the `include_missing` option. By default it is `true`. Let's have the following api:
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
- { "declared_params" => declared(params, include_missing: false) }
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
- { "declared_params" => declared(params, include_missing: false) }
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: /^[a-z]+$/
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
- #### Supported Parameter Types
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
- #### Custom Types
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 it defines a class-level `parse` method. This method
758
- must take one string argument and return an instance of the correct type, or
759
- raise an exception to indicate the value was invalid. E.g.,
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
- #### Validation of Nested Parameters
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
- #### Dependent Parameters
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: "A user ID."
1115
+ requires :user_id, type: Integer, desc: 'A user ID.'
1012
1116
  end
1013
- namespace ":user_id" do
1117
+ namespace ':user_id' do
1014
1118
  desc "Retrieve a user's status."
1015
1119
  params do
1016
- requires :status_id, type: Integer, desc: "A status ID."
1120
+ requires :status_id, type: Integer, desc: 'A status ID.'
1017
1121
  end
1018
- get ":status_id" do
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 "Returns all replies for a status."
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 "Returns a status."
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] =~ /^[[:alnum:]]+$/
1051
- fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must consist of alpha-numeric characters"
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 ":id/edit" do
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 "Get collection"
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 "Get collection."
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 "Get a sorted collection."
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: "unexpected error", detail: "missing widget" }, 500)
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! "This should have http status code 400"
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, lambda { |message, backtrace, options, env|
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: "Server error.", 500, { 'Content-Type' => 'text/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, { "Content-type" => "text/error" }).finish
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
- #### Rails 3.x
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 like "404 Not Found" or
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 "attachment" do
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 "Content-Disposition", "attachment; filename*=UTF-8''#{URI.escape(filename)}"
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, "text/plain"
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, "application/vnd.ms-excel"
1821
- formatter :xls, lambda { |object, env| object.to_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, "application/vnd.ms-excel"
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 "application/javascript"
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, "text/plain"
1922
- content_type :custom, "text/custom"
2056
+ content_type :txt, 'text/plain'
2057
+ content_type :custom, 'text/custom'
1923
2058
  parser :custom, CustomParser
1924
2059
 
1925
- put "value" do
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: "string", desc: "Status update text." }
2092
+ expose :text, documentation: { type: 'string', desc: 'Status update text.' }
1958
2093
  expose :ip, if: { type: :full }
1959
- expose :user_type, :user_id, if: lambda { |status, options| status.user.public? }
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/dblock/grape-roar), which defines a custom JSON formatter and enables presenting entities with Grape's `present` keyword.
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/LTe/grape-rabl) gem, which defines a custom Grape Rabl
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 Paramter for the Middleware).
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 "Includes custom settings."
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 "Returns a description of a parameter."
2361
+ desc 'Returns a description of a parameter.'
2227
2362
  params do
2228
- requires :id, type: Integer, desc: "Identity."
2363
+ requires :id, type: Integer, desc: 'Identity.'
2229
2364
  end
2230
- get "params/:id" do
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 "X-Robots-Tag", "noindex"
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["PATH_INFO"]`. This will hold everything that comes after the '/statuses/'
2531
+ `env['PATH_INFO']`. This will hold everything that comes after the '/statuses/'
2397
2532
  part.
2398
2533
 
2399
- # Using Custom Middleware
2534
+ ## Using Custom Middleware
2400
2535
 
2401
- ## Rails Middleware
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["action_dispatch.remote_ip"].to_s
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
- describe Twitter::API do
2449
- describe "GET /api/statuses/public_timeline" do
2450
- it "returns an empty array of statuses" do
2451
- get "/api/statuses/public_timeline"
2452
- expect(last_response.status).to eq(200)
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
- describe "GET /api/statuses/:id" do
2457
- it "returns a status by id" do
2458
- status = Status.create!
2459
- get "/api/statuses/#{status.id}"
2460
- expect(last_response.body).to eq status.to_json
2461
- end
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
- describe "GET /api/statuses/:id" do
2480
- it "returns a status by id" do
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 "test_helper"
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 "/api/statuses/public_timeline"
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
- describe "GET /api/statuses/public_timeline" do
2522
- it "returns an empty array of statuses" do
2523
- get "/api/statuses/public_timeline"
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
- describe "GET /api/statuses/:id" do
2529
- it "returns a status by id" do
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 "GET /api/statuses/public_timeline returns an empty array of statuses" do
2558
- get "/api/statuses/public_timeline"
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 "GET /api/statuses/:id returns a status by id" do
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("app", "api"), glob: File.join("**", "*.rb")
2609
- config.autoload_paths += Dir[Rails.root.join("app", "api", "*")]
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 << "Twitter::API"
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] for information on how to subscribe to these events.
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 NewRelic via the
2661
- [newrelic-grape](https://github.com/flyerhzm/newrelic-grape) gem, and
2662
- with Librato Metrics with the [grape-librato](https://github.com/seanmoon/grape-librato) gem.
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.