grape 1.7.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +37 -1
  3. data/CONTRIBUTING.md +1 -1
  4. data/README.md +30 -25
  5. data/UPGRADING.md +35 -0
  6. data/grape.gemspec +3 -6
  7. data/lib/grape/api.rb +2 -2
  8. data/lib/grape/content_types.rb +2 -8
  9. data/lib/grape/dsl/desc.rb +1 -1
  10. data/lib/grape/dsl/inside_route.rb +11 -11
  11. data/lib/grape/dsl/request_response.rb +2 -1
  12. data/lib/grape/dsl/settings.rb +2 -6
  13. data/lib/grape/endpoint.rb +28 -18
  14. data/lib/grape/error_formatter/base.rb +1 -1
  15. data/lib/grape/exceptions/base.rb +2 -2
  16. data/lib/grape/exceptions/missing_group_type.rb +1 -6
  17. data/lib/grape/exceptions/unsupported_group_type.rb +1 -6
  18. data/lib/grape/exceptions/validation_errors.rb +1 -6
  19. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +3 -3
  20. data/lib/grape/extensions/hash.rb +4 -7
  21. data/lib/grape/extensions/hashie/mash.rb +3 -3
  22. data/lib/grape/formatter/serializable_hash.rb +7 -7
  23. data/lib/grape/http/headers.rb +12 -2
  24. data/lib/grape/middleware/auth/base.rb +1 -1
  25. data/lib/grape/middleware/auth/strategies.rb +1 -2
  26. data/lib/grape/middleware/error.rb +5 -5
  27. data/lib/grape/middleware/formatter.rb +6 -6
  28. data/lib/grape/middleware/versioner/header.rb +11 -19
  29. data/lib/grape/railtie.rb +9 -0
  30. data/lib/grape/request.rb +8 -2
  31. data/lib/grape/router/route.rb +1 -3
  32. data/lib/grape/util/lazy_value.rb +3 -11
  33. data/lib/grape/util/strict_hash_configuration.rb +3 -4
  34. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  35. data/lib/grape/validations/params_scope.rb +8 -2
  36. data/lib/grape/validations/single_attribute_iterator.rb +3 -1
  37. data/lib/grape/validations/types/custom_type_coercer.rb +2 -16
  38. data/lib/grape/validations/validators/base.rb +9 -20
  39. data/lib/grape/validations/validators/default_validator.rb +2 -20
  40. data/lib/grape/validations/validators/multiple_params_base.rb +4 -8
  41. data/lib/grape/validations/validators/values_validator.rb +14 -5
  42. data/lib/grape/version.rb +1 -1
  43. data/lib/grape.rb +26 -5
  44. metadata +13 -253
  45. data/lib/grape/config.rb +0 -34
  46. data/lib/grape/extensions/deep_mergeable_hash.rb +0 -21
  47. data/lib/grape/extensions/deep_symbolize_hash.rb +0 -32
  48. data/spec/grape/api/custom_validations_spec.rb +0 -256
  49. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  50. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  51. data/spec/grape/api/documentation_spec.rb +0 -59
  52. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  53. data/spec/grape/api/instance_spec.rb +0 -103
  54. data/spec/grape/api/invalid_format_spec.rb +0 -45
  55. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  56. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  57. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  58. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  59. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  60. data/spec/grape/api/recognize_path_spec.rb +0 -21
  61. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  62. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  63. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  64. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  65. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  66. data/spec/grape/api_remount_spec.rb +0 -473
  67. data/spec/grape/api_spec.rb +0 -4347
  68. data/spec/grape/config_spec.rb +0 -17
  69. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  70. data/spec/grape/dsl/desc_spec.rb +0 -101
  71. data/spec/grape/dsl/headers_spec.rb +0 -62
  72. data/spec/grape/dsl/helpers_spec.rb +0 -100
  73. data/spec/grape/dsl/inside_route_spec.rb +0 -535
  74. data/spec/grape/dsl/logger_spec.rb +0 -24
  75. data/spec/grape/dsl/middleware_spec.rb +0 -60
  76. data/spec/grape/dsl/parameters_spec.rb +0 -180
  77. data/spec/grape/dsl/request_response_spec.rb +0 -206
  78. data/spec/grape/dsl/routing_spec.rb +0 -275
  79. data/spec/grape/dsl/settings_spec.rb +0 -261
  80. data/spec/grape/dsl/validations_spec.rb +0 -55
  81. data/spec/grape/endpoint/declared_spec.rb +0 -846
  82. data/spec/grape/endpoint_spec.rb +0 -1085
  83. data/spec/grape/entity_spec.rb +0 -336
  84. data/spec/grape/exceptions/base_spec.rb +0 -81
  85. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -145
  86. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  87. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  88. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  89. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  90. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -21
  91. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  92. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  93. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  94. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  95. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -23
  96. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  97. data/spec/grape/exceptions/validation_spec.rb +0 -19
  98. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  99. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  100. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  101. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  102. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  103. data/spec/grape/integration/rack_spec.rb +0 -51
  104. data/spec/grape/loading_spec.rb +0 -44
  105. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  106. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  107. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  108. data/spec/grape/middleware/base_spec.rb +0 -221
  109. data/spec/grape/middleware/error_spec.rb +0 -85
  110. data/spec/grape/middleware/exception_spec.rb +0 -294
  111. data/spec/grape/middleware/formatter_spec.rb +0 -461
  112. data/spec/grape/middleware/globals_spec.rb +0 -30
  113. data/spec/grape/middleware/stack_spec.rb +0 -155
  114. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  115. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  116. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  117. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  118. data/spec/grape/middleware/versioner_spec.rb +0 -21
  119. data/spec/grape/named_api_spec.rb +0 -19
  120. data/spec/grape/parser_spec.rb +0 -86
  121. data/spec/grape/path_spec.rb +0 -252
  122. data/spec/grape/presenters/presenter_spec.rb +0 -71
  123. data/spec/grape/request_spec.rb +0 -136
  124. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  125. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  126. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  127. data/spec/grape/util/stackable_values_spec.rb +0 -128
  128. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  129. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  130. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  131. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -40
  132. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  133. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -57
  134. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  135. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  136. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  137. data/spec/grape/validations/types_spec.rb +0 -111
  138. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  139. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  140. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  141. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  142. data/spec/grape/validations/validators/default_spec.rb +0 -463
  143. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  144. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  145. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  146. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  147. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  148. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  149. data/spec/grape/validations/validators/values_spec.rb +0 -696
  150. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  151. data/spec/grape/validations_spec.rb +0 -2029
  152. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  153. data/spec/integration/multi_json/json_spec.rb +0 -7
  154. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  155. data/spec/shared/versioning_examples.rb +0 -215
  156. data/spec/spec_helper.rb +0 -52
  157. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  158. data/spec/support/chunks.rb +0 -14
  159. data/spec/support/content_type_helpers.rb +0 -15
  160. data/spec/support/endpoint_faker.rb +0 -25
  161. data/spec/support/file_streamer.rb +0 -13
  162. data/spec/support/integer_helpers.rb +0 -13
  163. data/spec/support/versioned_helpers.rb +0 -55
