grape 0.6.1 → 0.7.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 (79) hide show
  1. checksums.yaml +9 -9
  2. data/.gitignore +7 -0
  3. data/.rubocop.yml +5 -0
  4. data/.travis.yml +3 -3
  5. data/CHANGELOG.md +42 -3
  6. data/CONTRIBUTING.md +118 -0
  7. data/Gemfile +4 -4
  8. data/README.md +312 -52
  9. data/Rakefile +6 -1
  10. data/UPGRADING.md +124 -0
  11. data/lib/grape.rb +2 -0
  12. data/lib/grape/api.rb +95 -44
  13. data/lib/grape/cookies.rb +0 -2
  14. data/lib/grape/endpoint.rb +63 -39
  15. data/lib/grape/error_formatter/base.rb +0 -3
  16. data/lib/grape/error_formatter/json.rb +0 -2
  17. data/lib/grape/error_formatter/txt.rb +0 -2
  18. data/lib/grape/error_formatter/xml.rb +0 -2
  19. data/lib/grape/exceptions/base.rb +0 -2
  20. data/lib/grape/exceptions/incompatible_option_values.rb +0 -3
  21. data/lib/grape/exceptions/invalid_formatter.rb +0 -3
  22. data/lib/grape/exceptions/invalid_versioner_option.rb +0 -4
  23. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +0 -2
  24. data/lib/grape/exceptions/missing_mime_type.rb +0 -4
  25. data/lib/grape/exceptions/missing_option.rb +0 -3
  26. data/lib/grape/exceptions/missing_vendor_option.rb +0 -3
  27. data/lib/grape/exceptions/unknown_options.rb +0 -4
  28. data/lib/grape/exceptions/unknown_validator.rb +0 -2
  29. data/lib/grape/exceptions/validation_errors.rb +6 -5
  30. data/lib/grape/formatter/base.rb +0 -3
  31. data/lib/grape/formatter/json.rb +0 -2
  32. data/lib/grape/formatter/serializable_hash.rb +15 -16
  33. data/lib/grape/formatter/txt.rb +0 -2
  34. data/lib/grape/formatter/xml.rb +0 -2
  35. data/lib/grape/http/request.rb +2 -4
  36. data/lib/grape/locale/en.yml +1 -1
  37. data/lib/grape/middleware/auth/oauth2.rb +15 -6
  38. data/lib/grape/middleware/base.rb +7 -7
  39. data/lib/grape/middleware/error.rb +11 -6
  40. data/lib/grape/middleware/formatter.rb +80 -78
  41. data/lib/grape/middleware/globals.rb +13 -0
  42. data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
  43. data/lib/grape/middleware/versioner/header.rb +5 -3
  44. data/lib/grape/middleware/versioner/param.rb +2 -4
  45. data/lib/grape/middleware/versioner/path.rb +3 -4
  46. data/lib/grape/namespace.rb +0 -1
  47. data/lib/grape/parser/base.rb +0 -3
  48. data/lib/grape/parser/json.rb +0 -2
  49. data/lib/grape/parser/xml.rb +0 -2
  50. data/lib/grape/path.rb +1 -3
  51. data/lib/grape/route.rb +0 -3
  52. data/lib/grape/util/hash_stack.rb +1 -1
  53. data/lib/grape/validations.rb +72 -22
  54. data/lib/grape/validations/coerce.rb +5 -4
  55. data/lib/grape/validations/default.rb +5 -3
  56. data/lib/grape/validations/presence.rb +1 -1
  57. data/lib/grape/validations/regexp.rb +0 -2
  58. data/lib/grape/validations/values.rb +2 -1
  59. data/lib/grape/version.rb +1 -1
  60. data/spec/grape/api_spec.rb +385 -96
  61. data/spec/grape/endpoint_spec.rb +162 -15
  62. data/spec/grape/entity_spec.rb +25 -0
  63. data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
  64. data/spec/grape/middleware/auth/oauth2_spec.rb +60 -15
  65. data/spec/grape/middleware/base_spec.rb +3 -8
  66. data/spec/grape/middleware/error_spec.rb +2 -2
  67. data/spec/grape/middleware/exception_spec.rb +4 -4
  68. data/spec/grape/middleware/formatter_spec.rb +7 -4
  69. data/spec/grape/middleware/versioner/param_spec.rb +8 -7
  70. data/spec/grape/path_spec.rb +24 -14
  71. data/spec/grape/util/hash_stack_spec.rb +8 -8
  72. data/spec/grape/validations/coerce_spec.rb +75 -33
  73. data/spec/grape/validations/default_spec.rb +57 -0
  74. data/spec/grape/validations/presence_spec.rb +13 -11
  75. data/spec/grape/validations/values_spec.rb +76 -2
  76. data/spec/grape/validations_spec.rb +443 -20
  77. data/spec/spec_helper.rb +2 -2
  78. data/spec/support/content_type_helpers.rb +11 -0
  79. metadata +9 -38
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- N2JkMzEwZGIyN2M4ZWVkMjczNmU1OTZiNTQzYTA3Yjc1YWI1MzRiNw==
4
+ NzU0ZDEzNjI0ZDhlMzg5M2YxOTMzMmU2NmUxMWIyZDYxMGM3ODBiNQ==
5
5
  data.tar.gz: !binary |-
