grape-security 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +45 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +70 -0
  5. data/.travis.yml +18 -0
  6. data/.yardopts +2 -0
  7. data/CHANGELOG.md +314 -0
  8. data/CONTRIBUTING.md +118 -0
  9. data/Gemfile +21 -0
  10. data/Guardfile +14 -0
  11. data/LICENSE +20 -0
  12. data/README.md +1777 -0
  13. data/RELEASING.md +105 -0
  14. data/Rakefile +69 -0
  15. data/UPGRADING.md +124 -0
  16. data/grape-security.gemspec +39 -0
  17. data/grape.png +0 -0
  18. data/lib/grape.rb +99 -0
  19. data/lib/grape/api.rb +646 -0
  20. data/lib/grape/cookies.rb +39 -0
  21. data/lib/grape/endpoint.rb +533 -0
  22. data/lib/grape/error_formatter/base.rb +31 -0
  23. data/lib/grape/error_formatter/json.rb +15 -0
  24. data/lib/grape/error_formatter/txt.rb +16 -0
  25. data/lib/grape/error_formatter/xml.rb +15 -0
  26. data/lib/grape/exceptions/base.rb +66 -0
  27. data/lib/grape/exceptions/incompatible_option_values.rb +10 -0
  28. data/lib/grape/exceptions/invalid_formatter.rb +10 -0
  29. data/lib/grape/exceptions/invalid_versioner_option.rb +10 -0
  30. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +10 -0
  31. data/lib/grape/exceptions/missing_mime_type.rb +10 -0
  32. data/lib/grape/exceptions/missing_option.rb +10 -0
  33. data/lib/grape/exceptions/missing_vendor_option.rb +10 -0
  34. data/lib/grape/exceptions/unknown_options.rb +10 -0
  35. data/lib/grape/exceptions/unknown_validator.rb +10 -0
  36. data/lib/grape/exceptions/validation.rb +26 -0
  37. data/lib/grape/exceptions/validation_errors.rb +43 -0
  38. data/lib/grape/formatter/base.rb +31 -0
  39. data/lib/grape/formatter/json.rb +12 -0
  40. data/lib/grape/formatter/serializable_hash.rb +35 -0
  41. data/lib/grape/formatter/txt.rb +11 -0
  42. data/lib/grape/formatter/xml.rb +12 -0
  43. data/lib/grape/http/request.rb +26 -0
  44. data/lib/grape/locale/en.yml +32 -0
  45. data/lib/grape/middleware/auth/base.rb +30 -0
  46. data/lib/grape/middleware/auth/basic.rb +13 -0
  47. data/lib/grape/middleware/auth/digest.rb +13 -0
  48. data/lib/grape/middleware/auth/oauth2.rb +83 -0
  49. data/lib/grape/middleware/base.rb +62 -0
  50. data/lib/grape/middleware/error.rb +89 -0
  51. data/lib/grape/middleware/filter.rb +17 -0
  52. data/lib/grape/middleware/formatter.rb +150 -0
  53. data/lib/grape/middleware/globals.rb +13 -0
  54. data/lib/grape/middleware/versioner.rb +32 -0
  55. data/lib/grape/middleware/versioner/accept_version_header.rb +67 -0
  56. data/lib/grape/middleware/versioner/header.rb +132 -0
  57. data/lib/grape/middleware/versioner/param.rb +42 -0
  58. data/lib/grape/middleware/versioner/path.rb +52 -0
  59. data/lib/grape/namespace.rb +23 -0
  60. data/lib/grape/parser/base.rb +29 -0
  61. data/lib/grape/parser/json.rb +11 -0
  62. data/lib/grape/parser/xml.rb +11 -0
  63. data/lib/grape/path.rb +70 -0
  64. data/lib/grape/route.rb +27 -0
  65. data/lib/grape/util/content_types.rb +18 -0
  66. data/lib/grape/util/deep_merge.rb +23 -0
  67. data/lib/grape/util/hash_stack.rb +120 -0
  68. data/lib/grape/validations.rb +322 -0
  69. data/lib/grape/validations/coerce.rb +63 -0
  70. data/lib/grape/validations/default.rb +25 -0
  71. data/lib/grape/validations/exactly_one_of.rb +26 -0
  72. data/lib/grape/validations/mutual_exclusion.rb +25 -0
  73. data/lib/grape/validations/presence.rb +16 -0
  74. data/lib/grape/validations/regexp.rb +12 -0
  75. data/lib/grape/validations/values.rb +23 -0
  76. data/lib/grape/version.rb +3 -0
  77. data/spec/grape/api_spec.rb +2571 -0
  78. data/spec/grape/endpoint_spec.rb +784 -0
  79. data/spec/grape/entity_spec.rb +324 -0
  80. data/spec/grape/exceptions/invalid_formatter_spec.rb +18 -0
  81. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +18 -0
  82. data/spec/grape/exceptions/missing_mime_type_spec.rb +18 -0
  83. data/spec/grape/exceptions/missing_option_spec.rb +18 -0
  84. data/spec/grape/exceptions/unknown_options_spec.rb +18 -0
  85. data/spec/grape/exceptions/unknown_validator_spec.rb +18 -0
  86. data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
  87. data/spec/grape/middleware/auth/basic_spec.rb +31 -0
  88. data/spec/grape/middleware/auth/digest_spec.rb +47 -0
  89. data/spec/grape/middleware/auth/oauth2_spec.rb +135 -0
  90. data/spec/grape/middleware/base_spec.rb +58 -0
  91. data/spec/grape/middleware/error_spec.rb +45 -0
  92. data/spec/grape/middleware/exception_spec.rb +184 -0
  93. data/spec/grape/middleware/formatter_spec.rb +258 -0
  94. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +121 -0
  95. data/spec/grape/middleware/versioner/header_spec.rb +302 -0
  96. data/spec/grape/middleware/versioner/param_spec.rb +58 -0
  97. data/spec/grape/middleware/versioner/path_spec.rb +44 -0
  98. data/spec/grape/middleware/versioner_spec.rb +22 -0
  99. data/spec/grape/path_spec.rb +229 -0
  100. data/spec/grape/util/hash_stack_spec.rb +132 -0
  101. data/spec/grape/validations/coerce_spec.rb +208 -0
  102. data/spec/grape/validations/default_spec.rb +123 -0
  103. data/spec/grape/validations/exactly_one_of_spec.rb +71 -0
  104. data/spec/grape/validations/mutual_exclusion_spec.rb +61 -0
  105. data/spec/grape/validations/presence_spec.rb +142 -0
  106. data/spec/grape/validations/regexp_spec.rb +40 -0
  107. data/spec/grape/validations/values_spec.rb +152 -0
  108. data/spec/grape/validations/zh-CN.yml +10 -0
  109. data/spec/grape/validations_spec.rb +994 -0
  110. data/spec/shared/versioning_examples.rb +121 -0
  111. data/spec/spec_helper.rb +26 -0
  112. data/spec/support/basic_auth_encode_helpers.rb +3 -0
  113. data/spec/support/content_type_helpers.rb +11 -0
  114. data/spec/support/versioned_helpers.rb +50 -0
  115. metadata +421 -0