@@ -15,24 +15,24 @@ module Grape
15
15
  private
16
16
 
17
17
  def serializable?(object)
18
- object.respond_to?(:serializable_hash) || (object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash }) || object.is_a?(Hash)
18
+ object.respond_to?(:serializable_hash) || array_serializable?(object) || object.is_a?(Hash)
19
19
  end
20
20
 
21
21
  def serialize(object)
22
22
  if object.respond_to? :serializable_hash
23
23
  object.serializable_hash
24
- elsif object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash }
24
+ elsif array_serializable?(object)
25
25
  object.map(&:serializable_hash)
26
26
  elsif object.is_a?(Hash)
27
- h = {}
28
- object.each_pair do |k, v|
29
- h[k] = serialize(v)
30
- end
31
- h
27
+ object.transform_values { |v| serialize(v) }
32
28
  else
33
29
  object
34
30
  end
35
31
  end
32
+
33
+ def array_serializable?(object)
34
+ object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash }
35
+ end
36
36
  end
37
37
  end
38
38
  end
@@ -10,7 +10,18 @@ module Grape
10
10
  PATH_INFO = 'PATH_INFO'
11
11
  REQUEST_METHOD = 'REQUEST_METHOD'
12
12
  QUERY_STRING = 'QUERY_STRING'