6
- ODE0MmI1YjY3YjZlNjdhOGY0ZGI1OTdjMWRhYmI3N2JiZjJmNWMwYg==
7
- !binary "U0hBNTEy":
6
+ NmMwYmIxODY2ZmI0YzY5MzljYTM0NWE5MDM4MzAwZDQ3M2Y4MmYwNg==
7
+ SHA512:
8
8
  metadata.gz: !binary |-
9
- NWYxMGEyMWE0NWE4ZTlkMjczMmY3ZjA1NGUwZjNhZmJlYjk3MGQ5NTE0NDk3
10
- OGUyOWU5YmVhNzMwMWJkOTIxYjgwZmU0ODk1YWU4Yzc1ZTdkMWY4NTc0MDlh
11
- ODQ4NjczZWRhMGVmMmQ1MDg3Njk5MjBjMTk5N2VjYmM0Y2ZiZDE=
9
+ ODgwYzYzMzlkNzJjMTgyYzkyMmQ3ZDhlNWEyNDRmMGNiZDMxZDI4NDIzZGRk
10
+ NzEwNjdjYjI0MzkzNzE1M2QwM2ExZGM2NDRlMmFlNDJlZWY3NjljMGMxN2Zm
11
+ OWNkMGM0ZGFkMDQwYzk1MDBlNDZkNjI4ZmJmZjM4M2Y3MmU4NDI=
12
12
  data.tar.gz: !binary |-
13
- OTI1ZDU2ZDViNjc2YWE4MTczMThmN2I5ZGYwZGZlYmE0YjZlMDI3NjVlNTE5
14
- NjcwOTRkNTg2NTgwODFhMTg3YjZkM2Y3NzYwYzkwNTYxMGEzMzU2NDBiYzBk
15
- ZGJlYTRmMDEwNGJkNDc3YzNiMmUyM2IyMDUwMGM3MGFhMDhiYTg=
13
+ N2Y5Y2NhNWQxMjNmYmZmMzJhZDQyMzE2NDY1ODRlNjVhNDc2ZjQxNTE4NjRl
14
+ YzczZTk5NGUwZGQ1ZmJiNTUwNmQ5MTM1ZWVkNzA0M2JlNDZhNGVkM2MyNTM5
15
+ Zjg2MzhkNGI5YjMxNzRlNWUyZDg1YzA2NjY4MjdkMWE4MTU1N2Y=
data/.gitignore CHANGED
@@ -35,4 +35,11 @@ tmp
35
35
  ## Rubinius
36
36
  .rbx
37
37
 
38
+ ## Bundler binstubs
39
+ bin
40
+
41
+ ## ripper-tags and gem-ctags
42
+ tags
43
+
38
44
  ## PROJECT::SPECIFIC
45
+ .project
@@ -1,6 +1,7 @@
1
1
  AllCops:
2
2
  Excludes:
3
3
  - vendor/**
4
+ - bin/**
4
5
 
5
6
  LineLength:
6
7
  Enabled: false
@@ -63,3 +64,7 @@ Blocks:
63
64
  WordArray:
64
65
  # %w vs. [ '', ... ]
65
66
  Enabled: false
67
+
68
+ CyclomaticComplexity:
69
+ Enabled: false
70
+
@@ -1,9 +1,9 @@
1
1
  language: ruby
2
2
  cache: bundler
3
+ before_install:
4
+ - gem install bundler -v '= 1.5.1'
3
5
  rvm:
6
+ - 2.1.0
4
7
  - 2.0.0
5
8
  - 1.9.3
6
9
  - jruby-19mode
7
- - rbx-19mode
8
- script:
9
- - "bundle exec rubocop"
@@ -1,5 +1,44 @@
1
- 0.6.1
2
- =====
1
+ 0.7.0 (4/2/2013)
2
+ =================
3
+
4
+ #### Features
5
+ * [#558](https://github.com/intridea/grape/pull/558): Support lambda-based values for params - [@wpschallenger](https://github.com/wpschallenger).
6
+ * [#510](https://github.com/intridea/grape/pull/510): Support lambda-based default values for params - [@myitcv](https://github.com/myitcv).
7
+ * [#511](https://github.com/intridea/grape/pull/511): Added `required` option for OAuth2 middleware - [@bcm](https://github.com/bcm).
8
+ * [#520](https://github.com/intridea/grape/pull/520): Use `default_error_status` to specify the default status code returned from `error!` - [@salimane](https://github.com/salimane).
9
+ * [#525](https://github.com/intridea/grape/pull/525): The default status code returned from `error!` has been changed from 403 to 500 - [@dblock](https://github.com/dblock).
10
+ * [#526](https://github.com/intridea/grape/pull/526): Allowed specifying headers in `error!` - [@dblock](https://github.com/dblock).
11
+ * [#527](https://github.com/intridea/grape/pull/527): The `before_validation` callback is now a distinct one - [@myitcv](https://github.com/myitcv).
12
+ * [#530](https://github.com/intridea/grape/pull/530): Added ability to restrict `declared(params)` to the local endpoint with `include_parent_namespaces: false` - [@myitcv](https://github.com/myitcv).
13
+ * [#531](https://github.com/intridea/grape/pull/531): Helpers are now available to auth middleware, executing in the context of the endpoint - [@joelvh](https://github.com/joelvh).
14
+ * [#540](https://github.com/intridea/grape/pull/540): Ruby 2.1.0 is now supported - [@salimane](https://github.com/salimane).
15
+ * [#544](https://github.com/intridea/grape/pull/544): The `rescue_from` keyword now handles subclasses of exceptions by default - [@xevix](https://github.com/xevix).
16
+ * [#545](https://github.com/intridea/grape/pull/545): Added `type` (`Array` or `Hash`) support to `requires`, `optional` and `group` - [@bwalex](https://github.com/bwalex).
17
+ * [#550](https://github.com/intridea/grape/pull/550): Added possibility to define reusable params - [@dm1try](https://github.com/dm1try).
18
+ * [#560](https://github.com/intridea/grape/pull/560): Use `Grape::Entity` documentation to define required and optional parameters with `requires using:` - [@reynardmh](https://github.com/reynardmh).
19
+ * [#572](https://github.com/intridea/grape/pull/572): Added `documentation` support to `requires`, `optional` and `group` parameters - [@johnallen3d](https://github.com/johnallen3d).
20
+
21
+ #### Fixes
22
+
23
+ * [#600](https://github.com/intridea/grape/pull/600): Don't use an `Entity` constant that is available in the namespace as presenter - [@fuksito](https://github.com/fuksito).
24
+ * [#590](https://github.com/intridea/grape/pull/590): Fix issue where endpoint param of type `Integer` cannot set values array - [@xevix](https://github.com/xevix).
25
+ * [#586](https://github.com/intridea/grape/pull/586): Do not repeat the same validation error messages - [@kiela](https://github.com/kiela).
26
+ * [#508](https://github.com/intridea/grape/pull/508): Allow parameters, such as content encoding, in `content_type` - [@dm1try](https://github.com/dm1try).
27
+ * [#492](https://github.com/intridea/grape/pull/492): Don't allow to have nil value when a param is required and has a list of allowed values - [@Antti](https://github.com/Antti).
28
+ * [#495](https://github.com/intridea/grape/pull/495): Fixed `ParamsScope#params` for parameters nested inside arrays - [@asross](https://github.com/asross).
29
+ * [#498](https://github.com/intridea/grape/pull/498): Dry'ed up options and headers logic, allow headers to be passed to OPTIONS requests - [@karlfreeman](https://github.com/karlfreeman).
30
+ * [#500](https://github.com/intridea/grape/pull/500): Skip entity auto-detection when explicitely passed - [@yaneq](https://github.com/yaneq).
31
+ * [#503](https://github.com/intridea/grape/pull/503): Calling declared(params) from child namespace fails to include parent namespace defined params - [@myitcv](https://github.com/myitcv).
32
+ * [#512](https://github.com/intridea/grape/pull/512): Don't create `Grape::Request` multiple times - [@dblock](https://github.com/dblock).
33
+ * [#538](https://github.com/intridea/grape/pull/538): Fixed default values for grouped params - [@dm1try](https://github.com/dm1try).
34
+ * [#549](https://github.com/intridea/grape/pull/549): Fixed handling of invalid version headers to return 406 if a header cannot be parsed - [@bwalex](https://github.com/bwalex).
35
+ * [#557](https://github.com/intridea/grape/pull/557): Pass `content_types` option to `Grape::Middleware::Error` to fix the content-type header for custom formats. - [@bernd](https://github.com/bernd).
36
+ * [#585](https://github.com/intridea/grape/pull/585): Fix after boot thread-safety issue - [@etehtsea](https://github.com/etehtsea).
37
+ * [#587](https://github.com/intridea/grape/pull/587): Fix oauth2 middleware compatibility with [draft-ietf-oauth-v2-31](http://tools.ietf.org/html/draft-ietf-oauth-v2-31) spec - [@etehtsea](https://github.com/etehtsea).
38
+ * [#610](https://github.com/intridea/grape/pull/610): Fixed group keyword was not working with type parameter - [@klausmeyer](https://github.com/klausmeyer/).
39
+
40
+ 0.6.1 (10/19/2013)
41
+ ==================
3
42
 
4
43
  #### Features
5
44
 
@@ -28,7 +67,7 @@
28
67
  * [#450](https://github.com/intridea/grape/pull/450): Added option to pass an exception handler lambda as an argument to `rescue_from` - [@robertopedroso](https://github.com/robertopedroso).
29
68
  * [#443](https://github.com/intridea/grape/pull/443): Let `requires` and `optional` take blocks that initialize new scopes - [@asross](https://github.com/asross).
30
69
  * [#452](https://github.com/intridea/grape/pull/452): Added `with` as a hash option to specify handlers for `rescue_from` and `error_formatter` [@robertopedroso](https://github.com/robertopedroso).
31
- * [#433](https://github.com/intridea/grape/issues/433), [#462](https://github.com/intridea/grape/issues/462): API change: validation errors are now collected and `Grape::Exceptions::ValidationErrors` is raised - [@stevschmid](https://github.com/stevschmid).
70
+ * [#433](https://github.com/intridea/grape/issues/433), [#462](https://github.com/intridea/grape/issues/462): Validation errors are now collected and `Grape::Exceptions::ValidationErrors` is raised - [@stevschmid](https://github.com/stevschmid).
32
71
 
33
72
  #### Fixes
34
73
 
@@ -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 CHANGED
@@ -10,11 +10,11 @@ group :development, :test do
10
10
  gem 'rb-fsevent'
11
11
  gem 'growl'
12
12
  gem 'json'
13
- gem 'rspec'
14
- gem 'rack-test', "~> 0.6.2", :require => "rack/test"
13
+ gem 'rspec', '~> 2.14.1'
14
+ gem 'rack-test', '~> 0.6.2', require: 'rack/test'
15
15
  gem 'github-markup'
16
16
  gem 'cookiejar'
17
17
  gem 'rack-contrib'
18
- gem 'redcarpet', :platforms => :ruby
19
- gem 'rubocop', '~> 0.14.1'
18
+ gem 'redcarpet', platforms: :ruby
19
+ gem 'rubocop', '~> 0.15.0'
20
20
  end
data/README.md CHANGED
@@ -8,11 +8,11 @@ providing a simple DSL to easily develop RESTful APIs. It has built-in support
8
8
  for common conventions, including multiple formats, subdomain/prefix restriction,
9
9
  content negotiation, versioning and much more.
10
10
 
11
- [![Build Status](https://travis-ci.org/intridea/grape.png?branch=master)](http://travis-ci.org/intridea/grape) [![Code Climate](https://codeclimate.com/github/intridea/grape.png)](https://codeclimate.com/github/intridea/grape)
11
+ [![Build Status](https://travis-ci.org/intridea/grape.png?branch=master)](http://travis-ci.org/intridea/grape) [![Code Climate](https://codeclimate.com/github/intridea/grape.png)](https://codeclimate.com/github/intridea/grape) [![Inline docs](http://inch-pages.github.io/github/intridea/grape.png)](http://inch-pages.github.io/github/intridea/grape) [![Dependency Status](https://www.versioneye.com/ruby/grape/0.6.1/badge.png)](https://www.versioneye.com/ruby/grape/0.6.1)
12
12
 
13
13
  ## Stable Release
14
14
 
15
- You're reading the documentation for the stable release of Grape, 0.6.1.
15
+ You're reading the documentation for the stable release of Grape, 0.7.0.
16
16
 
17
17
  ## Project Resources
18
18
 
@@ -40,7 +40,6 @@ the context of recreating parts of the Twitter API.
40
40
  ```ruby
41
41
  module Twitter
42
42
  class API < Grape::API
43
-
44
43
  version 'v1', using: :header, vendor: 'twitter'
45
44
  format :json
46
45
 
@@ -55,7 +54,6 @@ module Twitter
55
54
  end
56
55
 
57
56
  resource :statuses do
58
-
59
57
  desc "Return a public timeline."
60
58
  get :public_timeline do
61
59
  Status.limit(20)
@@ -110,7 +108,6 @@ module Twitter
110
108
  authenticate!
111
109
  current_user.statuses.find(params[:id]).destroy
112
110
  end
113
-
114
111
  end
115
112
  end
116
113
  end
@@ -151,7 +148,7 @@ require 'grape'
151
148
 
152
149
  class API < Grape::API
153
150
  get :hello do
154
- {hello: "world"}
151
+ { hello: "world" }
155
152
  end
156
153
  end
157
154
 
@@ -170,8 +167,8 @@ run Rack::Cascade.new [API, Web]
170
167
  Place API files into `app/api` and modify `application.rb`.
171
168
 
172
169
  ```ruby
173
- config.paths.add "app/api", glob: "**/*.rb"
174
- config.autoload_paths += Dir["#{Rails.root}/app/api/*"]
170
+ config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
171
+ config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
175
172
  ```
176
173
 
177
174
  Modify `config/routes`:
@@ -232,6 +229,10 @@ supplied. This behavior is similar to routing in Rails. To circumvent this defau
232
229
  one could use the `:strict` option. When this option is set to `true`, a `406 Not Acceptable` error
233
230
  is returned when no correct `Accept` header is supplied.
234
231
 
232
+ When an invalid `Accept` header is supplied, a `406 Not Acceptable` error is returned if the `:cascade`
233
+ option is set to `false`. Otherwise a `404 Not Found` error is returned by Rack if no other route
234
+ matches.
235
+
235
236
  ### Accept-Version Header
236
237
 
237
238
  ```ruby