@@ -0,0 +1,118 @@
1
+ Contributing to Grape
2
+ =====================
3
+
4
+ Grape is work of [hundreds of contributors](https://github.com/intridea/grape/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/intridea/grape/pulls), [propose features and discuss issues](https://github.com/intridea/grape/issues). When in doubt, ask a question in the [Grape Google Group](http://groups.google.com/group/ruby-grape).
5
+
6
+ #### Fork the Project
7
+
8
+ Fork the [project on Github](https://github.com/intridea/grape) and check out your copy.
9
+
10
+ ```
11
+ git clone https://github.com/contributor/grape.git
12
+ cd grape
13
+ git remote add upstream https://github.com/intridea/grape.git
14
+ ```
15
+
16
+ #### Create a Topic Branch
17
+
18
+ Make sure your fork is up-to-date and create a topic branch for your feature or bug fix.
19
+
20
+ ```
21
+ git checkout master
22
+ git pull upstream master
23
+ git checkout -b my-feature-branch
24
+ ```
25
+
26
+ #### Bundle Install and Test
27
+
28
+ Ensure that you can build the project and run tests.
29
+
30
+ ```
31
+ bundle install
32
+ bundle exec rake
33
+ ```
34
+
35
+ #### Write Tests
36
+
37
+ Try to write a test that reproduces the problem you're trying to fix or describes a feature that you want to build. Add to [spec/grape](spec/grape).
38
+
39
+ We definitely appreciate pull requests that highlight or reproduce a problem, even without a fix.
40
+
41
+ #### Write Code
42
+
43
+ Implement your feature or bug fix.
44
+
45
+ Ruby style is enforced with [Rubocop](https://github.com/bbatsov/rubocop), run `bundle exec rubocop` and fix any style issues highlighted.
46
+
47
+ Make sure that `bundle exec rake` completes without errors.
48
+
49
+ #### Write Documentation
50
+
51
+ Document any external behavior in the [README](README.md).
52
+
53
+ #### Update Changelog
54
+
55
+ Add a line to [CHANGELOG](CHANGELOG.md) under *Next Release*. Make it look like every other line, including your name and link to your Github account.
56
+
57
+ #### Commit Changes
58
+
59
+ Make sure git knows your name and email address:
60
+
61
+ ```
62
+ git config --global user.name "Your Name"
63
+ git config --global user.email "contributor@example.com"
64
+ ```
65
+
66
+ Writing good commit logs is important. A commit log should describe what changed and why.
67
+
68
+ ```
69
+ git add ...
70
+ git commit
71
+ ```
72
+
73
+ #### Push
74
+
75
+ ```
76
+ git push origin my-feature-branch
77
+ ```
78
+
79
+ #### Make a Pull Request
80
+
81
+ Go to https://github.com/contributor/grape and select your feature branch. Click the 'Pull Request' button and fill out the form. Pull requests are usually reviewed within a few days.
82
+
83
+ #### Rebase
84
+
85
+ If you've been working on a change for a while, rebase with upstream/master.
86
+
87
+ ```
88
+ git fetch upstream
89
+ git rebase upstream/master
90
+ git push origin my-feature-branch -f
91
+ ```
92
+
93
+ #### Update CHANGELOG Again
94
+
95
+ Update the [CHANGELOG](CHANGELOG.md) with the pull request number. A typical entry looks as follows.
96
+
97
+ ```
98
+ * [#123](https://github.com/intridea/grape/pull/123): Reticulated splines - [@contributor](https://github.com/contributor).
99
+ ```
100
+
101
+ Amend your previous commit and force push the changes.
102
+
103
+ ```
104
+ git commit --amend
105
+ git push origin my-feature-branch -f
106
+ ```
107
+
108
+ #### Check on Your Pull Request
109
+
110
+ Go back to your pull request after a few minutes and see whether it passed muster with Travis-CI. Everything should look green, otherwise fix issues and amend your commit as described above.
111
+
112
+ #### Be Patient
113
+
114
+ It's likely that your change will not be merged and that the nitpicky maintainers will ask you to do more, or fix seemingly benign problems. Hang on there!
115
+
116
+ #### Thank You
117
+
118
+ Please do know that we really appreciate and value your time and work. We love you, really.
data/Gemfile ADDED
@@ -0,0 +1,21 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'rspec', '~> 2.14.1'
7
+ gem 'rack-test', '~> 0.6.2', require: 'rack/test'
8
+ gem 'cookiejar'
9
+ gem 'rack-contrib'
10
+ gem 'rubocop', '~> 0.20.1'
11
+ gem 'guard'
12
+ gem 'guard-rspec'
13
+ gem 'guard-rubocop'
14
+ gem 'mime-types'
15
+ end
16
+
17
+ platforms :rbx do
18
+ gem 'rubysl'
19
+ gem 'rubinius-developer_tools'
20
+ gem 'racc'
21
+ end
@@ -0,0 +1,14 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
10
+
11
+ guard :rubocop do
12
+ watch(%r{.+\.rb$})
13
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
14
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Michael Bleigh and Intridea, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,1777 @@
1
+ This is a Deliv only version of this gem locked at 0.8.0 with backported fixes for https://nvd.nist.gov/vuln/detail/CVE-2018-3769.
2
+
3
+ Edit this gem with extream care! - David
4
+
5
+ ![grape logo](grape.png)
6
+
7
+ [![Build Status](https://travis-ci.org/intridea/grape.png?branch=master)](http://travis-ci.org/intridea/grape) [![Code Climate](https://codeclimate.com/github/intridea/grape.png)](https://codeclimate.com/github/intridea/grape) [![Inline docs](http://inch-ci.org/github/intridea/grape.png)](http://inch-ci.org/github/intridea/grape) [![Dependency Status](https://www.versioneye.com/ruby/grape/0.7.0/badge.png)](https://www.versioneye.com/ruby/grape/0.7.0)
8
+
9
+ ## Table of Contents
10
+
11
+ - [What is Grape?](#what-is-grape)
12
+ - [Stable Release](#stable-release)
13
+ - [Project Resources](#project-resources)
14
+ - [Installation](#installation)
15
+ - [Basic Usage](#basic-usage)
16
+ - [Mounting](#mounting)
17
+ - [Rack](#rack)
18
+ - [Alongside Sinatra (or other frameworks)](#alongside-sinatra-or-other-frameworks)
19
+ - [Rails](#rails)
20
+ - [Modules](#modules)
21
+ - [Versioning](#versioning)
22
+ - [Path](#path)
23
+ - [Header](#header)
24
+ - [Accept-Version Header](#accept-version-header)
25
+ - [Param](#param)
26
+ - [Describing Methods](#describing-methods)
27
+ - [Parameters](#parameters)
28
+ - [Parameter Validation and Coercion](#parameter-validation-and-coercion)
29
+ - [Namespace Validation and Coercion](#namespace-validation-and-coercion)
30
+ - [Custom Validators](#custom-validators)
31
+ - [Validation Errors](#validation-errors)
32
+ - [I18n](#i18n)
33
+ - [Headers](#headers)
34
+ - [Routes](#routes)
35
+ - [Helpers](#helpers)
36
+ - [Parameter Documentation](#parameter-documentation)
37
+ - [Cookies](#cookies)
38
+ - [Redirecting](#redirecting)
39
+ - [Allowed Methods](#allowed-methods)
40
+ - [Raising Exceptions](#raising-exceptions)
41
+ - [Default Error HTTP Status Code](#default-error-http-status-code)
42
+ - [Handling 404](#handling-404)
43
+ - [Exception Handling](#exception-handling)
44
+ - [Rails 3.x](#rails-3x)
45
+ - [Logging](#logging)
46
+ - [API Formats](#api-formats)
47
+ - [JSONP](#jsonp)
48
+ - [CORS](#cors)
49
+ - [Content-type](#content-type)
50
+ - [API Data Formats](#api-data-formats)
51
+ - [RESTful Model Representations](#restful-model-representations)
52
+ - [Grape Entities](#grape-entities)
53
+ - [Hypermedia](#hypermedia)
54
+ - [Rabl](#rabl)
55
+ - [Active Model Serializers](#active-model-serializers)
56
+ - [Authentication](#authentication)
57
+ - [Describing and Inspecting an API](#describing-and-inspecting-an-api)
58
+ - [Current Route and Endpoint](#current-route-and-endpoint)
59
+ - [Before and After](#before-and-after)
60
+ - [Anchoring](#anchoring)
61
+ - [Writing Tests](#writing-tests)
62
+ - [Writing Tests with Rack](#writing-tests-with-rack)
63
+ - [Writing Tests with Rails](#writing-tests-with-rails)
64
+ - [Stubbing Helpers](#stubbing-helpers)
65
+ - [Reloading API Changes in Development](#reloading-api-changes-in-development)
66
+ - [Rails 3.x](#rails-3x)
67
+ - [Performance Monitoring](#performance-monitoring)
68
+ - [Contributing to Grape](#contributing-to-grape)
69
+ - [Hacking on Grape](#hacking-on-grape)
70
+ - [License](#license)
71
+ - [Copyright](#copyright)
72
+
73
+ ## What is Grape?
74
+
75
+ Grape is a REST-like API micro-framework for Ruby. It's designed to run on Rack
76
+ or complement existing web application frameworks such as Rails and Sinatra by
77
+ providing a simple DSL to easily develop RESTful APIs. It has built-in support
78
+ for common conventions, including multiple formats, subdomain/prefix restriction,
79
+ content negotiation, versioning and much more.
80
+
81
+ ## Stable Release
82
+
83
+ You're reading the documentation for the stable release of Grape, 0.8.0.
84
+ Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
85
+
86
+ ## Project Resources
87
+
88
+ * Need help? [Grape Google Group](http://groups.google.com/group/ruby-grape)
89
+ * [Grape Wiki](https://github.com/intridea/grape/wiki)
90
+
91
+ ## Installation
92
+
93
+ Grape is available as a gem, to install it just install the gem:
94
+
95
+ gem install grape
96
+
97
+ If you're using Bundler, add the gem to Gemfile.
98
+
99
+ gem 'grape'
100
+
101
+ Run `bundle install`.
102
+
103
+ ## Basic Usage
104
+
105
+ Grape APIs are Rack applications that are created by subclassing `Grape::API`.
106
+ Below is a simple example showing some of the more common features of Grape in
107
+ the context of recreating parts of the Twitter API.
108
+
109
+ ```ruby
110
+ module Twitter
111
+ class API < Grape::API
112
+ version 'v1', using: :header, vendor: 'twitter'
113
+ format :json
114
+
115
+ helpers do
116
+ def current_user
117
+ @current_user ||= User.authorize!(env)
118
+ end
119
+
120
+ def authenticate!
121
+ error!('401 Unauthorized', 401) unless current_user
122
+ end
123
+ end
124
+
125
+ resource :statuses do
126
+ desc "Return a public timeline."
127
+ get :public_timeline do
128
+ Status.limit(20)
129
+ end
130
+
131
+ desc "Return a personal timeline."
132
+ get :home_timeline do
133
+ authenticate!
134
+ current_user.statuses.limit(20)
135
+ end
136
+
137
+ desc "Return a status."
138
+ params do
139
+ requires :id, type: Integer, desc: "Status id."
140
+ end
141
+ route_param :id do
142
+ get do
143
+ Status.find(params[:id])
144
+ end
145
+ end
146
+
147
+ desc "Create a status."
148
+ params do
149
+ requires :status, type: String, desc: "Your status."
150
+ end
151
+ post do
152
+ authenticate!
153
+ Status.create!({
154
+ user: current_user,
155
+ text: params[:status]
156
+ })
157
+ end
158
+
159
+ desc "Update a status."
160
+ params do
161
+ requires :id, type: String, desc: "Status ID."
162
+ requires :status, type: String, desc: "Your status."
163
+ end
164
+ put ':id' do
165
+ authenticate!
166
+ current_user.statuses.find(params[:id]).update({
167
+ user: current_user,
168
+ text: params[:status]
169
+ })
170
+ end
171
+
172
+ desc "Delete a status."
173
+ params do
174
+ requires :id, type: String, desc: "Status ID."
175
+ end
176
+ delete ':id' do
177
+ authenticate!
178
+ current_user.statuses.find(params[:id]).destroy
179
+ end
180
+ end
181
+ end
182
+ end
183
+ ```
184
+
185
+ ## Mounting
186
+
187
+ ### Rack
188
+
189
+ The above sample creates a Rack application that can be run from a rackup `config.ru` file
190
+ with `rackup`:
191
+
192
+ ```ruby
193
+ run Twitter::API
194
+ ```
195
+
196
+ And would respond to the following routes:
197
+
198
+ GET /statuses/public_timeline(.json)
199
+ GET /statuses/home_timeline(.json)
200
+ GET /statuses/:id(.json)
201
+ POST /statuses(.json)
202
+ PUT /statuses/:id(.json)
203
+ DELETE /statuses/:id(.json)
204
+
205
+ Grape will also automatically respond to HEAD and OPTIONS for all GET, and just OPTIONS for all other routes.
206
+
207
+ ### Alongside Sinatra (or other frameworks)
208
+
209
+ If you wish to mount Grape alongside another Rack framework such as Sinatra, you can do so easily using
210
+ `Rack::Cascade`:
211
+
212
+ ```ruby
213
+ # Example config.ru
214
+
215
+ require 'sinatra'
216
+ require 'grape'
217
+
218
+ class API < Grape::API
219
+ get :hello do
220
+ { hello: "world" }
221
+ end
222
+ end
223
+
224
+ class Web < Sinatra::Base
225
+ get '/' do
226
+ "Hello world."
227
+ end
228
+ end
229
+
230
+ use Rack::Session::Cookie
231
+ run Rack::Cascade.new [API, Web]
232
+ ```
233
+
234
+ ### Rails
235
+
236
+ Place API files into `app/api`. Rails expects a subdirectory that matches the name of the Ruby module and a file name that matches the name of the class. In our example, the file name location and directory for `Twitter::API` should be `app/api/twitter/api.rb`.
237
+
238
+ Modify `application.rb`:
239
+
240
+ ```ruby
241
+ config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
242
+ config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
243
+ ```
244
+
245
+ Modify `config/routes`:
246
+
247
+ ```ruby
248
+ mount Twitter::API => '/'
249
+ ```
250
+
251
+ See below for additional code that enables reloading of API changes in development.
252
+
253
+ ### Modules
254
+
255
+ You can mount multiple API implementations inside another one. These don't have to be
256
+ different versions, but may be components of the same API.
257
+
258
+ ```ruby
259
+ class Twitter::API < Grape::API
260
+ mount Twitter::APIv1
261
+ mount Twitter::APIv2
262
+ end
263
+ ```
264
+
265
+ You can also mount on a path, which is similar to using `prefix` inside the mounted API itself.
266
+
267
+ ```ruby
268
+ class Twitter::API < Grape::API
269
+ mount Twitter::APIv1 => '/v1'
270
+ end
271
+ ```
272
+
273
+ ## Versioning
274
+
275
+ There are four strategies in which clients can reach your API's endpoints: `:path`,
276
+ `:header`, `:accept_version_header` and `:param`. The default strategy is `:path`.
277
+
278
+ ### Path
279
+
280
+ ```ruby
281
+ version 'v1', using: :path
282
+ ```
283
+
284
+ Using this versioning strategy, clients should pass the desired version in the URL.
285
+
286
+ curl -H http://localhost:9292/v1/statuses/public_timeline
287
+
288
+ ### Header
289
+
290
+ ```ruby
291
+ version 'v1', using: :header, vendor: 'twitter'
292
+ ```
293
+
294
+ Using this versioning strategy, clients should pass the desired version in the HTTP `Accept` head.
295
+
296
+ curl -H Accept:application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
297
+
298
+ By default, the first matching version is used when no `Accept` header is
299
+ supplied. This behavior is similar to routing in Rails. To circumvent this default behavior,
300
+ one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
301
+ is returned when no correct `Accept` header is supplied.
302
+
303
+ When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is returned if the `:cascade`
304
+ option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route
305
+ matches.
306
+
307
+ ### Accept-Version Header
308
+
309
+ ```ruby
310
+ version 'v1', using: :accept_version_header
311
+ ```
312
+
313
+ Using this versioning strategy, clients should pass the desired version in the HTTP `Accept-Version` header.
314
+
315
+ curl -H "Accept-Version:v1" http://localhost:9292/statuses/public_timeline
316
+
317
+ By default, the first matching version is used when no `Accept-Version` header is
318
+ supplied. This behavior is similar to routing in Rails. To circumvent this default behavior,
319
+ one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
320
+ is returned when no correct `Accept` header is supplied.
321
+
322
+ ### Param
323
+
324
+ ```ruby
325
+ version 'v1', using: :param
326
+ ```
327
+
328
+ Using this versioning strategy, clients should pass the desired version as a request parameter,
329
+ either in the URL query string or in the request body.
330
+
331
+ curl -H http://localhost:9292/statuses/public_timeline?apiver=v1
332
+
333
+ The default name for the query parameter is 'apiver' but can be specified using the `:parameter` option.
334
+
335
+ ```ruby
336
+ version 'v1', using: :param, parameter: "v"
337
+ ```
338
+
339
+ curl -H http://localhost:9292/statuses/public_timeline?v=v1
340
+
341
+
342
+ ## Describing Methods
343
+
344
+ You can add a description to API methods and namespaces.
345
+
346
+ ```ruby
347
+ desc "Returns your public timeline."
348
+ get :public_timeline do
349
+ Status.limit(20)
350
+ end
351
+ ```
352
+
353
+ ## Parameters
354
+
355
+ Request parameters are available through the `params` hash object. This includes `GET`, `POST`
356
+ and `PUT` parameters, along with any named parameters you specify in your route strings.
357
+
358
+ ```ruby
359
+ get :public_timeline do
360
+ Status.order(params[:sort_by])
361
+ end
362
+ ```
363
+
364
+ Parameters are automatically populated from the request body on `POST` and `PUT` for form input, JSON and
365
+ XML content-types.
366
+
367
+ The request:
368
+
369
+ ```
370
+ curl -d '{"text": "140 characters"}' 'http://localhost:9292/statuses' -H Content-Type:application/json -v
371
+ ```
372
+
373
+ The Grape endpoint:
374
+
375
+ ```ruby
376
+ post '/statuses' do
377
+ Status.create!(text: params[:text])
378
+ end
379
+ ```
380
+
381
+ Multipart POSTs and PUTs are supported as well.
382
+
383
+ The request:
384
+
385
+ ```
386
+ curl --form image_file=@image.jpg http://localhost:9292/upload
387
+ ```
388
+
389
+ The Grape endpoint:
390
+
391
+ ```ruby
392
+ post "upload" do
393
+ # file in params[:image_file]
394
+ end
395
+ ```
396
+
397
+ In the case of conflict between either of:
398
+
399
+ * route string parameters
400
+ * `GET`, `POST` and `PUT` parameters
401
+ * the contents of the request body on `POST` and `PUT`
402
+
403
+ route string parameters will have precedence.
404
+
405
+ ## Parameter Validation and Coercion
406
+
407
+ You can define validations and coercion options for your parameters using a `params` block.
408
+
409
+ ```ruby
410
+ params do
411
+ requires :id, type: Integer
412
+ optional :text, type: String, regexp: /^[a-z]+$/
413
+ group :media do
414
+ requires :url
415
+ end
416
+ optional :audio do
417
+ requires :format, type: Symbol, values: [:mp3, :wav, :aac, :ogg], default: :mp3
418
+ end
419
+ mutually_exclusive :media, :audio
420
+ end
421
+ put ':id' do
422
+ # params[:id] is an Integer
423
+ end
424
+ ```
425
+
426
+ When a type is specified an implicit validation is done after the coercion to ensure
427
+ the output type is the one declared.
428
+
429
+ Optional parameters can have a default value.
430
+
431
+ ```ruby
432
+ params do
433
+ optional :color, type: String, default: 'blue'
434
+ optional :random_number, type: Integer, default: -> { Random.rand(1..100) }
435
+ optional :non_random_number, type: Integer, default: Random.rand(1..100)
436
+ end
437
+ ```
438
+
439
+ Parameters can be restricted to a specific set of values with the `:values` option.
440
+
441
+ Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
442
+ number for each call to the endpoint of this `params` block. To have the default evaluate
443
+ at calltime use a lambda, like `:random_number` above.
444
+
445
+ ```ruby
446
+ params do
447
+ requires :status, type: Symbol, values: [:not_started, :processing, :done]
448
+ end
449
+ ```
450
+
451
+ The :values option can also be supplied with a `Proc` to be evalutated at runtime. For example, given a status
452
+ model you may want to restrict by hashtags that you have previously defined in the `HashTag` model.
453
+
454
+ ```ruby
455
+ params do
456
+ required :hashtag, type: String, values: -> { Hashtag.all.map(&:tag) }
457
+ end
458
+ ```
459
+
460
+ Parameters can be nested using `group` or by calling `requires` or `optional` with a block.
461
+ In the above example, this means `params[:media][:url]` is required along with `params[:id]`,
462
+ and `params[:audio][:format]` is required only if `params[:audio]` is present.
463
+ With a block, `group`, `requires` and `optional` accept an additional option `type` which can
464
+ be either `Array` or `Hash`, and defaults to `Array`. Depending on the value, the nested
465
+ parameters will be treated either as values of a hash or as values of hashes in an array.
466
+
467
+ ```ruby
468
+ params do
469
+ optional :preferences, type: Array do
470
+ requires :key
471
+ requires :value
472
+ end
473
+
474
+ requires :name, type: Hash do
475
+ requires :first_name
476
+ requires :last_name
477
+ end
478
+ end
479
+ ```
480
+
481
+ Parameters can be defined as `mutually_exclusive`, ensuring that they aren't present at the same time in a request.
482
+
483
+ ```ruby
484
+ params do
485
+ optional :beer
486
+ optional :wine
487
+ mutually_exclusive :beer, :wine
488
+ end
489
+ ```
490
+
491
+ Multiple sets can be defined:
492
+
493
+ ```ruby
494
+ params do
495
+ optional :beer
496
+ optional :wine
497
+ mutually_exclusive :beer, :wine
498
+ optional :scotch
499
+ optional :aquavit
500
+ mutually_exclusive :scotch, :aquavit
501
+ end
502
+ ```
503
+
504
+ **Warning**: Never define mutually exclusive sets with any required params. Two mutually exclusive required params will mean params are never valid, thus making the endpoint useless. One required param mutually exclusive with an optional param will mean the latter is never valid.
505
+
506
+ Parameters can be defined as 'exactly_one_of', ensuring that exactly one parameter gets selected.
507
+
508
+ ```ruby
509
+ params do
510
+ optional :beer
511
+ optional :wine
512
+ exactly_one_of :beer, :wine
513
+ end
514
+ ```
515
+
516
+ ### Namespace Validation and Coercion
517
+
518
+ Namespaces allow parameter definitions and apply to every method within the namespace.
519
+
520
+ ```ruby
521
+ namespace :statuses do
522
+ params do
523
+ requires :user_id, type: Integer, desc: "A user ID."
524
+ end
525
+ namespace ":user_id" do
526
+ desc "Retrieve a user's status."
527
+ params do
528
+ requires :status_id, type: Integer, desc: "A status ID."
529
+ end
530
+ get ":status_id" do
531
+ User.find(params[:user_id]).statuses.find(params[:status_id])
532
+ end
533
+ end
534
+ end
535
+ ```
536
+
537
+ The `namespace` method has a number of aliases, including: `group`, `resource`,
538
+ `resources`, and `segment`. Use whichever reads the best for your API.
539
+
540
+ You can conveniently define a route parameter as a namespace using `route_param`.
541
+
542
+ ```ruby
543
+ namespace :statuses do
544
+ route_param :id do
545
+ desc "Returns all replies for a status."
546
+ get 'replies' do
547
+ Status.find(params[:id]).replies
548
+ end
549
+ desc "Returns a status."
550
+ get do
551
+ Status.find(params[:id])
552
+ end
553
+ end
554
+ end
555
+ ```
556
+
557
+ ### Custom Validators
558
+
559
+ ```ruby
560
+ class AlphaNumeric < Grape::Validations::Validator
561
+ def validate_param!(attr_name, params)
562
+ unless params[attr_name] =~ /^[[:alnum:]]+$/
563
+ raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message: "must consist of alpha-numeric characters"
564
+ end
565
+ end
566
+ end
567
+ ```
568
+
569
+ ```ruby
570
+ params do
571
+ requires :text, alpha_numeric: true
572
+ end
573
+ ```
574
+
575
+ You can also create custom classes that take parameters.
576
+
577
+ ```ruby
578
+ class Length < Grape::Validations::SingleOptionValidator
579
+ def validate_param!(attr_name, params)
580
+ unless params[attr_name].length <= @option
581
+ raise Grape::Exceptions::Validation, param: @scope.full_name(attr_name), message: "must be at the most #{@option} characters long"
582
+ end
583
+ end
584
+ end
585
+ ```
586
+
587
+ ```ruby
588
+ params do
589
+ requires :text, length: 140
590
+ end
591
+ ```
592
+
593
+ ### Validation Errors
594
+
595
+ Validation and coercion errors are collected and an exception of type `Grape::Exceptions::ValidationErrors` is raised.
596
+ If the exception goes uncaught it will respond with a status of 400 and an error message.
597
+ You can rescue a `Grape::Exceptions::ValidationErrors` and respond with a custom response.
598
+
599
+ ```ruby
600
+ rescue_from Grape::Exceptions::ValidationErrors do |e|
601
+ Rack::Response.new({
602
+ status: e.status,
603
+ message: e.message,
604
+ errors: e.errors
605
+ }.to_json, e.status)
606
+ end
607
+ ```
608
+
609
+ The validation errors are grouped by parameter name and can be accessed via ``Grape::Exceptions::ValidationErrors#errors``.
610
+
611
+ ### I18n
612
+
613
+ Grape supports I18n for parameter-related error messages, but will fallback to English if
614
+ translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
615
+
616
+ ## Headers
617
+
618
+ Request headers are available through the `headers` helper or from `env` in their original form.
619
+
620
+ ```ruby
621
+ get do
622
+ error!('Unauthorized', 401) unless headers['Secret-Password'] == 'swordfish'
623
+ end
624
+ ```
625
+
626
+ ```ruby
627
+ get do
628
+ error!('Unauthorized', 401) unless env['HTTP_SECRET_PASSWORD'] == 'swordfish'
629
+ end
630
+ ```
631
+
632
+ You can set a response header with `header` inside an API.
633
+
634
+ ```ruby
635
+ header 'X-Robots-Tag', 'noindex'
636
+ ```
637
+
638
+ When raising `error!`, pass additional headers as arguments.
639
+
640
+ ```ruby
641
+ error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
642
+ ```
643
+
644
+ ## Routes
645
+
646
+ Optionally, you can define requirements for your named route parameters using regular
647
+ expressions on namespace or endpoint. The route will match only if all requirements are met.
648
+
649
+ ```ruby
650
+ get ':id', requirements: { id: /[0-9]*/ } do
651
+ Status.find(params[:id])
652
+ end
653
+
654
+ namespace :outer, requirements: { id: /[0-9]*/ } do
655
+ get :id do
656
+ end
657
+
658
+ get ":id/edit" do
659
+ end
660
+ end
661
+ ```
662
+
663
+ ## Helpers
664
+
665
+ You can define helper methods that your endpoints can use with the `helpers`
666
+ macro by either giving a block or a module.
667
+
668
+ ```ruby
669
+ module StatusHelpers
670
+ def user_info(user)
671
+ "#{user} has statused #{user.statuses} status(s)"
672
+ end
673
+ end
674
+
675
+ class API < Grape::API
676
+ # define helpers with a block
677
+ helpers do
678
+ def current_user
679
+ User.find(params[:user_id])
680
+ end
681
+ end
682
+
683
+ # or mix in a module
684
+ helpers StatusHelpers
685
+
686
+ get 'info' do
687
+ # helpers available in your endpoint and filters
688
+ user_info(current_user)
689
+ end
690
+ end
691
+ ```
692
+
693
+ You can define reusable `params` using `helpers`.
694
+
695
+ ```ruby
696
+ class API < Grape::API
697
+ helpers do
698
+ params :pagination do
699
+ optional :page, type: Integer
700
+ optional :per_page, type: Integer
701
+ end
702
+ end
703
+
704
+ desc "Get collection"
705
+ params do
706
+ use :pagination # aliases: includes, use_scope
707
+ end
708
+ get do
709
+ Collection.page(params[:page]).per(params[:per_page])
710
+ end
711
+ end
712
+ ```
713
+
714
+ You can also define reusable `params` using shared helpers.
715
+
716
+ ```ruby
717
+ module SharedParams
718
+ extend Grape::API::Helpers
719
+
720
+ params :period do
721
+ optional :start_date
722
+ optional :end_date
723
+ end
724
+
725
+ params :pagination do
726
+ optional :page, type: Integer
727
+ optional :per_page, type: Integer
728
+ end
729
+ end
730
+
731
+ class API < Grape::API
732
+ helpers SharedParams
733
+
734
+ desc "Get collection."
735
+ params do
736
+ use :period, :pagination
737
+ end
738
+
739
+ get do
740
+ Collection
741
+ .from(params[:start_date])
742
+ .to(params[:end_date])
743
+ .page(params[:page])
744
+ .per(params[:per_page])
745
+ end
746
+ end
747
+ ```
748
+
749
+ Helpers support blocks that can help set default values. The following API can return a collection sorted by `id` or `created_at` in `asc` or `desc` order.
750
+
751
+ ```ruby
752
+ module SharedParams
753
+ extend Grape::API::Helpers
754
+
755
+ params :order do |options|
756
+ optional :order_by, type:Symbol, values:options[:order_by], default:options[:default_order_by]
757
+ optional :order, type:Symbol, values:%i(asc desc), default:options[:default_order]
758
+ end
759
+ end
760
+
761
+ class API < Grape::API
762
+ helpers SharedParams
763
+
764
+ desc "Get a sorted collection."
765
+ params do
766
+ use :order, order_by:%i(id created_at), default_order_by: :created_at, default_order: :asc
767
+ end
768
+
769
+ get do
770
+ Collection.send(params[:order], params[:order_by])
771
+ end
772
+ end
773
+ ```
774
+
775
+ ## Parameter Documentation
776
+
777
+ You can attach additional documentation to `params` using a `documentation` hash.
778
+
779
+ ```ruby
780
+ params do
781
+ optional :first_name, type: String, documentation: { example: 'Jim' }
782
+ requires :last_name, type: String, documentation: { example: 'Smith' }
783
+ end
784
+ ```
785
+
786
+ ## Cookies
787
+
788
+ You can set, get and delete your cookies very simply using `cookies` method.
789
+
790
+ ```ruby
791
+ class API < Grape::API
792
+ get 'status_count' do
793
+ cookies[:status_count] ||= 0
794
+ cookies[:status_count] += 1
795
+ { status_count: cookies[:status_count] }
796
+ end
797
+
798
+ delete 'status_count' do
799
+ { status_count: cookies.delete(:status_count) }
800
+ end
801
+ end
802
+ ```
803
+
804
+ Use a hash-based syntax to set more than one value.
805
+
806
+ ```ruby
807
+ cookies[:status_count] = {
808
+ value: 0,
809
+ expires: Time.tomorrow,
810
+ domain: '.twitter.com',
811
+ path: '/'
812
+ }
813
+
814
+ cookies[:status_count][:value] +=1
815
+ ```
816
+
817
+ Delete a cookie with `delete`.
818
+
819
+ ```ruby
820
+ cookies.delete :status_count
821
+ ```
822
+
823
+ Specify an optional path.
824
+
825
+ ```ruby
826
+ cookies.delete :status_count, path: '/'
827
+ ```
828
+
829
+ ## Redirecting
830
+
831
+ You can redirect to a new url temporarily (302) or permanently (301).
832
+
833
+ ```ruby
834
+ redirect '/statuses'
835
+ ```
836
+
837
+ ```ruby
838
+ redirect '/statuses', permanent: true
839
+ ```
840
+
841
+ ## Allowed Methods
842
+
843
+ When you add a `GET` route for a resource, a route for the `HEAD`
844
+ method will also be added automatically. You can disable this
845
+ behavior with `do_not_route_head!`.
846
+
847
+ ``` ruby
848
+ class API < Grape::API
849
+ do_not_route_head!
850
+
851
+ get '/example' do
852
+ # only responds to GET
853
+ end
854
+ end
855
+ ```
856
+
857
+ When you add a route for a resource, a route for the `OPTIONS`
858
+ method will also be added. The response to an OPTIONS request will
859
+ include an "Allow" header listing the supported methods.
860
+
861
+ ```ruby
862
+ class API < Grape::API
863
+ get '/rt_count' do
864
+ { rt_count: current_user.rt_count }
865
+ end
866
+
867
+ params do
868
+ requires :value, type: Integer, desc: 'Value to add to the rt count.'
869
+ end
870
+ put '/rt_count' do
871
+ current_user.rt_count += params[:value].to_i
872
+ { rt_count: current_user.rt_count }
873
+ end
874
+ end
875
+ ```
876
+
877
+ ``` shell
878
+ curl -v -X OPTIONS http://localhost:3000/rt_count
879
+
880
+ > OPTIONS /rt_count HTTP/1.1
881
+ >
882
+ < HTTP/1.1 204 No Content
883
+ < Allow: OPTIONS, GET, PUT
884
+ ```
885
+
886
+ You can disable this behavior with `do_not_route_options!`.
887
+
888
+ If a request for a resource is made with an unsupported HTTP method, an
889
+ HTTP 405 (Method Not Allowed) response will be returned.
890
+
891
+ ``` shell
892
+ curl -X DELETE -v http://localhost:3000/rt_count/
893
+
894
+ > DELETE /rt_count/ HTTP/1.1
895
+ > Host: localhost:3000
896
+ >
897
+ < HTTP/1.1 405 Method Not Allowed
898
+ < Allow: OPTIONS, GET, PUT
899
+ ```
900
+
901
+ ## Raising Exceptions
902
+
903
+ You can abort the execution of an API method by raising errors with `error!`.
904
+
905
+ ```ruby
906
+ error! 'Access Denied', 401
907
+ ```
908
+
909
+ You can also return JSON formatted objects by raising error! and passing a hash
910
+ instead of a message.
911
+
912
+ ```ruby
913
+ error!({ error: "unexpected error", detail: "missing widget" }, 500)
914
+ ```
915
+
916
+ ### Default Error HTTP Status Code
917
+
918
+ By default Grape returns a 500 status code from `error!`. You can change this with `default_error_status`.
919
+
920
+ ``` ruby
921
+ class API < Grape::API
922
+ default_error_status 400
923
+ get '/example' do
924
+ error! "This should have http status code 400"
925
+ end
926
+ end
927
+ ```
928
+
929
+ ### Handling 404
930
+
931
+ For Grape to handle all the 404s for your API, it can be useful to use a catch-all.
932
+ In its simplest form, it can be like:
933
+
934
+ ```ruby
935
+ route :any, '*path' do
936
+ error! # or something else
937
+ end
938
+ ```
939
+
940
+ It is very crucial to __define this endpoint at the very end of your API__, as it
941
+ literally accepts every request.
942
+
943
+ ## Exception Handling
944
+
945
+ Grape can be told to rescue all exceptions and return them in the API format.
946
+
947
+ ```ruby
948
+ class Twitter::API < Grape::API
949
+ rescue_from :all
950
+ end
951
+ ```
952
+
953
+ You can also rescue specific exceptions.
954
+
955
+ ```ruby
956
+ class Twitter::API < Grape::API
957
+ rescue_from ArgumentError, UserDefinedError
958
+ end
959
+ ```
960
+
961
+ In this case ```UserDefinedError``` must be inherited from ```StandardError```.
962
+
963
+ The error format will match the request format. See "Content-Types" below.
964
+
965
+ Custom error formatters for existing and additional types can be defined with a proc.
966
+
967
+ ```ruby
968
+ class Twitter::API < Grape::API
969
+ error_formatter :txt, lambda { |message, backtrace, options, env|
970
+ "error: #{message} from #{backtrace}"
971
+ }
972
+ end
973
+ ```
974
+
975
+ You can also use a module or class.
976
+
977
+ ```ruby
978
+ module CustomFormatter
979
+ def self.call(message, backtrace, options, env)
980
+ { message: message, backtrace: backtrace }
981
+ end
982
+ end
983
+
984
+ class Twitter::API < Grape::API
985
+ error_formatter :custom, CustomFormatter
986
+ end
987
+ ```
988
+
989
+ You can rescue all exceptions with a code block. The `error_response` wrapper
990
+ automatically sets the default error code and content-type.
991
+
992
+ ```ruby
993
+ class Twitter::API < Grape::API
994
+ rescue_from :all do |e|
995
+ error_response({ message: "rescued from #{e.class.name}" })
996
+ end
997
+ end
998
+ ```
999
+
1000
+ You can also rescue specific exceptions with a code block and handle the Rack
1001
+ response at the lowest level.
1002
+
1003
+ ```ruby
1004
+ class Twitter::API < Grape::API
1005
+ rescue_from :all do |e|
1006
+ Rack::Response.new([ e.message ], 500, { "Content-type" => "text/error" }).finish
1007
+ end
1008
+ end
1009
+ ```
1010
+
1011
+ Or rescue specific exceptions.
1012
+
1013
+ ```ruby
1014
+ class Twitter::API < Grape::API
1015
+ rescue_from ArgumentError do |e|
1016
+ Rack::Response.new([ "ArgumentError: #{e.message}" ], 500).finish
1017
+ end
1018
+ rescue_from NotImplementedError do |e|
1019
+ Rack::Response.new([ "NotImplementedError: #{e.message}" ], 500).finish
1020
+ end
1021
+ end
1022
+ ```
1023
+
1024
+ By default, `rescue_from` will rescue the exceptions listed and all their subclasses.
1025
+
1026
+ Assume you have the following exception classes defined.
1027
+
1028
+ ```ruby
1029
+ module APIErrors
1030
+ class ParentError < StandardError; end
1031
+ class ChildError < ParentError; end
1032
+ end
1033
+ ```
1034
+
1035
+ Then the following `rescue_from` clause will rescue exceptions of type `APIErrors::ParentError` and its subclasses (in this case `APIErrors::ChildError`).
1036
+
1037
+ ```ruby
1038
+ rescue_from APIErrors::ParentError do |e|
1039
+ Rack::Response.new({
1040
+ error: "#{e.class} error",
1041
+ message: e.message
1042
+ }.to_json, e.status).finish
1043
+ end
1044
+ ```
1045
+
1046
+ To only rescue the base exception class, set `rescue_subclasses: false`.
1047
+ The code below will rescue exceptions of type `RuntimeError` but _not_ its subclasses.
1048
+
1049
+ ```ruby
1050
+ rescue_from RuntimeError, rescue_subclasses: false do |e|
1051
+ Rack::Response.new({
1052
+ status: e.status,
1053
+ message: e.message,
1054
+ errors: e.errors
1055
+ }.to_json, e.status).finish
1056
+ end
1057
+ ```
1058
+
1059
+ #### Rails 3.x
1060
+
1061
+ When mounted inside containers, such as Rails 3.x, errors like "404 Not Found" or
1062
+ "406 Not Acceptable" will likely be handled and rendered by Rails handlers. For instance,
1063
+ accessing a nonexistent route "/api/foo" raises a 404, which inside rails will ultimately
1064
+ be translated to an `ActionController::RoutingError`, which most likely will get rendered
1065
+ to a HTML error page.
1066
+
1067
+ Most APIs will enjoy preventing downstream handlers from handling errors. You may set the
1068
+ `:cascade` option to `false` for the entire API or separately on specific `version` definitions,
1069
+ which will remove the `X-Cascade: true` header from API responses.
1070
+
1071
+ ```ruby
1072
+ cascade false
1073
+ ```
1074
+
1075
+ ```ruby
1076
+ version 'v1', using: :header, vendor: 'twitter', cascade: false
1077
+ ```
1078
+
1079
+ ## Logging
1080
+
1081
+ `Grape::API` provides a `logger` method which by default will return an instance of the `Logger`
1082
+ class from Ruby's standard library.
1083
+
1084
+ To log messages from within an endpoint, you need to define a helper to make the logger
1085
+ available in the endpoint context.
1086
+
1087
+ ```ruby
1088
+ class API < Grape::API
1089
+ helpers do
1090
+ def logger
1091
+ API.logger
1092
+ end
1093
+ end
1094
+ post '/statuses' do
1095
+ # ...
1096
+ logger.info "#{current_user} has statused"
1097
+ end
1098
+ end
1099
+ ```
1100
+
1101
+ You can also set your own logger.
1102
+
1103
+ ```ruby
1104
+ class MyLogger
1105
+ def warning(message)
1106
+ puts "this is a warning: #{message}"
1107
+ end
1108
+ end
1109
+
1110
+ class API < Grape::API
1111
+ logger MyLogger.new
1112
+ helpers do
1113
+ def logger
1114
+ API.logger
1115
+ end
1116
+ end
1117
+ get '/statuses' do
1118
+ logger.warning "#{current_user} has statused"
1119
+ end
1120
+ end
1121
+ ```
1122
+
1123
+ ## API Formats
1124
+
1125
+ By default, Grape supports _XML_, _JSON_, and _TXT_ content-types. The default format is `:txt`.
1126
+
1127
+ Serialization takes place automatically. For example, you do not have to call `to_json` in each JSON API implementation.
1128
+
1129
+ Your API can declare which types to support by using `content_type`. Response format is determined by the
1130
+ request's extension, an explicit `format` parameter in the query string, or `Accept` header.
1131
+
1132
+ The following API will only respond to the JSON content-type and will not parse any other input than `application/json`,
1133
+ `application/x-www-form-urlencoded`, `multipart/form-data`, `multipart/related` and `multipart/mixed`. All other requests
1134
+ will fail with an HTTP 406 error code.
1135
+
1136
+ ```ruby
1137
+ class Twitter::API < Grape::API
1138
+ format :json
1139
+ end
1140
+ ```
1141
+
1142
+ When the content-type is omitted, Grape will return a 406 error code unless `default_format` is specified.
1143
+ The following API will try to parse any data without a content-type using a JSON parser.
1144
+
1145
+ ```ruby
1146
+ class Twitter::API < Grape::API
1147
+ format :json
1148
+ default_format :json
1149
+ end
1150
+ ```
1151
+
1152
+ If you combine `format` with `rescue_from :all`, errors will be rendered using the same format.
1153
+ If you do not want this behavior, set the default error formatter with `default_error_formatter`.
1154
+
1155
+ ```ruby
1156
+ class Twitter::API < Grape::API
1157
+ format :json
1158
+ content_type :txt, "text/plain"
1159
+ default_error_formatter :txt
1160
+ end
1161
+ ```
1162
+
1163
+ Custom formatters for existing and additional types can be defined with a proc.
1164
+
1165
+ ```ruby
1166
+ class Twitter::API < Grape::API
1167
+ content_type :xls, "application/vnd.ms-excel"
1168
+ formatter :xls, lambda { |object, env| object.to_xls }
1169
+ end
1170
+ ```
1171
+
1172
+ You can also use a module or class.
1173
+
1174
+ ```ruby
1175
+ module XlsFormatter
1176
+ def self.call(object, env)
1177
+ object.to_xls
1178
+ end
1179
+ end
1180
+
1181
+ class Twitter::API < Grape::API
1182
+ content_type :xls, "application/vnd.ms-excel"
1183
+ formatter :xls, XlsFormatter
1184
+ end
1185
+ ```
1186
+
1187
+ Built-in formats are the following.
1188
+
1189
+ * `:json` and `:jsonapi`: use object's `to_json` when available, otherwise call `MultiJson.dump`
1190
+ * `:xml`: use object's `to_xml` when available, usually via `MultiXml`, otherwise call `to_s`
1191
+ * `:txt`: use object's `to_txt` when available, otherwise `to_s`
1192
+ * `:serializable_hash`: use object's `serializable_hash` when available, otherwise fallback to `:json`
1193
+
1194
+ Use `default_format` to set the fallback format when the format could not be determined from the `Accept` header.
1195
+ See below for the order for choosing the API format.
1196
+
1197
+ ```ruby
1198
+ class Twitter::API < Grape::API
1199
+ default_format :json
1200
+ end
1201
+ ```
1202
+
1203
+ The order for choosing the format is the following.
1204
+
1205
+ * Use the file extension, if specified. If the file is .json, choose the JSON format.
1206
+ * Use the value of the `format` parameter in the query string, if specified.
1207
+ * Use the format set by the `format` option, if specified.
1208
+ * Attempt to find an acceptable format from the `Accept` header.
1209
+ * Use the default format, if specified by the `default_format` option.
1210
+ * Default to `:txt`.
1211
+
1212
+ You can override this process explicitly by specifying `env['api.format']` in the API itself.
1213
+ For example, the following API will let you upload arbitrary files and return their contents as an attachment with the correct MIME type.
1214
+
1215
+ ```ruby
1216
+ class Twitter::API < Grape::API
1217
+ post "attachment" do
1218
+ filename = params[:file][:filename]
1219
+ content_type MIME::Types.type_for(filename)[0].to_s
1220
+ env['api.format'] = :binary # there's no formatter for :binary, data will be returned "as is"
1221
+ header "Content-Disposition", "attachment; filename*=UTF-8''#{URI.escape(filename)}"
1222
+ params[:file][:tempfile].read
1223
+ end
1224
+ end
1225
+ ```
1226
+
1227
+ ### JSONP
1228
+
1229
+ Grape supports JSONP via [Rack::JSONP](https://github.com/rack/rack-contrib), part of the
1230
+ [rack-contrib](https://github.com/rack/rack-contrib) gem. Add `rack-contrib` to your `Gemfile`.
1231
+
1232
+ ```ruby
1233
+ require 'rack/contrib'
1234
+
1235
+ class API < Grape::API
1236
+ use Rack::JSONP
1237
+ format :json
1238
+ get '/' do
1239
+ 'Hello World'
1240
+ end
1241
+ end
1242
+ ```
1243
+
1244
+ ### CORS
1245
+
1246
+ Grape supports CORS via [Rack::CORS](https://github.com/cyu/rack-cors), part of the
1247
+ [rack-cors](https://github.com/cyu/rack-cors) gem. Add `rack-cors` to your `Gemfile`,
1248
+ then use the middleware in your config.ru file.
1249
+
1250
+ ```ruby
1251
+ require 'rack/cors'
1252
+
1253
+ use Rack::Cors do
1254
+ allow do
1255
+ origins '*'
1256
+ resource '*', headers: :any, methods: :get
1257
+ end
1258
+ end
1259
+
1260
+ run Twitter::API
1261
+
1262
+ ```
1263
+
1264
+ ## Content-type
1265
+
1266
+ Content-type is set by the formatter. You can override the content-type of the response at runtime
1267
+ by setting the `Content-Type` header.
1268
+
1269
+ ```ruby
1270
+ class API < Grape::API
1271
+ get '/home_timeline_js' do
1272
+ content_type "application/javascript"
1273
+ "var statuses = ...;"
1274
+ end
1275
+ end
1276
+ ```
1277
+
1278
+ ## API Data Formats
1279
+
1280
+ Grape accepts and parses input data sent with the POST and PUT methods as described in the Parameters
1281
+ section above. It also supports custom data formats. You must declare additional content-types via
1282
+ `content_type` and optionally supply a parser via `parser` unless a parser is already available within
1283
+ Grape to enable a custom format. Such a parser can be a function or a class.
1284
+
1285
+ With a parser, parsed data is available "as-is" in `env['api.request.body']`.
1286
+ Without a parser, data is available "as-is" and in `env['api.request.input']`.
1287
+
1288
+ The following example is a trivial parser that will assign any input with the "text/custom" content-type
1289
+ to `:value`. The parameter will be available via `params[:value]` inside the API call.
1290
+
1291
+ ```ruby
1292
+ module CustomParser
1293
+ def self.call(object, env)
1294
+ { value: object.to_s }
1295
+ end
1296
+ end
1297
+ ```
1298
+
1299
+ ```ruby
1300
+ content_type :txt, "text/plain"
1301
+ content_type :custom, "text/custom"
1302
+ parser :custom, CustomParser
1303
+
1304
+ put "value" do
1305
+ params[:value]
1306
+ end
1307
+ ```
1308
+
1309
+ You can invoke the above API as follows.
1310
+
1311
+ ```
1312
+ curl -X PUT -d 'data' 'http://localhost:9292/value' -H Content-Type:text/custom -v
1313
+ ```
1314
+
1315
+ You can disable parsing for a content-type with `nil`. For example, `parser :json, nil` will disable JSON parsing altogether. The request data is then available as-is in `env['api.request.body']`.
1316
+
1317
+ ## RESTful Model Representations
1318
+
1319
+ Grape supports a range of ways to present your data with some help from a generic `present` method,
1320
+ which accepts two arguments: the object to be presented and the options associated with it. The options
1321
+ hash may include `:with`, which defines the entity to expose.
1322
+
1323
+ ### Grape Entities
1324
+
1325
+ Add the [grape-entity](https://github.com/intridea/grape-entity) gem to your Gemfile.
1326
+ Please refer to the [grape-entity documentation](https://github.com/intridea/grape-entity/blob/master/README.md)
1327
+ for more details.
1328
+
1329
+ The following example exposes statuses.
1330
+
1331
+ ```ruby
1332
+ module API
1333
+ module Entities
1334
+ class Status < Grape::Entity
1335
+ expose :user_name
1336
+ expose :text, documentation: { type: "string", desc: "Status update text." }
1337
+ expose :ip, if: { type: :full }
1338
+ expose :user_type, :user_id, if: lambda { |status, options| status.user.public? }
1339
+ expose :digest { |status, options| Digest::MD5.hexdigest(status.txt) }
1340
+ expose :replies, using: API::Status, as: :replies
1341
+ end
1342
+ end
1343
+
1344
+ class Statuses < Grape::API
1345
+ version 'v1'
1346
+
1347
+ desc 'Statuses index', {
1348
+ params: API::Entities::Status.documentation
1349
+ }
1350
+ get '/statuses' do
1351
+ statuses = Status.all
1352
+ type = current_user.admin? ? :full : :default
1353
+ present statuses, with: API::Entities::Status, type: type
1354
+ end
1355
+ end
1356
+ end
1357
+ ```
1358
+
1359
+ You can use entity documentation directly in the params block with `using: Entity.documentation`.
1360
+
1361
+ ```ruby
1362
+ module API
1363
+ class Statuses < Grape::API
1364
+ version 'v1'
1365
+
1366
+ desc 'Create a status'
1367
+ params do
1368
+ requires :all, except: [:ip], using: API::Entities::Status.documentation.except(:id)
1369
+ end
1370
+ post '/status' do
1371
+ Status.create! params
1372
+ end
1373
+ end
1374
+ end
1375
+ ```
1376
+
1377
+ You can present with multiple entities using an optional Symbol argument.
1378
+
1379
+ ```ruby
1380
+ get '/statuses' do
1381
+ statuses = Status.all.page(1).per(20)
1382
+ present :total_page, 10
1383
+ present :per_page, 20
1384
+ present :statuses, statuses, with: API::Entities::Status
1385
+ end
1386
+ ```
1387
+
1388
+ The response will be
1389
+
1390
+ ```
1391
+ {
1392
+ total_page: 10,
1393
+ per_page: 20,
1394
+ statuses: []
1395
+ }
1396
+ ```
1397
+
1398
+ In addition to separately organizing entities, it may be useful to put them as namespaced
1399
+ classes underneath the model they represent.
1400
+
1401
+ ```ruby
1402
+ class Status
1403
+ def entity
1404
+ Entity.new(self)
1405
+ end
1406
+
1407
+ class Entity < Grape::Entity
1408
+ expose :text, :user_id
1409
+ end
1410
+ end
1411
+ ```
1412
+
1413
+ If you organize your entities this way, Grape will automatically detect the `Entity` class and
1414
+ use it to present your models. In this example, if you added `present Status.new` to your endpoint,
1415
+ Grape will automatically detect that there is a `Status::Entity` class and use that as the
1416
+ representative entity. This can still be overridden by using the `:with` option or an explicit
1417
+ `represents` call.
1418
+
1419
+ ### Hypermedia
1420
+
1421
+ You can use any Hypermedia representer, including [Roar](https://github.com/apotonick/roar).
1422
+ Roar renders JSON and works with the built-in Grape JSON formatter. Add `Roar::Representer::JSON`
1423
+ into your models or call `to_json` explicitly in your API implementation.
1424
+
1425
+ ### Rabl
1426
+
1427
+ You can use [Rabl](https://github.com/nesquena/rabl) templates with the help of the
1428
+ [grape-rabl](https://github.com/LTe/grape-rabl) gem, which defines a custom Grape Rabl
1429
+ formatter.
1430
+
1431
+ ### Active Model Serializers
1432
+
1433
+ You can use [Active Model Serializers](https://github.com/rails-api/active_model_serializers) serializers with the help of the
1434
+ [grape-active_model_serializers](https://github.com/jrhe/grape-active_model_serializers) gem, which defines a custom Grape AMS
1435
+ formatter.
1436
+
1437
+
1438
+ ## Authentication
1439
+
1440
+ ### Basic and Digest Auth
1441
+
1442
+ Grape has built-in Basic and Digest authentication.
1443
+
1444
+ ```ruby
1445
+ http_basic do |username, password|
1446
+ # verify user's password here
1447
+ { 'test' => 'password1' }[username] == password
1448
+ end
1449
+ ```
1450
+
1451
+ ```ruby
1452
+ http_digest({ realm: 'Test Api', opaque: 'app secret' }) do |username|
1453
+ # lookup the user's password here
1454
+ { 'user1' => 'password1' }[username]
1455
+ end
1456
+ ```
1457
+
1458
+ Use [warden-oauth2](https://github.com/opperator/warden-oauth2) or [rack-oauth2](https://github.com/nov/rack-oauth2) for OAuth2 support.
1459
+
1460
+ ## Describing and Inspecting an API
1461
+
1462
+ Grape routes can be reflected at runtime. This can notably be useful for generating
1463
+ documentation.
1464
+
1465
+ Grape exposes arrays of API versions and compiled routes. Each route
1466
+ contains a `route_prefix`, `route_version`, `route_namespace`, `route_method`,
1467
+ `route_path` and `route_params`. The description and the optional hash that
1468
+ follows the API path may contain any number of keys and its values are also
1469
+ accessible via dynamically-generated `route_[name]` functions.
1470
+
1471
+ ```ruby
1472
+ TwitterAPI::versions # yields [ 'v1', 'v2' ]
1473
+ TwitterAPI::routes # yields an array of Grape::Route objects
1474
+ TwitterAPI::routes[0].route_version # yields 'v1'
1475
+ TwitterAPI::routes[0].route_description # etc.
1476
+ ```
1477
+
1478
+ ## Current Route and Endpoint
1479
+
1480
+ It's possible to retrieve the information about the current route from within an API
1481
+ call with `route`.
1482
+
1483
+ ```ruby
1484
+ class MyAPI < Grape::API
1485
+ desc "Returns a description of a parameter."
1486
+ params do
1487
+ requires :id, type: Integer, desc: "Identity."
1488
+ end
1489
+ get "params/:id" do
1490
+ route.route_params[params[:id]] # yields the parameter description
1491
+ end
1492
+ end
1493
+ ```
1494
+
1495
+ The current endpoint responding to the request is `self` within the API block
1496
+ or `env['api.endpoint']` elsewhere. The endpoint has some interesting properties,
1497
+ such as `source` which gives you access to the original code block of the API
1498
+ implementation. This can be particularly useful for building a logger middleware.
1499
+
1500
+ ```ruby
1501
+ class ApiLogger < Grape::Middleware::Base
1502
+ def before
1503
+ file = env['api.endpoint'].source.source_location[0]
1504
+ line = env['api.endpoint'].source.source_location[1]
1505
+ logger.debug "[api] #{file}:#{line}"
1506
+ end
1507
+ end
1508
+ ```
1509
+
1510
+ ## Before and After
1511
+
1512
+ Blocks can be executed before or after every API call, using `before`, `after`,
1513
+ `before_validation` and `after_validation`.
1514
+
1515
+ Before and after callbacks execute in the following order:
1516
+
1517
+ 1. `before`
1518
+ 2. `before_validation`
1519
+ 3. _validations_
1520
+ 4. `after_validation`
1521
+ 5. _the API call_
1522
+ 6. `after`
1523
+
1524
+ Steps 4, 5 and 6 only happen if validation succeeds.
1525
+
1526
+ E.g. using `before`:
1527
+
1528
+ ```ruby
1529
+ before do
1530
+ header "X-Robots-Tag", "noindex"
1531
+ end
1532
+ ```
1533
+
1534
+ The block applies to every API call within and below the current namespace:
1535
+
1536
+ ```ruby
1537
+ class MyAPI < Grape::API
1538
+ get '/' do
1539
+ "root - #{@blah}"
1540
+ end
1541
+
1542
+ namespace :foo do
1543
+ before do
1544
+ @blah = 'blah'
1545
+ end
1546
+
1547
+ get '/' do
1548
+ "root - foo - #{@blah}"
1549
+ end
1550
+
1551
+ namespace :bar do
1552
+ get '/' do
1553
+ "root - foo - bar - #{@blah}"
1554
+ end
1555
+ end
1556
+ end
1557
+ end
1558
+ ```
1559
+
1560
+ The behaviour is then:
1561
+
1562
+ ```bash
1563
+ GET / # 'root - '
1564
+ GET /foo # 'root - foo - blah'
1565
+ GET /foo/bar # 'root - foo - bar - blah'
1566
+ ```
1567
+
1568
+ Params on a `namespace` (or whatever alias you are using) also work when using
1569
+ `before_validation` or `after_validation`:
1570
+
1571
+ ```ruby
1572
+ class MyAPI < Grape::API
1573
+ params do
1574
+ requires :blah, type: Integer
1575
+ end
1576
+ resource ':blah' do
1577
+ after_validation do
1578
+ # if we reach this point validations will have passed
1579
+ @blah = declared(params, include_missing: false)[:blah]
1580
+ end
1581
+
1582
+ get '/' do
1583
+ @blah.class
1584
+ end
1585
+ end
1586
+ end
1587
+ ```
1588
+
1589
+ The behaviour is then:
1590
+
1591
+ ```bash
1592
+ GET /123 # 'Fixnum'
1593
+ GET /foo # 400 error - 'blah is invalid'
1594
+ ```
1595
+
1596
+ ## Anchoring
1597
+
1598
+ Grape by default anchors all request paths, which means that the request URL
1599
+ should match from start to end to match, otherwise a `404 Not Found` is
1600
+ returned. However, this is sometimes not what you want, because it is not always
1601
+ known upfront what can be expected from the call. This is because Rack-mount by
1602
+ default anchors requests to match from the start to the end, or not at all.
1603
+ Rails solves this problem by using a `anchor: false` option in your routes.
1604
+ In Grape this option can be used as well when a method is defined.
1605
+
1606
+ For instance when your API needs to get part of an URL, for instance:
1607
+
1608
+ ```ruby
1609
+ class TwitterAPI < Grape::API
1610
+ namespace :statuses do
1611
+ get '/(*:status)', anchor: false do
1612
+
1613
+ end
1614
+ end
1615
+ end
1616
+ ```
1617
+
1618
+ This will match all paths starting with '/statuses/'. There is one caveat though:
1619
+ the `params[:status]` parameter only holds the first part of the request url.
1620
+ Luckily this can be circumvented by using the described above syntax for path
1621
+ specification and using the `PATH_INFO` Rack environment variable, using
1622
+ `env["PATH_INFO"]`. This will hold everything that comes after the '/statuses/'
1623
+ part.
1624
+
1625
+ ## Writing Tests
1626
+
1627
+ You can test a Grape API with RSpec by making HTTP requests and examining the response.
1628
+
1629
+ ### Writing Tests with Rack
1630
+
1631
+ Use `rack-test` and define your API as `app`.
1632
+
1633
+ ```ruby
1634
+ require 'spec_helper'
1635
+
1636
+ describe Twitter::API do
1637
+ include Rack::Test::Methods
1638
+
1639
+ def app
1640
+ Twitter::API
1641
+ end
1642
+
1643
+ describe Twitter::API do
1644
+ describe "GET /api/v1/statuses" do
1645
+ it "returns an empty array of statuses" do
1646
+ get "/api/v1/statuses"
1647
+ last_response.status.should == 200
1648
+ JSON.parse(last_response.body).should == []
1649
+ end
1650
+ end
1651
+ describe "GET /api/v1/statuses/:id" do
1652
+ it "returns a status by id" do
1653
+ status = Status.create!
1654
+ get "/api/v1/statuses/#{status.id}"
1655
+ last_response.body.should == status.to_json
1656
+ end
1657
+ end
1658
+ end
1659
+ end
1660
+ ```
1661
+
1662
+ ### Writing Tests with Rails
1663
+
1664
+ ```ruby
1665
+ require 'spec_helper'
1666
+
1667
+ describe Twitter::API do
1668
+ describe "GET /api/v1/statuses" do
1669
+ it "returns an empty array of statuses" do
1670
+ get "/api/v1/statuses"
1671
+ response.status.should == 200
1672
+ JSON.parse(response.body).should == []
1673
+ end
1674
+ end
1675
+ describe "GET /api/v1/statuses/:id" do
1676
+ it "returns a status by id" do
1677
+ status = Status.create!
1678
+ get "/api/v1/statuses/#{status.id}"
1679
+ response.body.should == status.to_json
1680
+ end
1681
+ end
1682
+ end
1683
+ ```
1684
+
1685
+ In Rails, HTTP request tests would go into the `spec/requests` group. You may want your API code to go into
1686
+ `app/api` - you can match that layout under `spec` by adding the following in `spec/spec_helper.rb`.
1687
+
1688
+ ```ruby
1689
+ RSpec.configure do |config|
1690
+ config.include RSpec::Rails::RequestExampleGroup, type: :request, example_group: {
1691
+ file_path: /spec\/api/
1692
+ }
1693
+ end
1694
+ ```
1695
+
1696
+ ### Stubbing Helpers
1697
+
1698
+ Because helpers are mixed in based on the context when an endpoint is defined, it can
1699
+ be difficult to stub or mock them for testing. The `Grape::Endpoint.before_each` method
1700
+ can help by allowing you to define behavior on the endpoint that will run before every
1701
+ request.
1702
+
1703
+ ```ruby
1704
+ describe 'an endpoint that needs helpers stubbed' do
1705
+ before do
1706
+ Grape::Endpoint.before_each do |endpoint|
1707
+ endpoint.stub(:helper_name).and_return('desired_value')
1708
+ end
1709
+ end
1710
+
1711
+ after do
1712
+ Grape::Endpoint.before_each nil
1713
+ end
1714
+
1715
+ it 'should properly stub the helper' do
1716
+ # ...
1717
+ end
1718
+ end
1719
+ ```
1720
+
1721
+ ## Reloading API Changes in Development
1722
+
1723
+ ### Rails 3.x
1724
+
1725
+ Add API paths to `config/application.rb`.
1726
+
1727
+ ```ruby
1728
+ # Auto-load API and its subdirectories
1729
+ config.paths.add File.join("app", "api"), glob: File.join("**", "*.rb")
1730
+ config.autoload_paths += Dir[Rails.root.join("app", "api", "*")]
1731
+ ```
1732
+
1733
+ Create `config/initializers/reload_api.rb`.
1734
+
1735
+ ```ruby
1736
+ if Rails.env.development?
1737
+ ActiveSupport::Dependencies.explicitly_unloadable_constants << "Twitter::API"
1738
+
1739
+ api_files = Dir[Rails.root.join('app', 'api', '**', '*.rb')]
1740
+ api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
1741
+ Rails.application.reload_routes!
1742
+ end
1743
+ ActionDispatch::Callbacks.to_prepare do
1744
+ api_reloader.execute_if_updated
1745
+ end
1746
+ end
1747
+ ```
1748
+
1749
+ See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-rails-3-reload-lib-directory-for-each-request/4368838#4368838) for more information.
1750
+
1751
+ ## Performance Monitoring
1752
+
1753
+ Grape integrates with NewRelic via the
1754
+ [newrelic-grape](https://github.com/flyerhzm/newrelic-grape) gem, and
1755
+ with Librato Metrics with the [grape-librato](https://github.com/seanmoon/grape-librato) gem.
1756
+
1757
+ ## Contributing to Grape
1758
+
1759
+ Grape is work of hundreds of contributors. You're encouraged to submit pull requests, propose
1760
+ features and discuss issues.
1761
+
1762
+ See [CONTRIBUTING](CONTRIBUTING.md).
1763
+
1764
+ ## Hacking on Grape
1765
+
1766
+ You can start hacking on Grape on
1767
+ [Nitrous.IO](https://www.nitrous.io/?utm_source=github.com&utm_campaign=grape&utm_medium=hackonnitrous) in a matter of seconds:
1768
+
1769
+ [![Hack intridea/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)
1770
+
1771
+ ## License
1772
+
1773
+ MIT License. See LICENSE for details.
1774
+
1775
+ ## Copyright
1776
+
1777
+ Copyright (c) 2010-2013 Michael Bleigh, and Intridea, Inc.