13
- CONTENT_TYPE = 'Content-Type'
13
+
14
+ if Grape.lowercase_headers?
15
+ ALLOW = 'allow'
16
+ LOCATION = 'location'
17
+ TRANSFER_ENCODING = 'transfer-encoding'
18
+ X_CASCADE = 'x-cascade'
19
+ else
20
+ ALLOW = 'Allow'
21
+ LOCATION = 'Location'
22
+ TRANSFER_ENCODING = 'Transfer-Encoding'
23
+ X_CASCADE = 'X-Cascade'
24
+ end
14
25
 
15
26
  GET = 'GET'
16
27
  POST = 'POST'
@@ -24,7 +35,6 @@ module Grape
24
35
  SUPPORTED_METHODS_WITHOUT_OPTIONS = Grape::Util::LazyObject.new { [GET, POST, PUT, PATCH, DELETE, HEAD].freeze }
25
36
 
26
37
  HTTP_ACCEPT_VERSION = 'HTTP_ACCEPT_VERSION'
27
- X_CASCADE = 'X-Cascade'
28
38
  HTTP_TRANSFER_ENCODING = 'HTTP_TRANSFER_ENCODING'
29
39
  HTTP_ACCEPT = 'HTTP_ACCEPT'
30
40
 
@@ -28,7 +28,7 @@ module Grape
28
28
 
29
29
  strategy_info = Grape::Middleware::Auth::Strategies[options[:type]]
30
30
 
31
- throw(:error, status: 401, message: 'API Authorization Failed.') unless strategy_info.present?
31
+ throw(:error, status: 401, message: 'API Authorization Failed.') if strategy_info.blank?
32
32
 
33
33
  strategy = strategy_info.create(@app, options) do |*args|
34
34
  auth_proc_context.instance_exec(*args, &auth_proc)
@@ -12,8 +12,7 @@ module Grape
12
12
 
13
13
  def auth_strategies
14
14
  @auth_strategies ||= {
15
- http_basic: StrategyInfo.new(Rack::Auth::Basic, ->(settings) { [settings[:realm]] }),
16
- http_digest: StrategyInfo.new(Rack::Auth::Digest::MD5, ->(settings) { [settings[:realm], settings[:opaque]] })
15
+ http_basic: StrategyInfo.new(Rack::Auth::Basic, ->(settings) { [settings[:realm]] })
17
16
  }
18
17
  end
19
18
 
@@ -51,7 +51,7 @@ module Grape
51
51
  end
52
52
 
53
53
  def error!(message, status = options[:default_status], headers = {}, backtrace = [], original_exception = nil)
54
- headers = headers.reverse_merge(Grape::Http::Headers::CONTENT_TYPE => content_type)
54
+ headers = headers.reverse_merge(Rack::CONTENT_TYPE => content_type)
55
55
  rack_response(format_message(message, backtrace, original_exception), status, headers)
56
56
  end
57
57
 
@@ -63,15 +63,15 @@ module Grape
63
63
  def error_response(error = {})
64
64
  status = error[:status] || options[:default_status]
65
65
  message = error[:message] || options[:default_message]
66
- headers = { Grape::Http::Headers::CONTENT_TYPE => content_type }
66
+ headers = { Rack::CONTENT_TYPE => content_type }
67
67
  headers.merge!(error[:headers]) if error[:headers].is_a?(Hash)
68
68
  backtrace = error[:backtrace] || error[:original_exception]&.backtrace || []
69
69
  original_exception = error.is_a?(Exception) ? error : error[:original_exception] || nil
70
70
  rack_response(format_message(message, backtrace, original_exception), status, headers)
71
71
  end
72
72
 
73
- def rack_response(message, status = options[:default_status], headers = { Grape::Http::Headers::CONTENT_TYPE => content_type })
74
- message = ERB::Util.html_escape(message) if headers[Grape::Http::Headers::CONTENT_TYPE] == TEXT_HTML
73
+ def rack_response(message, status = options[:default_status], headers = { Rack::CONTENT_TYPE => content_type })
74
+ message = ERB::Util.html_escape(message) if headers[Rack::CONTENT_TYPE] == TEXT_HTML
75
75
  Rack::Response.new([message], Rack::Utils.status_code(status), headers)
76
76
  end
77
77
 