@@ -289,7 +290,7 @@ get :public_timeline do
289
290
  end
290
291
  ```
291
292
 
292
- Parameters are automatically populated from the request body on POST and PUT for form input, JSON and
293
+ Parameters are automatically populated from the request body on `POST` and `PUT` for form input, JSON and
293
294
  XML content-types.
294
295
 
295
296
  The request:
@@ -302,7 +303,7 @@ The Grape endpoint:
302
303
 
303
304
  ```ruby
304
305
  post '/statuses' do
305
- Status.create!({ text: params[:text] })
306
+ Status.create!(text: params[:text])
306
307
  end
307
308
  ```
308
309
 
@@ -311,7 +312,7 @@ Multipart POSTs and PUTs are supported as well.
311
312
  The request:
312
313
 
313
314
  ```
314
- curl --form image_file=image.jpg http://localhost:9292/upload
315
+ curl --form image_file=@image.jpg http://localhost:9292/upload
315
316
  ```
316
317
 
317
318
  The Grape endpoint:
@@ -322,6 +323,14 @@ post "upload" do
322
323
  end
323
324
  ```
324
325
 
326
+ In the case of conflict between either of:
327
+
328
+ * route string parameters
329
+ * `GET`, `POST` and `PUT` parameters
330
+ * the contents of the request body on `POST` and `PUT`
331
+
332
+ route string parameters will have precedence.
333
+
325
334
  ## Parameter Validation and Coercion
