grape 0.11.0 → 0.12.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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +23 -80
  3. data/.travis.yml +1 -1
  4. data/CHANGELOG.md +27 -0
  5. data/Gemfile +1 -1
  6. data/Guardfile +1 -1
  7. data/LICENSE +1 -1
  8. data/README.md +131 -30
  9. data/Rakefile +1 -1
  10. data/UPGRADING.md +110 -1
  11. data/gemfiles/rails_3.gemfile +1 -1
  12. data/gemfiles/rails_4.gemfile +1 -1
  13. data/grape.gemspec +4 -4
  14. data/lib/grape.rb +92 -62
  15. data/lib/grape/api.rb +10 -10
  16. data/lib/grape/cookies.rb +1 -1
  17. data/lib/grape/dsl/configuration.rb +7 -7
  18. data/lib/grape/dsl/helpers.rb +3 -3
  19. data/lib/grape/dsl/inside_route.rb +50 -21
  20. data/lib/grape/dsl/parameters.rb +25 -6
  21. data/lib/grape/dsl/request_response.rb +1 -1
  22. data/lib/grape/dsl/routing.rb +11 -10
  23. data/lib/grape/dsl/settings.rb +1 -1
  24. data/lib/grape/endpoint.rb +21 -19
  25. data/lib/grape/error_formatter/json.rb +1 -1
  26. data/lib/grape/exceptions/base.rb +1 -1
  27. data/lib/grape/exceptions/validation.rb +1 -1
  28. data/lib/grape/exceptions/validation_errors.rb +2 -2
  29. data/lib/grape/formatter/base.rb +1 -1
  30. data/lib/grape/formatter/json.rb +1 -1
  31. data/lib/grape/formatter/serializable_hash.rb +4 -4
  32. data/lib/grape/formatter/txt.rb +1 -1
  33. data/lib/grape/formatter/xml.rb +1 -1
  34. data/lib/grape/http/headers.rb +27 -0
  35. data/lib/grape/http/request.rb +1 -1
  36. data/lib/grape/middleware/error.rb +10 -4
  37. data/lib/grape/middleware/formatter.rb +13 -9
  38. data/lib/grape/middleware/globals.rb +2 -1
  39. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -2
  40. data/lib/grape/middleware/versioner/header.rb +4 -4
  41. data/lib/grape/middleware/versioner/param.rb +2 -2
  42. data/lib/grape/middleware/versioner/path.rb +1 -1
  43. data/lib/grape/namespace.rb +2 -1
  44. data/lib/grape/parser/json.rb +1 -1
  45. data/lib/grape/parser/xml.rb +1 -1
  46. data/lib/grape/path.rb +3 -3
  47. data/lib/grape/presenters/presenter.rb +9 -0
  48. data/lib/grape/validations/params_scope.rb +3 -3
  49. data/lib/grape/validations/validators/allow_blank.rb +1 -1
  50. data/lib/grape/validations/validators/coerce.rb +6 -5
  51. data/lib/grape/validations/validators/default.rb +2 -2
  52. data/lib/grape/validations/validators/multiple_params_base.rb +1 -0
  53. data/lib/grape/validations/validators/regexp.rb +1 -1
  54. data/lib/grape/version.rb +1 -1
  55. data/spec/grape/api/custom_validations_spec.rb +47 -0
  56. data/spec/grape/api/deeply_included_options_spec.rb +56 -0
  57. data/spec/grape/api_spec.rb +64 -42
  58. data/spec/grape/dsl/configuration_spec.rb +2 -2
  59. data/spec/grape/dsl/helpers_spec.rb +1 -1
  60. data/spec/grape/dsl/inside_route_spec.rb +75 -19
  61. data/spec/grape/dsl/parameters_spec.rb +59 -10
  62. data/spec/grape/dsl/request_response_spec.rb +62 -2
  63. data/spec/grape/dsl/routing_spec.rb +116 -18
  64. data/spec/grape/endpoint_spec.rb +57 -5
  65. data/spec/grape/entity_spec.rb +1 -1
  66. data/spec/grape/exceptions/body_parse_errors_spec.rb +5 -5
  67. data/spec/grape/exceptions/invalid_accept_header_spec.rb +32 -32
  68. data/spec/grape/exceptions/validation_errors_spec.rb +1 -1
  69. data/spec/grape/integration/rack_spec.rb +4 -3
  70. data/spec/grape/middleware/auth/strategies_spec.rb +2 -2
  71. data/spec/grape/middleware/base_spec.rb +2 -2
  72. data/spec/grape/middleware/error_spec.rb +1 -1
  73. data/spec/grape/middleware/exception_spec.rb +5 -5
  74. data/spec/grape/middleware/formatter_spec.rb +10 -10
  75. data/spec/grape/middleware/globals_spec.rb +27 -0
  76. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +1 -1
  77. data/spec/grape/middleware/versioner/header_spec.rb +1 -1
  78. data/spec/grape/middleware/versioner/param_spec.rb +1 -1
  79. data/spec/grape/middleware/versioner/path_spec.rb +1 -1
  80. data/spec/grape/path_spec.rb +6 -4
  81. data/spec/grape/presenters/presenter_spec.rb +70 -0
  82. data/spec/grape/util/inheritable_values_spec.rb +1 -1
  83. data/spec/grape/util/stackable_values_spec.rb +1 -1
  84. data/spec/grape/util/strict_hash_configuration_spec.rb +1 -1
  85. data/spec/grape/validations/params_scope_spec.rb +64 -0
  86. data/spec/grape/validations/validators/allow_blank_spec.rb +10 -0
  87. data/spec/grape/validations/validators/coerce_spec.rb +48 -18
  88. data/spec/grape/validations/validators/default_spec.rb +110 -20
  89. data/spec/grape/validations/validators/presence_spec.rb +41 -3
  90. data/spec/grape/validations/validators/regexp_spec.rb +7 -2
  91. data/spec/grape/validations_spec.rb +20 -1
  92. data/spec/support/file_streamer.rb +11 -0
  93. data/spec/support/versioned_helpers.rb +1 -1
  94. metadata +14 -2