@@ -109,7 +109,7 @@ module Grape
109
109
  return :error_response if klass == Grape::Exceptions::InvalidVersionHeader
110
110
  return unless options[:rescue_grape_exceptions] || !options[:rescue_all]
111
111
 
112
- :error_response
112
+ options[:grape_exceptions_rescue_handler] || :error_response
113
113
  end
114
114
 
115
115
  def rescue_handler_for_any_class(klass)
@@ -54,7 +54,7 @@ module Grape
54
54
  end
55
55
 
56
56
  def fetch_formatter(headers, options)
57
- api_format = mime_types[headers[Grape::Http::Headers::CONTENT_TYPE]] || env[Grape::Env::API_FORMAT]
57
+ api_format = mime_types[headers[Rack::CONTENT_TYPE]] || env[Grape::Env::API_FORMAT]
58
58
  Grape::Formatter.formatter_for(api_format, **options)
59
59
  end
60
60
 
@@ -63,10 +63,10 @@ module Grape
63
63
  # @param headers [Hash]
64
64
  # @return [Hash]
65
65
  def ensure_content_type(headers)
66
- if headers[Grape::Http::Headers::CONTENT_TYPE]
66
+ if headers[Rack::CONTENT_TYPE]
67
67
  headers
68
68
  else
69
- headers.merge(Grape::Http::Headers::CONTENT_TYPE => content_type_for(env[Grape::Env::API_FORMAT]))
69
+ headers.merge(Rack::CONTENT_TYPE => content_type_for(env[Grape::Env::API_FORMAT]))
70
70
  end
71
71
  end
72
72
 
@@ -103,7 +103,7 @@ module Grape
103
103
  begin
104
104
  body = (env[Grape::Env::API_REQUEST_BODY] = parser.call(body, env))
105
105
  if body.is_a?(Hash)
106
- env[Grape::Env::RACK_REQUEST_FORM_HASH] = if env[Grape::Env::RACK_REQUEST_FORM_HASH]
106
+ env[Grape::Env::RACK_REQUEST_FORM_HASH] = if env.key?(Grape::Env::RACK_REQUEST_FORM_HASH)
107
107
  env[Grape::Env::RACK_REQUEST_FORM_HASH].merge(body)
108
108
  else
109
109
  body
@@ -164,14 +164,14 @@ module Grape
164
164
  \w+/[\w+.-]+) # eg application/vnd.example.myformat+xml
165
165
  (?:
166
166
  (?:;[^,]*?)? # optionally multiple formats in a row
167
- ;\s*q=([\d.]+) # optional "quality" preference (eg q=0.5)
167
+ ;\s*q=([\w.]+) # optional "quality" preference (eg q=0.5)
168
168
  )?
169
169
  }x
170
170
 
171
171
  vendor_prefix_pattern = /vnd\.[^+]+\+/
172
172
 
173
173
  accept.scan(accept_into_mime_and_quality)
174
- .sort_by { |_, quality_preference| -quality_preference.to_f }
174
+ .sort_by { |_, quality_preference| -(quality_preference ? quality_preference.to_f : 1.0) }
175
175
  .flat_map { |mime, _| [mime, mime.sub(vendor_prefix_pattern, '')] }
176
176
  end
177
177
  end
@@ -57,8 +57,7 @@ module Grape
57
57
  end
58
58
 
59
59
  def strict_version_vendor_accept_header_presence_check
60
- return unless versions.present?
61
- return if an_accept_header_with_version_and_vendor_is_present?
60
+ return if versions.blank? || an_accept_header_with_version_and_vendor_is_present?
62
61
 
63
62
  fail_with_invalid_accept_header!('API vendor or version not found.')
64
63
  end
@@ -101,25 +100,18 @@ module Grape
101
100
  end
102
101
 
103
102
  def available_media_types
104
- available_media_types = []
105
-
106
- content_types.each_key do |extension|
107
- versions.reverse_each do |version|
108
- available_media_types += [
109
- "application/vnd.#{vendor}-#{version}+#{extension}",
110
- "application/vnd.#{vendor}-#{version}"
111
- ]
103
+ [].tap do |available_media_types|
104
+ content_types.each_key do |extension|
105
+ versions.reverse_each do |version|
106
+ available_media_types << "application/vnd.#{vendor}-#{version}+#{extension}"
107
+ available_media_types << "application/vnd.#{vendor}-#{version}"
108
+ end
109
+ available_media_types << "application/vnd.#{vendor}+#{extension}"
112
110
  end