326
335
 
327
336
  You can define validations and coercion options for your parameters using a `params` block.
@@ -350,20 +359,52 @@ Optional parameters can have a default value.
350
359
  ```ruby
351
360
  params do
352
361
  optional :color, type: String, default: 'blue'
362
+ optional :random_number, type: Integer, default: -> { Random.rand(1..100) }
363
+ optional :non_random_number, type: Integer, default: Random.rand(1..100)
353
364
  end
354
365
  ```
355
366
 
356
367
  Parameters can be restricted to a specific set of values with the `:values` option.
357
368
 
369
+ Default values are eagerly evaluated. Above `:non_random_number` will evaluate to the same
370
+ number for each call to the endpoint of this `params` block. To have the default evaluate
371
+ at calltime use a lambda, like `:random_number` above.
372
+
358
373
  ```ruby
359
374
  params do
360
375
  requires :status, type: Symbol, values: [:not_started, :processing, :done]
361
376
  end
362
377
  ```
363
378
 
379
+ The :values option can also be supplied with a `Proc` to be evalutated at runtime. For example, given a status
380
+ model you may want to restrict by hashtags that you have previously defined in the `HashTag` model.
381
+
382
+ ```ruby
383
+ params do
384
+ required :hashtag, type: String, values: -> { Hashtag.all.map(&:tag) }
385
+ end
386
+ ```
387
+
364
388
  Parameters can be nested using `group` or by calling `requires` or `optional` with a block.
365
389
  In the above example, this means `params[:media][:url]` is required along with `params[:id]`,
366
- and `params[:audio][:mp3]` is required only if `params[:audio]` is present.
390
+ and `params[:audio][:format]` is required only if `params[:audio]` is present.
391
+ With a block, `group`, `requires` and `optional` accept an additional option `type` which can
392
+ be either `Array` or `Hash`, and defaults to `Array`. Depending on the value, the nested
393
+ parameters will be treated either as values of a hash or as values of hashes in an array.
394
+
395
+ ```ruby
396
+ params do
397
+ optional :preferences, type: Array do
398
+ requires :key
399
+ requires :value
400
+ end
401
+
402
+ requires :name, type: Hash do
403
+ requires :first_name
404
+ requires :last_name
405
+ end
406
+ end
407
+ ```
367
408
 
368
409
  ### Namespace Validation and Coercion
369
410
 
@@ -389,6 +430,23 @@ end
389
430
  The `namespace` method has a number of aliases, including: `group`, `resource`,
390
431
  `resources`, and `segment`. Use whichever reads the best for your API.
391
432
 
433
+ You can conveniently define a route parameter as a namespace using `route_param`.
434
+
435
+ ```ruby
436
+ namespace :statuses do
437
+ route_param :id do
438
+ desc "Returns all replies for a status."
439
+ get 'replies' do
440
+ Status.find(params[:id]).replies
441
+ end
442
+ desc "Returns a status."
443
+ get do
444
+ Status.find(params[:id])
445
+ end
446
+ end
447
+ end
448
+ ```
449
+
392
450
  ### Custom Validators
393
451
 
394
452
  ```ruby
@@ -434,9 +492,9 @@ You can rescue a `Grape::Exceptions::ValidationErrors` and respond with a custom
434
492
  ```ruby
435
493
  rescue_from Grape::Exceptions::ValidationErrors do |e|
436
494
  Rack::Response.new({
437
- 'status' => e.status,
438
- 'message' => e.message,
439
- 'errors' => e.errors
495
+ status: e.status,
496
+ message: e.message,
497
+ errors: e.errors
440
498
  }.to_json, e.status)
441
499
  end
442
500
  ```
@@ -448,7 +506,6 @@ The validation errors are grouped by parameter name and can be accessed via ``Gr
448
506
  Grape supports I18n for parameter-related error messages, but will fallback to English if
449
507
  translations for the default locale have not been provided. See [en.yml](lib/grape/locale/en.yml) for message keys.
450
508
 
451
-
452
509
  ## Headers
453
510
 
454
511
  Request headers are available through the `headers` helper or from `env` in their original form.
@@ -468,7 +525,13 @@ end
468
525
  You can set a response header with `header` inside an API.
469
526
 
470
527
  ```ruby
471
- header "X-Robots-Tag", "noindex"
528
+ header 'X-Robots-Tag', 'noindex'
529
+ ```
530
+
531
+ When raising `error!`, pass additional headers as arguments.
532
+
533
+ ```ruby
534
+ error! 'Unauthorized', 401, 'X-Error-Detail' => 'Invalid token.'
472
535
  ```
473
536
 
474
537
  ## Routes
@@ -520,13 +583,75 @@ class API < Grape::API
520
583
  end
521
584
  ```
522
585
 
