grape 0.18.0 → 0.19.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +52 -69
  3. data/Gemfile +3 -3
  4. data/Gemfile.lock +37 -41
  5. data/README.md +49 -7
  6. data/UPGRADING.md +56 -1
  7. data/gemfiles/rack_edge.gemfile +34 -0
  8. data/gemfiles/rails_5.gemfile +1 -1
  9. data/gemfiles/rails_edge.gemfile +34 -0
  10. data/lib/grape/api.rb +1 -1
  11. data/lib/grape/dsl/helpers.rb +1 -0
  12. data/lib/grape/dsl/inside_route.rb +2 -0
  13. data/lib/grape/dsl/parameters.rb +24 -12
  14. data/lib/grape/dsl/request_response.rb +2 -3
  15. data/lib/grape/dsl/routing.rb +4 -0
  16. data/lib/grape/endpoint.rb +15 -16
  17. data/lib/grape/error_formatter/base.rb +1 -0
  18. data/lib/grape/formatter/serializable_hash.rb +2 -2
  19. data/lib/grape/http/headers.rb +2 -0
  20. data/lib/grape/middleware/base.rb +3 -4
  21. data/lib/grape/middleware/error.rb +1 -1
  22. data/lib/grape/middleware/versioner/path.rb +1 -1
  23. data/lib/grape/router.rb +37 -21
  24. data/lib/grape/router/attribute_translator.rb +1 -1
  25. data/lib/grape/router/pattern.rb +9 -1
  26. data/lib/grape/router/route.rb +4 -0
  27. data/lib/grape/validations/params_scope.rb +24 -6
  28. data/lib/grape/validations/validators/base.rb +1 -2
  29. data/lib/grape/validations/validators/regexp.rb +2 -1
  30. data/lib/grape/version.rb +1 -1
  31. data/pkg/grape-0.17.0.gem +0 -0
  32. data/spec/grape/api/patch_method_helpers_spec.rb +1 -2
  33. data/spec/grape/api_spec.rb +87 -21
  34. data/spec/grape/dsl/desc_spec.rb +2 -4
  35. data/spec/grape/dsl/inside_route_spec.rb +29 -22
  36. data/spec/grape/dsl/parameters_spec.rb +15 -1
  37. data/spec/grape/endpoint_spec.rb +53 -19
  38. data/spec/grape/middleware/formatter_spec.rb +39 -30
  39. data/spec/grape/middleware/versioner/param_spec.rb +15 -10
  40. data/spec/grape/middleware/versioner/path_spec.rb +4 -3
  41. data/spec/grape/util/inheritable_setting_spec.rb +2 -1
  42. data/spec/grape/util/strict_hash_configuration_spec.rb +1 -2
  43. data/spec/grape/validations/params_scope_spec.rb +182 -0
  44. data/spec/grape/validations/validators/default_spec.rb +1 -3
  45. data/spec/grape/validations/validators/presence_spec.rb +29 -1
  46. data/spec/grape/validations/validators/regexp_spec.rb +88 -0
  47. metadata +5 -3
  48. data/pkg/grape-0.18.0.gem +0 -0