113
- available_media_types << "application/vnd.#{vendor}+#{extension}"
114
- end
115
-
116
- available_media_types << "application/vnd.#{vendor}"
117
111
 
118
- content_types.each_value do |media_type|
119
- available_media_types << media_type
112
+ available_media_types << "application/vnd.#{vendor}"
113
+ available_media_types.concat(content_types.values.flatten)
120
114
  end
121
-
122
- available_media_types.flatten
123
115
  end
124
116
 
125
117
  def headers_contain_wrong_vendor?
@@ -130,7 +122,7 @@ module Grape
130
122
 
131
123
  def headers_contain_wrong_version?
132
124
  header.values.all? do |header_value|
133
- version?(header_value) && !versions.include?(request_version(header_value))
125
+ version?(header_value) && versions.exclude?(request_version(header_value))
134
126
  end
135
127
  end
136
128
 
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ class Railtie < ::Rails::Railtie
5
+ initializer 'grape.deprecator' do |app|
6
+ app.deprecators[:grape] = Grape.deprecator
7
+ end
8
+ end
9
+ end
data/lib/grape/request.rb CHANGED
@@ -46,8 +46,14 @@ module Grape
46
46
  end
47
47
  end
48
48
 
49
- def transform_header(header)
50
- -header[5..].split('_').each(&:capitalize!).join('-')
49
+ if Grape.lowercase_headers?
50
+ def transform_header(header)
51
+ -header[5..].tr('_', '-').downcase
52
+ end
53
+ else
54
+ def transform_header(header)
55
+ -header[5..].split('_').map(&:capitalize).join('-')
56
+ end
51
57
  end
52
58
  end
53
59
  end
@@ -84,9 +84,7 @@ module Grape
84
84
  path, line = *location.scan(SOURCE_LOCATION_REGEXP).first
85
85
  path = File.realpath(path) if Pathname.new(path).relative?
86
86
  expected ||= name
87
- warn <<~WARNING
88
- #{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.
89
- WARNING
87
+ Grape.deprecator.warn("#{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.")
90
88
  end
91
89
  end
92
90
  end
@@ -70,29 +70,21 @@ module Grape
70
70
  end
71
71
 
72
72
  def evaluate
73
- evaluated = []
74
- @value_hash.each_with_index do |value, index|
75
- evaluated[index] = value.evaluate
76
- end
77
- evaluated
73
+ @value_hash.map(&:evaluate)
78
74
  end
79
75
  end
80
76
 
81
77
  class LazyValueHash < LazyValueEnumerable
82
78
  def initialize(hash)
83
79
  super
84
- @value_hash = {}.with_indifferent_access
80
+ @value_hash = ActiveSupport::HashWithIndifferentAccess.new
85
81
  hash.each do |key, value|
86
82
  self[key] = value
87
83
  end
88
84
  end
89
85
 
90
86
  def evaluate
91
- evaluated = {}.with_indifferent_access
92
- @value_hash.each do |key, value|
93
- evaluated[key] = value.evaluate
94
- end
95
- evaluated
87
+ @value_hash.transform_values(&:evaluate)
96
88
  end
97
89
  end
98
90
  end
@@ -66,11 +66,10 @@ module Grape
66
66
  end
67
67
 
68
68
  define_method :to_hash do
69
- merge_hash = {}
70
- setting_name.each_key { |k| merge_hash[k] = send("#{k}_context").to_hash }
71
-
72
69
  @settings.to_hash.merge(
73
- merge_hash
70
+ setting_name.each_key.with_object({}) do |k, merge_hash|
71
+ merge_hash[k] = send("#{k}_context").to_hash
72
+ end
74
73
  )
75
74
  end
76
75
  end
@@ -6,7 +6,7 @@ module Grape
6
6
  private
7
7
 
8
8
  def yield_attributes(resource_params, _attrs)
9
- yield resource_params, skip?(resource_params)
9
+ yield resource_params unless skip?(resource_params)
10
10
  end
11
11
  end
12
12
  end
@@ -10,6 +10,11 @@ module Grape
10
10
 