586
+ You can define reusable `params` using `helpers`.
587
+
588
+ ```ruby
589
+ class API < Grape::API
590
+ helpers do
591
+ params :pagination do
592
+ optional :page, type: Integer
593
+ optional :per_page, type: Integer
594
+ end
595
+ end
596
+
597
+ desc "Get collection"
598
+ params do
599
+ use :pagination # aliases: includes, use_scope
600
+ end
601
+ get do
602
+ Collection.page(params[:page]).per(params[:per_page])
603
+ end
604
+ end
605
+ ```
606
+
607
+ You can also define reusable `params` using shared helpers.
608
+
609
+ ```ruby
610
+ module SharedParams
611
+ extend Grape::API::Helpers
612
+
613
+ params :period do
614
+ optional :start_date
615
+ optional :end_date
616
+ end
617
+
618
+ params :pagination do
619
+ optional :page, type: Integer
620
+ optional :per_page, type: Integer
621
+ end
622
+ end
623
+
624
+ class API < Grape::API
625
+ helpers SharedParams
626
+
627
+ desc "Get collection"
628
+ params do
629
+ use :period, :pagination
630
+ end
631
+ get do
632
+ Collection.from(params[:start_date]).to(params[:end_date])
633
+ .page(params[:page]).per(params[:per_page])
634
+ end
635
+ end
636
+ ```
637
+
638
+ ## Parameter Documentation
639
+
640
+ You can attach additional documentation to `params` using a `documentation` hash.
641
+
642
+ ```ruby
643
+ params do
644
+ optional :first_name, type: String, documentation: { example: 'Jim' }
645
+ requires :last_name, type: String, documentation: { example: 'Smith' }
646
+ end
647
+ ```
648
+
523
649
  ## Cookies
524
650
 
525
651
  You can set, get and delete your cookies very simply using `cookies` method.
526
652
 
527
653
  ```ruby
528
654
  class API < Grape::API
529
-
530
655
  get 'status_count' do
531
656
  cookies[:status_count] ||= 0
532
657
  cookies[:status_count] += 1
@@ -536,7 +661,6 @@ class API < Grape::API
536
661
  delete 'status_count' do
537
662
  { status_count: cookies.delete(:status_count) }
538
663
  end
539
-
540
664
  end
541
665
  ```
542
666
 
@@ -544,10 +668,10 @@ Use a hash-based syntax to set more than one value.
544
668
 
545
669
  ```ruby
546
670
  cookies[:status_count] = {
547
- value: 0,
548
- expires: Time.tomorrow,
549
- domain: '.twitter.com',
550
- path: '/'
671
+ value: 0,
672
+ expires: Time.tomorrow,
673
+ domain: '.twitter.com',
674
+ path: '/'
551
675
  }
552
676
 
553
677
  cookies[:status_count][:value] +=1
@@ -570,11 +694,11 @@ cookies.delete :status_count, path: '/'
570
694
  You can redirect to a new url temporarily (302) or permanently (301).
571
695
 
572
696
  ```ruby
573
- redirect "/statuses"
697
+ redirect '/statuses'
574
698
  ```
575
699
 
576
700
  ```ruby
577
- redirect "/statuses", permanent: true
701
+ redirect '/statuses', permanent: true
578
702
  ```
579
703
 
580
704
  ## Allowed Methods
@@ -585,13 +709,11 @@ behavior with `do_not_route_head!`.
585
709
 
586
710
  ``` ruby
587
711
  class API < Grape::API
588
-
589
712
  do_not_route_head!
590
713
 
591
714
  get '/example' do
592
715
  # only responds to GET
593
716
  end
594
-
595
717
  end
596
718
  ```
597
719
 
@@ -601,7 +723,6 @@ include an "Allow" header listing the supported methods.
601
723
 
602
724
  ```ruby
603
725
  class API < Grape::API
604
-
605
726
  get '/rt_count' do
606
727
  { rt_count: current_user.rt_count }
607
728
  end
@@ -613,7 +734,6 @@ class API < Grape::API
613
734
  current_user.rt_count += params[:value].to_i
614
735
  { rt_count: current_user.rt_count }
615
736
  end
616
-
617
737
  end
618
738
  ```
619
739
 
@@ -646,14 +766,27 @@ curl -X DELETE -v http://localhost:3000/rt_count/
646
766
  You can abort the execution of an API method by raising errors with `error!`.
647
767
 
648
768
  ```ruby
649
- error! "Access Denied", 401
769
+ error! 'Access Denied', 401
650
770
  ```
651
771
 
652
772
  You can also return JSON formatted objects by raising error! and passing a hash
653
773
  instead of a message.
654
774
 
655
775
  ```ruby
656
- error!({ "error" => "unexpected error", "detail" => "missing widget" }, 500)
776
+ error!({ error: "unexpected error", detail: "missing widget" }, 500)
777
+ ```
778
+
779
+ ### Default Error HTTP Status Code
780
+
781
+ By default Grape returns a 500 status code from `error!`. You can change this with `default_error_status`.
782
+
783
+ ``` ruby
784
+ class API < Grape::API
785
+ default_error_status 400
786
+ get '/example' do
787
+ error! "This should have http status code 400"
788
+ end
789
+ end
657
790
  ```
658
791
 
659
792
  ### Handling 404
@@ -684,10 +817,12 @@ You can also rescue specific exceptions.
684
817
 
685
818
  ```ruby
686
819
  class Twitter::API < Grape::API
687
- rescue_from ArgumentError, NotImplementedError
820
+ rescue_from ArgumentError, UserDefinedError
688
821
  end
689
822
  ```
690
823
 
824
+ In this case ```UserDefinedError``` must be inherited from ```StandardError```.
825
+
691
826
  The error format will match the request format. See "Content-Types" below.
692
827
 
693
828
  Custom error formatters for existing and additional types can be defined with a proc.
@@ -741,14 +876,49 @@ Or rescue specific exceptions.
741
876
  ```ruby