@@ -18,7 +18,7 @@ module Grape
18
18
  spec = formatters(options)[api_format]
19
19
  case spec
20
20
  when nil
21
- lambda { |obj, env| obj }
21
+ ->(obj, _env) { obj }
22
22
  when Symbol
23
23
  method(spec)
24
24
  else
@@ -2,7 +2,7 @@ module Grape
2
2
  module Formatter
3
3
  module Json
4
4
  class << self
5
- def call(object, env)
5
+ def call(object, _env)
6
6
  return object.to_json if object.respond_to?(:to_json)
7
7
  MultiJson.dump(object)
8
8
  end
@@ -2,7 +2,7 @@ module Grape
2
2
  module Formatter
3
3
  module SerializableHash
4
4
  class << self
5
- def call(object, env)
5
+ def call(object, _env)
6
6
  return object if object.is_a?(String)
7
7
  return MultiJson.dump(serialize(object)) if serializable?(object)
8
8
  return object.to_json if object.respond_to?(:to_json)
@@ -12,15 +12,15 @@ module Grape
12
12
  private
13
13
 
14
14
  def serializable?(object)
15
- object.respond_to?(:serializable_hash) || object.kind_of?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false) || object.kind_of?(Hash)
15
+ object.respond_to?(:serializable_hash) || object.is_a?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false) || object.is_a?(Hash)
16
16
  end
17
17
 
18
18
  def serialize(object)
19
19
  if object.respond_to? :serializable_hash
20
20
  object.serializable_hash
21
- elsif object.kind_of?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false)
21
+ elsif object.is_a?(Array) && !object.map { |o| o.respond_to? :serializable_hash }.include?(false)
22
22
  object.map(&:serializable_hash)
23
- elsif object.kind_of?(Hash)
23
+ elsif object.is_a?(Hash)
24
24
  object.inject({}) do |h, (k, v)|
25
25
  h[k] = serialize(v)
26
26
  h
@@ -2,7 +2,7 @@ module Grape
2
2
  module Formatter
3
3
  module Txt
4
4
  class << self
5
- def call(object, env)
5
+ def call(object, _env)
6
6
  object.respond_to?(:to_txt) ? object.to_txt : object.to_s
7
7
  end
8
8
  end
@@ -2,7 +2,7 @@ module Grape
2
2
  module Formatter
3
3
  module Xml