data/README.md CHANGED
@@ -38,6 +38,7 @@
38
38
  - [Multiple Allowed Types](#multiple-allowed-types)
39
39
  - [Validation of Nested Parameters](#validation-of-nested-parameters)
40
40
  - [Dependent Parameters](#dependent-parameters)
41
+ - [Group Options](#group-options)
41
42
  - [Built-in Validators](#built-in-validators)
42
43
  - [Namespace Validation and Coercion](#namespace-validation-and-coercion)
43
44
  - [Custom Validators](#custom-validators)
@@ -104,7 +105,7 @@ content negotiation, versioning and much more.
104
105
 
105
106
  ## Stable Release
106
107
 
107
- You're reading the documentation for the stable release of Grape.
108
+ You're reading the documentation for the stable release of Grape, 0.19.0.
108
109
  Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
109
110
 
110
111
  ## Project Resources
@@ -1000,6 +1001,35 @@ params do
1000
1001
  end
1001
1002
  ```
1002
1003
 
1004
+
1005
+ ### Group Options
1006
+
1007
+ Parameters options can be grouped. It can be useful if you want to extract
1008
+ common validation or types for several parameters. The example below presents a
1009
+ typical case when parameters share common options.
1010
+
1011
+ ```ruby
1012
+ params do
1013
+ requires :first_name, type: String, regexp: /w+/, desc: 'First name'
1014
+ requires :middle_name, type: String, regexp: /w+/, desc: 'Middle name'
1015
+ requires :last_name, type: String, regexp: /w+/, desc: 'Last name'
1016
+ end
1017
+ ```
1018
+
1019
+ Grape allows you to present the same logic through the `with` method in your
1020
+ parameters block, like so:
1021
+
1022
+ ```ruby
1023
+ params do
1024
+ with(type: String, regexp: /w+/) do
1025
+ requires :first_name, desc: 'First name'
1026
+ requires :middle_name, desc: 'Middle name'
1027
+ requires :last_name, desc: 'Last name'
1028
+ end
1029
+ end
1030
+ ```
1031
+
1032
+
1003
1033
  ### Built-in Validators
1004
1034
 
1005
1035
  #### `allow_blank`
@@ -1075,12 +1105,12 @@ end
1075
1105
  ```
1076
1106
 
1077
1107
  Values and except can be combined to define a range of accepted values while not allowing
1078
- certain values within the set. Custom error messages can be defined for both when the parameter
1079
- passed falls within the ```except``` list or when it falls entirely outside the ```value``` list.
1108
+ certain values within the set. Custom error messages can be defined for both when the parameter
1109
+ passed falls within the ```except``` list or when it falls entirely outside the ```value``` list.
1080
1110
 
1081
1111
  ```ruby
1082
1112
  params do
1083
- requires :number, type: Integer, values: { value: 1..20 except: [4,13], except_message: 'includes unsafe numbers', message: 'is outside the range of numbers allowed' }
1113
+ requires :number, type: Integer, values: { value: 1..20, except: [4, 13], except_message: 'includes unsafe numbers', message: 'is outside the range of numbers allowed' }
1084
1114
  end
1085
1115
  ```
1086
1116
 
@@ -1710,7 +1740,7 @@ cookies.delete :status_count, path: '/'
1710
1740
 
1711
1741
  ## HTTP Status Code
1712
1742
 
1713
- By default Grape returns a 200 status code for `GET`-Requests and 201 for `POST`-Requests.
1743
+ By default Grape returns a 201 for `POST`-Requests, 204 for `DELETE`-Requests and 200 status code for all other Requests.
1714
1744
  You can use `status` to query and set the actual HTTP Status Code
1715
1745
 
1716
1746
  ```ruby
@@ -1776,7 +1806,9 @@ end
1776
1806
 
1777
1807
  When you add a route for a resource, a route for the `OPTIONS`
1778
1808
  method will also be added. The response to an OPTIONS request will
1779
- include an "Allow" header listing the supported methods.
1809
+ include an "Allow" header listing the supported methods. If the resource
1810
+ has `before` and `after` callbacks they will be executed, but no other callbacks will
1811
+ run.
1780
1812
 
1781
1813
  ```ruby
1782
1814
  class API < Grape::API
@@ -1806,7 +1838,9 @@ curl -v -X OPTIONS http://localhost:3000/rt_count
1806
1838
  You can disable this behavior with `do_not_route_options!`.
1807
1839
 
1808
1840
  If a request for a resource is made with an unsupported HTTP method, an
1809
- HTTP 405 (Method Not Allowed) response will be returned.
1841
+ HTTP 405 (Method Not Allowed) response will be returned. If the resource
1842
+ has `before` callbacks they will be executed, but no other callbacks will
1843
+ run.
1810
1844
 
1811
1845
  ``` shell
1812
1846
  curl -X DELETE -v http://localhost:3000/rt_count/
@@ -2766,6 +2800,14 @@ Before and after callbacks execute in the following order:
2766
2800
 
2767
2801
  Steps 4, 5 and 6 only happen if validation succeeds.
2768
2802
 
2803
+ If a request for a resource is made with an unsupported HTTP method (returning
2804
+ HTTP 405) only `before` callbacks will be executed. The remaining callbacks will
2805
+ be bypassed.
2806
+
2807
+ If a request for a resource is made that triggers the built-in `OPTIONS` handler,
2808
+ only `before` and `after` callbacks will be executed. The remaining callbacks will
2809
+ be bypassed.
2810
+
2769
2811
  #### Examples
2770
2812
 
2771
2813
  Using a simple `before` block to set a header
data/UPGRADING.md CHANGED
@@ -1,6 +1,61 @@
1
1
  Upgrading Grape
2
2
  ===============
3
3
 
4
+ ### Upgrading to >= 0.18.1
5
+
6
+ #### Changes in priority of :any routes
7
+
8
+ Prior to this version, `:any` routes were searched after matching first route and 405 routes. This behavior has changed and `:any` routes are now searched before 405 processing. In the following example the `:any` route will match first when making a request with an unsupported verb.
9
+
10
+ ```ruby
11
+ post :example do
12
+ 'example'
13
+ end
14
+ route :any, '*path' do
15
+ error! :not_found, 404
16
+ end
17
+
18
+ get '/example' #=> before: 405, after: 404
19
+ ```
20
+
21
+ #### Removed param processing from built-in OPTIONS handler
22
+
23
+ When a request is made to the built-in `OPTIONS` handler, only the `before` and `after`
24
+ callbacks associated with the resource will be run. The `before_validation` and
25
+ `after_validation` callbacks and parameter validations will be skipped.
26
+
27
+ See [#1505](https://github.com/ruby-grape/grape/pull/1505) for more information.
28
+
29
+ #### Changed endpoint params validation
30
+
31
+ Grape now correctly returns validation errors for all params when multiple params are passed to a requires.
32
+ The following code will return `one is missing, two is missing` when calling the endpoint without parameters.
33
+
34
+ ```ruby
35
+ params do
36
+ requires :one, :two
37
+ end
38
+ ```
39
+
40
+ Prior to this version the response would be `one is missing`.
41
+
42
+ See [#1510](https://github.com/ruby-grape/grape/pull/1510) for more information.
43
+
44
+ #### The default status code for DELETE is now 204 instead of 200.
45
+
46
+ Breaking change: Sets the default response status code for a delete request to 204.
47
+ A status of 204 makes the response more distinguishable and therefore easier to handle on the client side, particularly because a DELETE request typically returns an empty body as the resource was deleted or voided.
48
+
49
+ To achieve the old behavior, one has to set it explicitly:
50
+ ```ruby
51
+ delete :id do
52
+ status 200
53
+ 'foo successfully deleted'
54
+ end
55
+ ```
56
+
57
+ For more information see: [#1532](https://github.com/ruby-grape/grape/pull/1532).
58
+
4
59
  ### Upgrading to >= 0.17.0
5
60
 
6
61
  #### Removed official support for Ruby < 2.2.2
@@ -14,7 +69,7 @@ See [#1441](https://github.com/ruby-grape/grape/pull/1441) for nmore information
14
69
  The `rescue_from` clauses declared inside a namespace would take a priority over ones declared in the root scope.
15
70
  This could possibly affect those users who use different `rescue_from` clauses in root scope and in namespaces.
16
71
 
17
- See [#1405](https://github.com/ruby-grape/grape/pull/1405) for more inforomation.
72
+ See [#1405](https://github.com/ruby-grape/grape/pull/1405) for more information.
18
73
 
19
74
  #### Helper methods injected inside `rescue_from` in middleware
20
75
 
@@ -0,0 +1,34 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'rack', github: 'rack/rack'
6
+
7
+ group :development, :test do
8
+ gem 'bundler'
9
+ gem 'rake'
10
+ gem 'rubocop', '~> 0.45.0'
11
+ end
12
+
13
+ group :development do
14
+ gem 'guard'
15
+ gem 'guard-rspec'
16
+ gem 'guard-rubocop'
17
+ gem 'yard'
18
+ gem 'appraisal'
19
+ gem 'benchmark-ips'
20
+ gem 'redcarpet'
21
+ end
22
+
23
+ group :test do
24
+ gem 'grape-entity', '~> 0.6'
25
+ gem 'maruku'
26
+ gem 'rack-test'
27
+ gem 'rspec', '~> 3.0'
28
+ gem 'cookiejar'
29
+ gem 'rack-jsonp', require: 'rack/jsonp'
30
+ gem 'mime-types', '~> 3.0'
31
+ gem 'ruby-grape-danger', '~> 0.1.0', require: false
32
+ end
33
+
34
+ gemspec path: '../'
@@ -7,7 +7,7 @@ gem 'rails', '5.0.0'
7
7
  group :development, :test do
8
8
  gem 'bundler'
9
9
  gem 'rake'
10
- gem 'rubocop', '0.39.0'
10
+ gem 'rubocop', '0.45.0'
11
11
  end
12
12
 
13
13
  group :development do
@@ -0,0 +1,34 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'rails', github: 'rails/rails'
6
+
7
+ group :development, :test do
8
+ gem 'bundler'
9
+ gem 'rake'
10
+ gem 'rubocop', '~> 0.45.0'
11
+ end
12
+
13
+ group :development do
14
+ gem 'guard'
15
+ gem 'guard-rspec'
16
+ gem 'guard-rubocop'
17
+ gem 'yard'
18
+ gem 'appraisal'
19
+ gem 'benchmark-ips'
20
+ gem 'redcarpet'
21
+ end
22
+
23
+ group :test do
24
+ gem 'grape-entity', '~> 0.6'
25
+ gem 'maruku'
26
+ gem 'rack-test'
27
+ gem 'rspec', '~> 3.0'
28
+ gem 'cookiejar'
29
+ gem 'rack-jsonp', require: 'rack/jsonp'
30
+ gem 'mime-types', '~> 3.0'
31
+ gem 'ruby-grape-danger', '~> 0.1.0', require: false
32
+ end
33
+
34
+ gemspec path: '../'
data/lib/grape/api.rb CHANGED
@@ -161,7 +161,7 @@ module Grape
161
161
  route_settings[:endpoint] = route.app
162
162
 
163
163
  # using the :any shorthand produces [nil] for route methods, substitute all manually
164
- route_settings[:methods] = %w(GET PUT POST DELETE PATCH HEAD OPTIONS) if route_settings[:methods].include?('ANY')
164
+ route_settings[:methods] = %w(GET PUT POST DELETE PATCH HEAD OPTIONS) if route_settings[:methods].include?('*')
165
165
  end
166
166
  end
167
167
 
@@ -31,6 +31,7 @@ module Grape
31
31
  mod = new_mod || Module.new
32
32
  define_boolean_in_mod(mod)
33
33
  inject_api_helpers_to_mod(mod) if new_mod
34
+
34
35
  inject_api_helpers_to_mod(mod) do
35
36
  mod.class_eval(&block)
36
37
  end if block_given?
@@ -129,6 +129,8 @@ module Grape
129
129
  case request.request_method.to_s.upcase
130
130
  when Grape::Http::Headers::POST
131
131
  201
132
+ when Grape::Http::Headers::DELETE
133
+ 204
132
134
  else
133
135
  200
134
136
  end
@@ -99,6 +99,7 @@ module Grape
99
99
 
100
100
  opts = attrs.extract_options!.clone
101
101
  opts[:presence] = { value: true, message: opts[:message] }
102
+ opts = @group.merge(opts) if @group
102
103
 
103
104
  if opts[:using]
104
105
  require_required_and_optional_fields(attrs.first, opts)
@@ -118,6 +119,7 @@ module Grape
118
119
 
119
120
  opts = attrs.extract_options!.clone
120
121
  type = opts[:type]
122
+ opts = @group.merge(opts) if @group
121
123
 
122
124
  # check type for optional parameter group
123
125
  if attrs && block_given?
@@ -134,6 +136,13 @@ module Grape
134
136
  end
135
137
  end
136
138
 
139
+ # Define common settings for one or more parameters
140
+ # @param (see #requires)
141
+ # @option (see #requires)
142
+ def with(*attrs, &block)
143
+ new_group_scope(attrs.clone, &block)
144
+ end
145
+
137
146
  # Disallow the given parameters to be present in the same request.
138
147
  # @param attrs [*Symbol] parameters to validate
139
148
  def mutually_exclusive(*attrs)
@@ -167,8 +176,8 @@ module Grape
167
176
  # @yield a parameter definition DSL
168
177
  def given(*attrs, &block)
169
178
  attrs.each do |attr|
170
- attr_ = attr.is_a?(Hash) ? attr.keys[0] : attr
171
- raise Grape::Exceptions::UnknownParameter.new(attr_) unless declared_param?(attr_)
179
+ proxy_attr = attr.is_a?(Hash) ? attr.keys[0] : attr
180
+ raise Grape::Exceptions::UnknownParameter.new(proxy_attr) unless declared_param?(proxy_attr)
172
181
  end
173
182
  new_lateral_scope(dependent_on: attrs, &block)
174
183
  end
@@ -184,21 +193,24 @@ module Grape
184
193
 
185
194
  alias group requires
186
195
 
196
+ def map_params(params, element)
197
+ if params.is_a?(Array)
198
+ params.map do |el|
199
+ map_params(el, element)
200
+ end
201
+ elsif params.is_a?(Hash)
202
+ params[element] || {}
203
+ else
204
+ {}
205
+ end
206
+ end
207
+
187
208
  # @param params [Hash] initial hash of parameters
188
209
  # @return hash of parameters relevant for the current scope
189
210
  # @api private
190
211
  def params(params)
191
212
  params = @parent.params(params) if @parent
192
- if @element
193
- params = if params.is_a?(Array)
194
- # used for calculating parent array indices for error messages
195
- params.map { |el| el[@element] || {} }
196
- elsif params.is_a?(Hash)
197
- params[@element] || {}
198
- else
199
- {}
200
- end
201
- end
213
+ params = map_params(params, @element) if @element
202
214
  params
203
215
  end
204
216
  end
@@ -110,11 +110,10 @@ module Grape
110
110
  end
111
111
  handler ||= extract_with(options)
112
112
 
113
- case
114
- when args.include?(:all)
113
+ if args.include?(:all)
115
114
  namespace_inheritable(:rescue_all, true)
116
115
  namespace_inheritable :all_rescue_handler, handler
117
- when args.include?(:grape_exceptions)
116
+ elsif args.include?(:grape_exceptions)
118
117
  namespace_inheritable(:rescue_all, true)
119
118
  namespace_inheritable(:rescue_grape_exceptions, true)
120
119
  else
@@ -99,6 +99,7 @@ module Grape
99
99
  method: :any,
100
100
  path: path,
101
101
  app: app,
102
+ route_options: { anchor: false },
102
103
  forward_match: !app.respond_to?(:inheritable_setting),
103
104
  for: self
104
105
  )
@@ -118,6 +119,7 @@ module Grape
118
119
  # end
119
120
  # end
120
121
  def route(methods, paths = ['/'], route_options = {}, &block)
122
+ methods = '*' if methods == :any
121
123
  endpoint_options = {
122
124
  method: methods,
123
125
  path: paths,
@@ -198,6 +200,7 @@ module Grape
198
200
  # @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
199
201
  def route_param(param, options = {}, &block)
200
202
  options = options.dup
203
+
201
204
  options[:requirements] = {
202
205
  param.to_sym => options[:requirements]
203
206
  } if options[:requirements].is_a?(Regexp)
@@ -205,6 +208,7 @@ module Grape
205
208
  Grape::Validations::ParamsScope.new(api: self) do
206
209
  requires param, type: options[:type]
207
210
  end if options.key?(:type)
211
+
208
212
  namespace(":#{param}", options, &block)
209
213
  end
210
214
 
@@ -95,6 +95,8 @@ module Grape
95
95
  @options[:route_options] ||= {}
96
96
 
97
97
  @lazy_initialize_lock = Mutex.new
98
+ @lazy_initialized = nil
99
+ @block = nil
98
100
 
99
101
  return unless block_given?
100
102
 
@@ -124,8 +126,8 @@ module Grape
124
126
  [options[:method],
125
127
  Namespace.joined_space(namespace_stackable(:namespace)),
126
128
  (namespace_stackable(:mount_path) || []).join('/'),
127
- options[:path].join('/')
128
- ].join(' ')
129
+ options[:path].join('/')]
130
+ .join(' ')
129
131
  end
130
132
 
131
133
  def routes
@@ -248,21 +250,18 @@ module Grape
248
250
 
249
251
  run_filters befores, :before
250
252
 
251
- allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS]
252
- raise Grape::Exceptions::MethodNotAllowed, header.merge('Allow' => allowed_methods) if !options? && allowed_methods
253
-
254
- run_filters before_validations, :before_validation
255
- run_validators validations, request
256
- run_filters after_validations, :after_validation
253
+ if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
254
+ raise Grape::Exceptions::MethodNotAllowed, header.merge('Allow' => allowed_methods) unless options?
255
+ header 'Allow', allowed_methods
256
+ response_object = ''
257
+ status 204
258
+ else
259
+ run_filters before_validations, :before_validation
260
+ run_validators validations, request
261
+ run_filters after_validations, :after_validation
262
+ response_object = @block ? @block.call(self) : nil
263
+ end
257
264
 
258
- response_object =
259
- if options?
260
- header 'Allow', allowed_methods
261
- status 204
262
- ''
263
- else
264
- @block ? @block.call(self) : nil
265
- end
266
265
  run_filters afters, :after
267
266
  cookies.write(header)
268
267