742
877
  class Twitter::API < Grape::API
743
878
  rescue_from ArgumentError do |e|
744
- Rack::Response.new([ "ArgumentError: #{e.message}" ], 500)
879
+ Rack::Response.new([ "ArgumentError: #{e.message}" ], 500).finish
745
880
  end
746
881
  rescue_from NotImplementedError do |e|
747
- Rack::Response.new([ "NotImplementedError: #{e.message}" ], 500)
882
+ Rack::Response.new([ "NotImplementedError: #{e.message}" ], 500).finish
748
883
  end
749
884
  end
750
885
  ```
751
886
 
887
+ By default, `rescue_from` will rescue the exceptions listed and all their subclasses.
888
+
889
+ Assume you have the following exception classes defined.
890
+
891
+ ```ruby
892
+ module APIErrors
893
+ class ParentError < StandardError; end
894
+ class ChildError < ParentError; end
895
+ end
896
+ ```
897
+
898
+ Then the following `rescue_from` clause will rescue exceptions of type `APIErrors::ParentError` and its subclasses (in this case `APIErrors::ChildError`).
899
+
900
+ ```ruby
901
+ rescue_from APIErrors::ParentError do |e|
902
+ Rack::Response.new({
903
+ error: "#{e.class} error",
904
+ message: e.message
905
+ }.to_json, e.status).finish
906
+ end
907
+ ```
908
+
909
+ To only rescue the base exception class, set `rescue_subclasses: false`.
910
+ The code below will rescue exceptions of type `RuntimeError` but _not_ its subclasses.
911
+
912
+ ```ruby
913
+ rescue_from RuntimeError, rescue_subclasses: false do |e|
914
+ Rack::Response.new(
915
+ status: e.status,
916
+ message: e.message,
917
+ errors: e.errors
918
+ }.to_json, e.status).finish
919
+ end
920
+ ```
921
+
752
922
  #### Rails 3.x
753
923
 
754
924
  When mounted inside containers, such as Rails 3.x, errors like "404 Not Found" or
@@ -1008,7 +1178,6 @@ The following example exposes statuses.
1008
1178
 
1009
1179
  ```ruby
1010
1180
  module API
1011
-
1012
1181
  module Entities
1013
1182
  class Status < Grape::Entity
1014
1183
  expose :user_name
@@ -1024,7 +1193,7 @@ module API
1024
1193
  version 'v1'
1025
1194
 
1026
1195
  desc 'Statuses index', {
1027
- object_fields: API::Entities::Status.documentation
1196
+ params: API::Entities::Status.documentation
1028
1197
  }
1029
1198
  get '/statuses' do
1030
1199
  statuses = Status.all
@@ -1035,6 +1204,23 @@ module API
1035
1204
  end
1036
1205
  ```
1037
1206
 
1207
+ You can use entity documentation directly in the params block with `using: Entity.documentation`.
1208
+
1209
+ ```ruby
1210
+ module API
1211
+ class Statuses < Grape::API
1212
+ version 'v1'
1213
+
1214
+ desc 'Create a status', {
1215
+ requires :all, except: [:ip], using: API::Entities::Status.documentation.except(:id)
1216
+ }
1217
+ post '/status' do
1218
+ Status.create! params
1219
+ end
1220
+ end
1221
+ end
1222
+ ```
1223
+
1038
1224
  You can present with multiple entities using an optional Symbol argument.
1039
1225
 
1040
1226
  ```ruby
@@ -1062,7 +1248,7 @@ classes underneath the model they represent.
1062
1248
  ```ruby
1063
1249
  class Status
1064
1250
  def entity
1065
- Status.new(self)
1251
+ Entity.new(self)
1066
1252
  end
1067
1253
 
1068
1254
  class Entity < Grape::Entity
@@ -1083,14 +1269,19 @@ You can use any Hypermedia representer, including [Roar](https://github.com/apot
1083
1269
  Roar renders JSON and works with the built-in Grape JSON formatter. Add `Roar::Representer::JSON`
1084
1270
  into your models or call `to_json` explicitly in your API implementation.
1085
1271
 
1086
- Other alternatives include `ActiveModel::Serializers` via [grape-active_model_serializers](https://github.com/jrhe/grape-active_model_serializers).
1087
-
1088
1272
  ### Rabl
1089
1273
 
1090
1274
  You can use [Rabl](https://github.com/nesquena/rabl) templates with the help of the
1091
1275
  [grape-rabl](https://github.com/LTe/grape-rabl) gem, which defines a custom Grape Rabl
1092
1276
  formatter.
1093
1277
 
1278
+ ### Active Model Serializers
1279
+
1280
+ You can use [Active Model Serializers](https://github.com/rails-api/active_model_serializers) serializers with the help of the
1281
+ [grape-active_model_serializers](https://github.com/jrhe/grape-active_model_serializers) gem, which defines a custom Grape AMS
1282
+ formatter.
1283
+
1284
+
1094
1285
  ## Authentication
1095
1286
 
1096
1287
  ### Basic and Digest Auth
@@ -1165,7 +1356,21 @@ end
1165
1356
 
1166
1357
  ## Before and After
1167
1358
 
1168
- Execute a block before or after every API call with `before` and `after`.
1359
+ Blocks can be executed before or after every API call, using `before`, `after`,
1360
+ `before_validation` and `after_validation`.
1361
+
1362
+ Before and after callbacks execute in the following order:
1363
+
1364
+ 1. `before`
1365
+ 2. `before_validation`
1366
+ 3. _validations_
1367
+ 4. `after_validation`
1368
+ 5. _the API call_
1369
+ 6. `after`
1370
+
1371
+ Steps 4, 5 and 6 only happen if validation succeeds.
1372
+
1373
+ E.g. using `before`:
1169
1374
 
1170
1375
  ```ruby