4
4
  class << self
5
- def call(object, env)
5
+ def call(object, _env)
6
6
  return object.to_xml if object.respond_to?(:to_xml)
7
7
  fail Grape::Exceptions::InvalidFormatter.new(object.class, 'xml')
8
8
  end
@@ -0,0 +1,27 @@
1
+ module Grape
2
+ module Http
3
+ module Headers
4
+ # https://github.com/rack/rack/blob/master/lib/rack.rb
5
+ HTTP_VERSION = 'HTTP_VERSION'.freeze
6
+ PATH_INFO = 'PATH_INFO'.freeze
7
+ QUERY_STRING = 'QUERY_STRING'.freeze
8
+ CONTENT_TYPE = 'Content-Type'.freeze
9
+
10
+ GET = 'GET'.freeze
11
+ POST = 'POST'.freeze
12
+ PUT = 'PUT'.freeze
13
+ PATCH = 'PATCH'.freeze
14
+ DELETE = 'DELETE'.freeze
15
+ HEAD = 'HEAD'.freeze
16
+ OPTIONS = 'OPTIONS'.freeze
17
+
18
+ HTTP_ACCEPT_VERSION = 'HTTP_ACCEPT_VERSION'.freeze
19
+ X_CASCADE = 'X-Cascade'.freeze
20
+ HTTP_TRANSFER_ENCODING = 'HTTP_TRANSFER_ENCODING'.freeze
21
+ HTTP_ACCEPT = 'HTTP_ACCEPT'.freeze
22
+
23
+ ACCEPT = 'accept'.freeze
24
+ FORMAT = 'format'.freeze
25
+ end
26
+ end
27
+ end
@@ -17,7 +17,7 @@ module Grape
17
17
  def headers
18
18
  @headers ||= env.dup.inject({}) do |h, (k, v)|
19
19
  if k.to_s.start_with? 'HTTP_'
20
- k = k[5..-1].gsub('_', '-').downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }
20
+ k = k[5..-1].tr('_', '-').downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }
21
21
  h[k] = v
22
22
  end
23
23
  h
@@ -29,7 +29,7 @@ module Grape
29
29
  rescue StandardError => e
30
30
  is_rescuable = rescuable?(e.class)
31
31
  if e.is_a?(Grape::Exceptions::Base) && !is_rescuable
32
- handler = lambda { |arg| error_response(arg) }
32
+ handler = ->(arg) { error_response(arg) }
33
33
  else
34
34
  raise unless is_rescuable
35
35
  handler = find_handler(e.class)
@@ -47,7 +47,7 @@ module Grape
47
47
  end
48
48
 
49
49
  def rescuable?(klass)
50
- options[:rescue_all] || (options[:rescue_handlers] || []).any? { |error, handler| klass <= error } || (options[:base_only_rescue_handlers] || []).include?(klass)
50
+ options[:rescue_all] || (options[:rescue_handlers] || []).any? { |error, _handler| klass <= error } || (options[:base_only_rescue_handlers] || []).include?(klass)
51
51
  end
52
52
 
53
53
  def exec_handler(e, &handler)
@@ -58,20 +58,26 @@ module Grape
58
58
  end
59
59
  end
60
60
 
61
+ def error!(message, status = options[:default_status], headers = {}, backtrace = [])
62
+ headers = { Grape::Http::Headers::CONTENT_TYPE => content_type }.merge(headers)
63
+ rack_response(format_message(message, backtrace), status, headers)
64
+ end
65
+
61
66
  def handle_error(e)
62
67
  error_response(message: e.message, backtrace: e.backtrace)
63
68
  end
64
69
 
70
+ # TODO: This method is deprecated. Refactor out.
65
71
  def error_response(error = {})
66
72
  status = error[:status] || options[:default_status]
67
73
  message = error[:message] || options[:default_message]
68
- headers = { 'Content-Type' => content_type }
74
+ headers = { Grape::Http::Headers::CONTENT_TYPE => content_type }
69
75
  headers.merge!(error[:headers]) if error[:headers].is_a?(Hash)
70
76
  backtrace = error[:backtrace] || []
71
77
  rack_response(format_message(message, backtrace), status, headers)
72
78
  end
73
79
 