11
11
  include Grape::DSL::Parameters
12
12
 
13
+ # There are a number of documentation options on entities that don't have
14
+ # corresponding validators. Since there is nowhere that enumerates them all,
15
+ # we maintain a list of them here and skip looking up validators for them.
16
+ RESERVED_DOCUMENTATION_KEYWORDS = %i[as required param_type is_array format example].freeze
17
+
13
18
  class Attr
14
19
  attr_accessor :key, :scope
15
20
 
@@ -359,7 +364,8 @@ module Grape
359
364
  coerce_type validations, attrs, doc, opts
360
365
 
361
366
  validations.each do |type, options|
362
- next if type == :as
367
+ # Don't try to look up validators for documentation params that don't have one.
368
+ next if RESERVED_DOCUMENTATION_KEYWORDS.include?(type)
363
369
 
364
370
  validate(type, options, attrs, doc, opts)
365
371
  end
@@ -414,7 +420,7 @@ module Grape
414
420
 
415
421
  # but not special JSON types, which
416
422
  # already imply coercion method
417
- return unless [JSON, Array[JSON]].include? validations[:coerce]
423
+ return if [JSON, Array[JSON]].exclude? validations[:coerce]
418
424
 
419
425
  raise ArgumentError, 'coerce_with disallowed for type: JSON'
420
426
  end
@@ -6,8 +6,10 @@ module Grape
6
6
  private
7
7
 
8
8
  def yield_attributes(val, attrs)
9
+ return if skip?(val)
10
+
9
11
  attrs.each do |attr_name|
10
- yield val, attr_name, empty?(val), skip?(val)
12
+ yield val, attr_name, empty?(val)
11
13
  end
12
14
  end
13
15
 
@@ -141,7 +141,7 @@ module Grape
141
141
  lambda do |val|
142
142
  method.call(val).tap do |new_val|
143
143
  new_val.map do |item|
144
- item.is_a?(Hash) ? symbolize_keys(item) : item
144
+ item.is_a?(Hash) ? item.deep_symbolize_keys : item
145
145
  end
146
146
  end
147
147
  end
@@ -149,7 +149,7 @@ module Grape
149
149
  # Hash objects are processed directly
150
150
  elsif type == Hash
151
151
  lambda do |val|
152
- symbolize_keys method.call(val)
152
+ method.call(val).deep_symbolize_keys
153
153
  end
154
154
 
155
155
  # Simple types are not processed.
@@ -158,20 +158,6 @@ module Grape
158
158
  method
159
159
  end
160
160
  end
161
-
162
- def symbolize_keys!(hash)
163
- hash.each_key do |key|
164
- hash[key.to_sym] = hash.delete(key) if key.respond_to?(:to_sym)
165
- end
166
- hash
167
- end
168
-
169
- def symbolize_keys(hash)
170
- hash.inject({}) do |new_hash, (key, value)|
171
- new_key = key.respond_to?(:to_sym) ? key.to_sym : key
172
- new_hash.merge!(new_key => value)
173
- end
174
- end
175
161
  end
176
162
  end
177
163
  end
@@ -46,34 +46,23 @@ module Grape
46
46
  # there may be more than one error per field
47
47
  array_errors = []
48
48
 
49
- attributes.each do |val, attr_name, empty_val, skip_value|
50
- next if skip_value
49
+ attributes.each do |val, attr_name, empty_val|
51
50
  next if !@scope.required? && empty_val
52
51
  next unless @scope.meets_dependency?(val, params)
53
52
 
54
- begin
55
- validate_param!(attr_name, val) if @required || (val.respond_to?(:key?) && val.key?(attr_name))
56
- rescue Grape::Exceptions::Validation => e
57
- array_errors << e
58
- end
53
+ validate_param!(attr_name, val) if @required || (val.respond_to?(:key?) && val.key?(attr_name))
54
+ rescue Grape::Exceptions::Validation => e
55
+ array_errors << e
59
56
  end
60
57
 
61
58
  raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
62
59
  end
63
60
 
64
- def self.convert_to_short_name(klass)
65
- ret = klass.name.gsub(/::/, '/')
66
- ret.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
67
- ret.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
68
- ret.tr!('-', '_')
69
- ret.downcase!
70
- File.basename(ret, '_validator')
71
- end
72
-
73
61
  def self.inherited(klass)