1171
1376
  before do
@@ -1173,6 +1378,68 @@ before do
1173
1378
  end
1174
1379
  ```
1175
1380
 
1381
+ The block applies to every API call within and below the current namespace:
1382
+
1383
+ ```ruby
1384
+ class MyAPI < Grape::API
1385
+ get '/' do
1386
+ "root - #{@blah}"
1387
+ end
1388
+
1389
+ namespace :foo do
1390
+ before do
1391
+ @blah = 'blah'
1392
+ end
1393
+
1394
+ get '/' do
1395
+ "root - foo - #{@blah}"
1396
+ end
1397
+
1398
+ namespace :bar do
1399
+ get '/' do
1400
+ "root - foo - bar - #{@blah}"
1401
+ end
1402
+ end
1403
+ end
1404
+ end
1405
+ ```
1406
+
1407
+ The behaviour is then:
1408
+
1409
+ ```bash
1410
+ GET / # 'root - '
1411
+ GET /foo # 'root - foo - blah'
1412
+ GET /foo/bar # 'root - foo - bar - blah'
1413
+ ```
1414
+
1415
+ Params on a `namespace` (or whatever alias you are using) also work when using
1416
+ `before_validation` or `after_validation`:
1417
+
1418
+ ```ruby
1419
+ class MyAPI < Grape::API
1420
+ params do
1421
+ requires :blah, type: Integer
1422
+ end
1423
+ resource ':blah' do
1424
+ after_validation do
1425
+ # if we reach this point validations will have passed
1426
+ @blah = declared(params, include_missing: false)[:blah]
1427
+ end
1428
+
1429
+ get '/' do
1430
+ @blah.class
1431
+ end
1432
+ end
1433
+ end
1434
+ ```
1435
+
1436
+ The behaviour is then:
1437
+
1438
+ ```bash
1439
+ GET /123 # 'Fixnum'
1440
+ GET /foo # 400 error - 'blah is invalid'
1441
+ ```
1442
+
1176
1443
  ## Anchoring
1177
1444
 
1178
1445
  Grape by default anchors all request paths, which means that the request URL
@@ -1281,31 +1548,28 @@ Add API paths to `config/application.rb`.
1281
1548
 
1282
1549
  ```ruby
1283
1550
  # Auto-load API and its subdirectories
1284
- config.paths.add "app/api", glob: "**/*.rb"
1285
- config.autoload_paths += Dir["#{Rails.root}/app/api/*"]
1551
+ config.paths.add File.join("app", "api"), glob: File.join("**", "*.rb")
1552
+ config.autoload_paths += Dir[Rails.root.join("app", "api", "*")]
1286
1553
  ```
1287
1554
 
1288
1555
  Create `config/initializers/reload_api.rb`.
1289
1556
 
1290
1557
  ```ruby
1291
1558
  if Rails.env.development?
1292
-
1293
1559
  ActiveSupport::Dependencies.explicitly_unloadable_constants << "Twitter::API"
1294
1560
 
1295
- api_files = Dir["#{Rails.root}/app/api/**/*.rb"]
1561
+ api_files = Dir[Rails.root.join('app', 'api', '**', '*.rb')]
1296
1562
  api_reloader = ActiveSupport::FileUpdateChecker.new(api_files) do
1297
1563
  Rails.application.reload_routes!
1298
1564
  end
1299
1565
  ActionDispatch::Callbacks.to_prepare do
1300
1566
  api_reloader.execute_if_updated
1301
1567
  end
1302
-
1303
1568
  end
1304
1569
  ```
1305
1570
 
1306
1571
  See [StackOverflow #3282655](http://stackoverflow.com/questions/3282655/ruby-on-rails-3-reload-lib-directory-for-each-request/4368838#4368838) for more information.
1307
1572
 
1308
-
1309
1573
  ## Performance Monitoring
1310
1574
 
1311
1575
  Grape integrates with NewRelic via the
@@ -1314,14 +1578,10 @@ with Librato Metrics with the [grape-librato](https://github.com/seanmoon/grape-
1314
1578
 
1315
1579
  ## Contributing to Grape
1316
1580
 
1317
- Grape is work of dozens of contributors. You're encouraged to submit pull requests, propose
1581
+ Grape is work of hundreds of contributors. You're encouraged to submit pull requests, propose
1318
1582
  features and discuss issues.
1319
1583
 
1320
- * Fork the project
1321
- * Write tests for your new feature or a test that reproduces a bug
1322
- * Implement your feature or make a bug fix
1323
- * Add a line to `CHANGELOG.md` describing your change
1324
- * Commit, push and make a pull request. Bonus points for topic branches.
1584
+ See [CONTRIBUTING](CONTRIBUTING.md).
1325
1585
 
1326
1586
  ## License
1327
1587