74
- def rack_response(message, status = options[:default_status], headers = { 'Content-Type' => content_type })
80
+ def rack_response(message, status = options[:default_status], headers = { Grape::Http::Headers::CONTENT_TYPE => content_type })
75
81
  Rack::Response.new([message], status, headers).finish
76
82
  end
77
83
 
@@ -26,17 +26,21 @@ module Grape
26
26
  def after
27
27
  status, headers, bodies = *@app_response
28
28
  # allow content-type to be explicitly overwritten
29
- api_format = mime_types[headers['Content-Type']] || env['api.format']
29
+ api_format = mime_types[headers[Grape::Http::Headers::CONTENT_TYPE]] || env['api.format']
30
30
  formatter = Grape::Formatter::Base.formatter_for api_format, options
31
31
  begin
32
- bodymap = bodies.collect do |body|
33
- formatter.call body, env
34
- end
32
+ bodymap = if bodies.respond_to?(:collect)
33
+ bodies.collect do |body|
34
+ formatter.call body, env
35
+ end
36
+ else
37
+ bodies
38
+ end
35
39
  rescue Grape::Exceptions::InvalidFormatter => e
36
40
  throw :error, status: 500, message: e.message
37
41
  end
38
- headers['Content-Type'] = content_type_for(env['api.format']) unless headers['Content-Type']
39
- Rack::Response.new(bodymap, status, headers).to_a
42
+ headers[Grape::Http::Headers::CONTENT_TYPE] = content_type_for(env['api.format']) unless headers[Grape::Http::Headers::CONTENT_TYPE]
43
+ Rack::Response.new(bodymap, status, headers)
40
44
  end
41
45
 
42
46
  private
@@ -50,7 +54,7 @@ module Grape
50
54
  if (request.post? || request.put? || request.patch? || request.delete?) &&
51
55
  (!request.form_data? || !request.media_type) &&
52
56
  (!request.parseable_data?) &&
53
- (request.content_length.to_i > 0 || request.env['HTTP_TRANSFER_ENCODING'] == 'chunked')
57
+ (request.content_length.to_i > 0 || request.env[Grape::Http::Headers::HTTP_TRANSFER_ENCODING] == 'chunked')
54
58
 
55
59
  if (input = env['rack.input'])
56
60
  input.rewind
@@ -115,7 +119,7 @@ module Grape
115
119
  end
116
120
 
117
121
  def format_from_params
118
- fmt = Rack::Utils.parse_nested_query(env['QUERY_STRING'])['format']
122
+ fmt = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[Grape::Http::Headers::FORMAT]
119
123
  # avoid symbol memory leak on an unknown format
120
124
  return fmt.to_sym if content_type_for(fmt)
121
125
  fmt
@@ -129,7 +133,7 @@ module Grape
129
133
  end
130
134
 
131
135
  def mime_array
132
- accept = headers['accept']
136
+ accept = headers[Grape::Http::Headers::ACCEPT]
133
137
  return [] unless accept
134
138
 