74
- return unless klass.name.present?
62
+ return if klass.name.blank?
75
63
 
76
- Validations.register_validator(convert_to_short_name(klass), klass)
64
+ short_validator_name = klass.name.demodulize.underscore.delete_suffix('_validator')
65
+ Validations.register_validator(short_validator_name, klass)
77
66
  end
78
67
 
79
68
  def message(default_key = nil)
@@ -95,8 +84,8 @@ module Grape
95
84
  end
96
85
 
97
86
  Grape::Validations::Base = Class.new(Grape::Validations::Validators::Base) do
98
- def initialize(*)
87
+ def self.inherited(*)
88
+ Grape.deprecator.warn 'Grape::Validations::Base is deprecated! Use Grape::Validations::Validators::Base instead.'
99
89
  super
100
- warn '[DEPRECATION] `Grape::Validations::Base` is deprecated. Use `Grape::Validations::Validators::Base` instead.'
101
90
  end
102
91
  end
@@ -12,10 +12,10 @@ module Grape
12
12
  def validate_param!(attr_name, params)
13
13
  params[attr_name] = if @default.is_a? Proc
14
14
  @default.call
15
- elsif @default.frozen? || !duplicatable?(@default)
15
+ elsif @default.frozen? || !@default.duplicable?
16
16
  @default
17
17
  else
18
- duplicate(@default)
18
+ @default.dup
19
19
  end
20
20
  end
21
21
 
@@ -27,24 +27,6 @@ module Grape
27
27
  validate_param!(attr_name, resource_params) if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
28
28
  end
29
29
  end
30
-
31
- private
32
-
33
- # return true if we might be able to dup this object
34
- def duplicatable?(obj)
35
- !obj.nil? &&
36
- obj != true &&
37
- obj != false &&
38
- !obj.is_a?(Symbol) &&
39
- !obj.is_a?(Numeric)
40
- end
41
-
42
- # make a best effort to dup the object
43
- def duplicate(obj)
44
- obj.dup
45
- rescue TypeError
46
- obj
47
- end
48
30
  end
49
31
  end
50
32
  end
@@ -8,14 +8,10 @@ module Grape
8
8
  attributes = MultipleAttributesIterator.new(self, @scope, params)
9
9
  array_errors = []
10
10
 
11
- attributes.each do |resource_params, skip_value|
12
- next if skip_value
13
-
14
- begin
15
- validate_params!(resource_params)
16
- rescue Grape::Exceptions::Validation => e
17
- array_errors << e
18
- end
11
+ attributes.each do |resource_params|
12
+ validate_params!(resource_params)
13
+ rescue Grape::Exceptions::Validation => e
14
+ array_errors << e
19
15
  end
20
16
 
21
17
  raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
@@ -10,13 +10,11 @@ module Grape
10
10
  @values = options[:value]
11
11
  @proc = options[:proc]
12
12
 
13
- warn '[DEPRECATION] The values validator except option is deprecated. ' \
14
- 'Use the except validator instead.' if @excepts
13
+ Grape.deprecator.warn('The values validator except option is deprecated. Use the except validator instead.') if @excepts
15
14
 
16
15
  raise ArgumentError, 'proc must be a Proc' if @proc && !@proc.is_a?(Proc)
17
16
 
18
- warn '[DEPRECATION] The values validator proc option is deprecated. ' \
19
- 'The lambda expression can now be assigned directly to values.' if @proc
17
+ Grape.deprecator.warn('The values validator proc option is deprecated. The lambda expression can now be assigned directly to values.') if @proc
20
18
  else
21
19
  @excepts = nil
22
20
  @values = nil
@@ -45,7 +43,7 @@ module Grape
45
43
  unless check_values(param_array, attr_name)
46
44
 
47
45
  raise validation_exception(attr_name, message(:values)) \
48
- if @proc && !param_array.all? { |param| @proc.call(param) }
46
+ if @proc && !validate_proc(@proc, param_array)
49
47
  end
50
48
 
51
49
  private
@@ -70,6 +68,17 @@ module Grape
70
68
  param_array.none? { |param| excepts.include?(param) }
71
69
  end
72
70
 
