grape 1.7.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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')