135
139
  accept_into_mime_and_quality = %r{
@@ -4,7 +4,8 @@ module Grape
4
4
  module Middleware
5
5
  class Globals < Base
6
6
  def before
7
- @env['grape.request'] = Grape::Request.new(@env)
7
+ request = Grape::Request.new(@env)
8
+ @env['grape.request'] = request
8
9
  @env['grape.request.headers'] = request.headers
9
10
  @env['grape.request.params'] = request.params if @env['rack.input']
10
11
  end
@@ -18,7 +18,7 @@ module Grape
18
18
  # route.
19
19
  class AcceptVersionHeader < Base
20
20
  def before
21
- potential_version = (env['HTTP_ACCEPT_VERSION'] || '').strip
21
+ potential_version = (env[Grape::Http::Headers::HTTP_ACCEPT_VERSION] || '').strip
22
22
 
23
23
  if strict?
24
24
  # If no Accept-Version header:
@@ -59,7 +59,7 @@ module Grape
59
59
  end
60
60
 
61
61
  def error_headers
62
- cascade? ? { 'X-Cascade' => 'pass' } : {}
62
+ cascade? ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {}
63
63
  end
64
64
  end
65
65
  end
@@ -67,8 +67,8 @@ module Grape
67
67
  def available_media_types
68
68
  available_media_types = []
69
69
 
70
- content_types.each do |extension, media_type|
71
- versions.reverse.each do |version|
70
+ content_types.each do |extension, _media_type|
71
+ versions.reverse_each do |version|
72
72
  available_media_types += ["application/vnd.#{vendor}-#{version}+#{extension}", "application/vnd.#{vendor}-#{version}"]
73
73
  end
74
74
  available_media_types << "application/vnd.#{vendor}+#{extension}"
@@ -84,7 +84,7 @@ module Grape
84
84
  end
85
85
 
86
86
  def rack_accept_header
87
- Rack::Accept::MediaType.new env['HTTP_ACCEPT']
87
+ Rack::Accept::MediaType.new env[Grape::Http::Headers::HTTP_ACCEPT]
88
88
  rescue RuntimeError => e
89
89
  raise Grape::Exceptions::InvalidAcceptHeader.new(e.message, error_headers)
90
90
  end
@@ -113,7 +113,7 @@ module Grape
113
113
  end
114
114
 
115
115
  def error_headers
116
- cascade? ? { 'X-Cascade' => 'pass' } : {}
116
+ cascade? ? { Grape::Http::Headers::X_CASCADE => 'pass' } : {}
117
117
  end
118
118
 
119
119
  # @param [String] media_type a content type
@@ -27,10 +27,10 @@ module Grape
27
27
 
28
28
  def before
29
29
  paramkey = options[:parameter]
30
- potential_version = Rack::Utils.parse_nested_query(env['QUERY_STRING'])[paramkey]
30
+ potential_version = Rack::Utils.parse_nested_query(env[Grape::Http::Headers::QUERY_STRING])[paramkey]
31
31
  unless potential_version.nil?
32
32
  if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
33
- throw :error, status: 404, message: '404 API Version Not Found', headers: { 'X-Cascade' => 'pass' }
33
+ throw :error, status: 404, message: '404 API Version Not Found', headers: { Grape::Http::Headers::X_CASCADE => 'pass' }
34
34
  end
35
35
  env['api.version'] = potential_version
36
36
  env['rack.request.query_hash'].delete(paramkey) if env.key? 'rack.request.query_hash'
@@ -24,7 +24,7 @@ module Grape
24
24
  end
25
25
 
26
26
  def before
27
- path = env['PATH_INFO'].dup
27
+ path = env[Grape::Http::Headers::PATH_INFO].dup
28
28
 
29
29
  if prefix && path.index(prefix) == 0
30
30
  path.sub!(prefix, '')
@@ -5,7 +5,8 @@ module Grape
5
5
  # options:
6
6
  # requirements: a hash
7
7
  def initialize(space, options = {})
8
- @space, @options = space.to_s, options
8
+ @space = space.to_s
9
+ @options = options
9
10
  end
10
11
 
11
12
  def requirements
@@ -2,7 +2,7 @@ module Grape
2
2
  module Parser
3
3
  module Json
4
4
  class << self
5
- def call(object, env)
5
+ def call(object, _env)
6
6
  MultiJson.load(object)
7
7
  rescue MultiJson::ParseError
8
8
  # handle JSON parsing errors via the rescue handlers or provide error message
@@ -2,7 +2,7 @@ module Grape
2
2
  module Parser
3
3
  module Xml
4
4
  class << self
5
- def call(object, env)
5
+ def call(object, _env)
6
6
  MultiXml.parse(object)
7
7
  rescue MultiXml::ParseError
8
8
  # handle XML parsing errors via the rescue handlers or provide error message
data/lib/grape/path.rb CHANGED
@@ -17,7 +17,7 @@ module Grape
17
17
  end
18
18
 
19
19
  def root_prefix
20
- split_setting(:root_prefix, '/')
20
+ split_setting(:root_prefix)
21
21
  end
22
22
 
23
23
  def uses_specific_format?
@@ -38,7 +38,7 @@ module Grape
38
38
 
39
39
  def suffix
40
40
  if uses_specific_format?
41
- ''
41
+ "(.#{settings[:format]})"
42
42
  elsif !uses_path_versioning? || (has_namespace? || has_path?)
43
43
  '(.:format)'
44
44
  else
@@ -68,7 +68,7 @@ module Grape
68
68
  parts.flatten.reject { |part| part == '/' }
69
69
  end
70
70
 
71
- def split_setting(key, delimiter)
71
+ def split_setting(key)
72
72
  return if settings[key].nil?
73
73
  settings[key].to_s.split('/')
74
74
  end
@@ -0,0 +1,9 @@
1
+ module Grape
2
+ module Presenters
3
+ class Presenter
4
+ def self.represent(object, _options = {})
5
+ object
6
+ end
7
+ end
8
+ end
9
+ end
@@ -117,7 +117,7 @@ module Grape
117
117
  doc_attrs[:desc] = desc if desc
118
118
 
119
119
  default = validations[:default]
120
- doc_attrs[:default] = default if default
120
+ doc_attrs[:default] = default if validations.key?(:default)
121
121
 
122
122
  values = validations[:values]
123
123
  doc_attrs[:values] = values if values
@@ -181,9 +181,9 @@ module Grape
181
181
  def validate_value_coercion(coerce_type, values)
182
182
  return unless coerce_type && values
183
183
  return if values.is_a?(Proc)
184
- coerce_type = coerce_type.first if coerce_type.kind_of?(Array)
184
+ coerce_type = coerce_type.first if coerce_type.is_a?(Array)
185
185
  value_types = values.is_a?(Range) ? [values.begin, values.end] : values
186
- if value_types.any? { |v| !v.kind_of?(coerce_type) }
186
+ if value_types.any? { |v| !v.is_a?(coerce_type) }
187
187
  fail Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
188
188
  end
189
189
  end
@@ -21,7 +21,7 @@ module Grape
21
21
 
22
22
  return unless should_validate
23
23
 
24
- unless value.present?
24
+ unless value == false || value.present?
25
25
  fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :blank
26
26
  end
27
27
  end
@@ -32,7 +32,7 @@ module Grape
32
32
  val.is_a?(TrueClass) || val.is_a?(FalseClass) || (val.is_a?(String) && val.empty?)
33
33
  elsif klass == Rack::Multipart::UploadedFile
34
34
  val.is_a?(Hashie::Mash) && val.key?(:tempfile)
35
- elsif [DateTime, Date, Numeric].any?{ |vclass| vclass >= klass }
35
+ elsif [DateTime, Date, Numeric].any? { |vclass| vclass >= klass }
36
36
  return true if val.is_a?(String) && val.empty?
37
37
  val.is_a?(klass)
38
38
  else
@@ -41,8 +41,8 @@ module Grape
41
41
  end
42
42
 
43
43
  def valid_type?(val)
44
- if @option.is_a?(Array)
45
- _valid_array_type?(@option[0], val)
44
+ if @option.is_a?(Array) || @option.is_a?(Set)
45
+ _valid_array_type?(@option.first, val)
46
46
  else
47
47
  _valid_single_type?(@option, val)
48
48
  end
@@ -50,8 +50,9 @@ module Grape
50
50
 
51
51
  def coerce_value(type, val)
52
52
  # Don't coerce things other than nil to Arrays or Hashes
53
- return val || [] if type == Array
54
- return val || {} if type == Hash
53
+ return val || [] if type == Array
54
+ return val || Set.new if type == Set
55
+ return val || {} if type == Hash
55
56
 
56
57
  converter = Virtus::Attribute.build(type)
57
58
  converter.coerce(val)
@@ -11,12 +11,12 @@ module Grape
11
11
  end
12
12
 
13
13
  def validate!(params)
14
+ return unless @scope.should_validate?(params)
15
+
14
16
  attrs = AttributesIterator.new(self, @scope, params)
15
- parent_element = @scope.element
16
17
  attrs.each do |resource_params, attr_name|
17
18
  if resource_params[attr_name].nil?
18
19
  validate_param!(attr_name, resource_params)
19
- params[parent_element] = resource_params if parent_element && params[parent_element].nil?
20
20
  end
21
21
  end
22
22
  end
@@ -15,6 +15,7 @@ module Grape
15
15
  end
16
16
 
17
17
  def keys_in_common(resource_params)
18
+ return [] unless resource_params.is_a?(Hash)
18
19
  (all_keys & resource_params.stringify_keys.keys).map(&:to_s)
19
20
  end
20
21