71
+ def validate_proc(proc, param_array)
72
+ case proc.arity
73
+ when 0
74
+ param_array.all? { |_param| proc.call }
75
+ when 1
76
+ param_array.all? { |param| proc.call(param) }
77
+ else
78
+ raise ArgumentError, 'proc arity must be 0 or 1'
79
+ end
80
+ end
81
+
73
82
  def except_message
74
83
  options = instance_variable_get(:@option)
75
84
  options_key?(:except_message) ? options[:except_message] : message(:except_values)
data/lib/grape/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Grape
4
4
  # The current version of Grape.
5
- VERSION = '1.7.1'
5
+ VERSION = '2.0.0'
6
6
  end
data/lib/grape.rb CHANGED
@@ -5,33 +5,49 @@ require 'rack'
5
5
  require 'rack/builder'
6
6
  require 'rack/accept'
7
7
  require 'rack/auth/basic'
8
- require 'rack/auth/digest/md5'
9
8
  require 'set'
10
9
  require 'bigdecimal'
11
10
  require 'date'
12
11
  require 'active_support'
13
12
  require 'active_support/concern'
13
+ require 'active_support/configurable'
14
14
  require 'active_support/version'
15
15
  require 'active_support/isolated_execution_state' if ActiveSupport::VERSION::MAJOR > 6
16
16
  require 'active_support/core_ext/array/conversions'
17
17
  require 'active_support/core_ext/array/extract_options'
18
18
  require 'active_support/core_ext/array/wrap'
19
+ require 'active_support/core_ext/enumerable'
19
20
  require 'active_support/core_ext/hash/conversions'
20
21
  require 'active_support/core_ext/hash/deep_merge'
21
22
  require 'active_support/core_ext/hash/except'
22
23
  require 'active_support/core_ext/hash/indifferent_access'
24
+ require 'active_support/core_ext/hash/keys'
23
25
  require 'active_support/core_ext/hash/reverse_merge'
24
26
  require 'active_support/core_ext/hash/slice'
25
27
  require 'active_support/core_ext/object/blank'
28
+ require 'active_support/core_ext/object/deep_dup'
29
+ require 'active_support/core_ext/object/duplicable'
30
+ require 'active_support/core_ext/string/exclude'
26
31
  require 'active_support/dependencies/autoload'
32
+ require 'active_support/deprecation'
33
+ require 'active_support/inflector'
27
34
  require 'active_support/notifications'
28
35
  require 'i18n'
29
36
 
30
37
  I18n.load_path << File.expand_path('grape/locale/en.yml', __dir__)
31
38
 
32
39
  module Grape
40
+ include ActiveSupport::Configurable
33
41
  extend ::ActiveSupport::Autoload
34
42
 
43
+ def self.deprecator
44
+ @deprecator ||= ActiveSupport::Deprecation.new('2.0', 'Grape')
45
+ end
46
+
47
+ def self.lowercase_headers?
48
+ Rack::CONTENT_TYPE == 'content-type'
49
+ end
50
+
35
51
  eager_autoload do
36
52
  autoload :API
37
53
  autoload :Endpoint
@@ -92,8 +108,6 @@ module Grape
92
108
  module Extensions
93
109
  extend ::ActiveSupport::Autoload
94
110
  eager_autoload do
95
- autoload :DeepMergeableHash
96
- autoload :DeepSymbolizeHash
97
111
  autoload :Hash
98
112
  end
99
113
  module ActiveSupport
@@ -285,13 +299,20 @@ module Grape
285
299
  autoload :InvalidValue
286
300
  end
287
301
  end
302
+
303
+ configure do |config|
304
+ config.param_builder = Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
305
+ config.compile_methods!
306
+ end
288
307
  end
289
308
 
290
- require 'grape/config'
291
309
  require 'grape/content_types'
292
310
 
293
311
  require 'grape/util/lazy_value'
294
312
  require 'grape/util/lazy_block'
295
313
  require 'grape/util/endpoint_configuration'
296
-
297
314
  require 'grape/version'
315
+
316
+ # https://api.rubyonrails.org/classes/ActiveSupport/Deprecation.html
317
+ # adding Grape.deprecator to Rails App if any
318
+ require 'grape/railtie' if defined?(Rails::Railtie) && ActiveSupport.gem_version >= Gem::Version.new('7.1')