grape 1.2.5 → 1.4.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 (262) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -0
  3. data/LICENSE +1 -1
  4. data/README.md +53 -16
  5. data/UPGRADING.md +231 -23
  6. data/grape.gemspec +10 -1
  7. data/lib/grape.rb +6 -7
  8. data/lib/grape/api.rb +4 -2
  9. data/lib/grape/api/helpers.rb +2 -0
  10. data/lib/grape/api/instance.rb +36 -33
  11. data/lib/grape/config.rb +2 -0
  12. data/lib/grape/content_types.rb +34 -0
  13. data/lib/grape/cookies.rb +2 -0
  14. data/lib/grape/dsl/api.rb +2 -0
  15. data/lib/grape/dsl/callbacks.rb +2 -0
  16. data/lib/grape/dsl/configuration.rb +2 -0
  17. data/lib/grape/dsl/desc.rb +2 -0
  18. data/lib/grape/dsl/headers.rb +2 -0
  19. data/lib/grape/dsl/helpers.rb +4 -2
  20. data/lib/grape/dsl/inside_route.rb +83 -34
  21. data/lib/grape/dsl/logger.rb +2 -0
  22. data/lib/grape/dsl/middleware.rb +2 -0
  23. data/lib/grape/dsl/parameters.rb +8 -6
  24. data/lib/grape/dsl/request_response.rb +4 -2
  25. data/lib/grape/dsl/routing.rb +9 -5
  26. data/lib/grape/dsl/settings.rb +7 -1
  27. data/lib/grape/dsl/validations.rb +20 -1
  28. data/lib/grape/eager_load.rb +3 -1
  29. data/lib/grape/endpoint.rb +21 -13
  30. data/lib/grape/error_formatter.rb +3 -1
  31. data/lib/grape/error_formatter/base.rb +2 -0
  32. data/lib/grape/error_formatter/json.rb +2 -0
  33. data/lib/grape/error_formatter/txt.rb +2 -0
  34. data/lib/grape/error_formatter/xml.rb +2 -0
  35. data/lib/grape/exceptions/base.rb +11 -13
  36. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  37. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  38. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  39. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  40. data/lib/grape/exceptions/invalid_response.rb +2 -0
  41. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  42. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  43. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  44. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  45. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  46. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  47. data/lib/grape/exceptions/missing_option.rb +2 -0
  48. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  49. data/lib/grape/exceptions/unknown_options.rb +2 -0
  50. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  51. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  52. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  53. data/lib/grape/exceptions/validation.rb +3 -1
  54. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  55. data/lib/grape/exceptions/validation_errors.rb +13 -12
  56. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  57. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  58. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  59. data/lib/grape/extensions/hash.rb +2 -0
  60. data/lib/grape/extensions/hashie/mash.rb +2 -0
  61. data/lib/grape/formatter.rb +5 -3
  62. data/lib/grape/formatter/json.rb +2 -0
  63. data/lib/grape/formatter/serializable_hash.rb +2 -0
  64. data/lib/grape/formatter/txt.rb +2 -0
  65. data/lib/grape/formatter/xml.rb +2 -0
  66. data/lib/grape/http/headers.rb +50 -18
  67. data/lib/grape/middleware/auth/base.rb +2 -0
  68. data/lib/grape/middleware/auth/dsl.rb +2 -0
  69. data/lib/grape/middleware/auth/strategies.rb +2 -0
  70. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  71. data/lib/grape/middleware/base.rb +7 -7
  72. data/lib/grape/middleware/error.rb +3 -1
  73. data/lib/grape/middleware/filter.rb +2 -0
  74. data/lib/grape/middleware/formatter.rb +8 -6
  75. data/lib/grape/middleware/globals.rb +2 -0
  76. data/lib/grape/middleware/helpers.rb +2 -0
  77. data/lib/grape/middleware/stack.rb +4 -1
  78. data/lib/grape/middleware/versioner.rb +2 -0
  79. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  80. data/lib/grape/middleware/versioner/header.rb +6 -4
  81. data/lib/grape/middleware/versioner/param.rb +3 -1
  82. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
  83. data/lib/grape/middleware/versioner/path.rb +3 -1
  84. data/lib/grape/namespace.rb +14 -2
  85. data/lib/grape/parser.rb +3 -1
  86. data/lib/grape/parser/json.rb +2 -0
  87. data/lib/grape/parser/xml.rb +2 -0
  88. data/lib/grape/path.rb +15 -3
  89. data/lib/grape/presenters/presenter.rb +2 -0
  90. data/lib/grape/request.rb +15 -8
  91. data/lib/grape/router.rb +30 -29
  92. data/lib/grape/router/attribute_translator.rb +39 -8
  93. data/lib/grape/router/pattern.rb +20 -16
  94. data/lib/grape/router/route.rb +12 -26
  95. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  96. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  97. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  98. data/lib/grape/util/base_inheritable.rb +15 -6
  99. data/lib/grape/util/cache.rb +20 -0
  100. data/lib/grape/util/endpoint_configuration.rb +2 -0
  101. data/lib/grape/util/env.rb +19 -17
  102. data/lib/grape/util/inheritable_setting.rb +2 -0
  103. data/lib/grape/util/inheritable_values.rb +2 -0
  104. data/lib/grape/util/json.rb +2 -0
  105. data/lib/grape/util/lazy_block.rb +2 -0
  106. data/lib/grape/util/lazy_object.rb +43 -0
  107. data/lib/grape/util/lazy_value.rb +2 -0
  108. data/lib/grape/util/registrable.rb +2 -0
  109. data/lib/grape/util/reverse_stackable_values.rb +4 -0
  110. data/lib/grape/util/stackable_values.rb +10 -20
  111. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  112. data/lib/grape/util/xml.rb +2 -0
  113. data/lib/grape/validations.rb +2 -0
  114. data/lib/grape/validations/attributes_iterator.rb +3 -3
  115. data/lib/grape/validations/multiple_attributes_iterator.rb +2 -0
  116. data/lib/grape/validations/params_scope.rb +27 -14
  117. data/lib/grape/validations/single_attribute_iterator.rb +13 -2
  118. data/lib/grape/validations/types.rb +12 -34
  119. data/lib/grape/validations/types/array_coercer.rb +65 -0
  120. data/lib/grape/validations/types/build_coercer.rb +47 -49
  121. data/lib/grape/validations/types/custom_type_coercer.rb +15 -49
  122. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  123. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  124. data/lib/grape/validations/types/file.rb +22 -18
  125. data/lib/grape/validations/types/json.rb +46 -39
  126. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  127. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  128. data/lib/grape/validations/types/set_coercer.rb +40 -0
  129. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  130. data/lib/grape/validations/validator_factory.rb +2 -0
  131. data/lib/grape/validations/validators/all_or_none.rb +3 -1
  132. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  133. data/lib/grape/validations/validators/as.rb +2 -0
  134. data/lib/grape/validations/validators/at_least_one_of.rb +3 -1
  135. data/lib/grape/validations/validators/base.rb +8 -5
  136. data/lib/grape/validations/validators/coerce.rb +39 -29
  137. data/lib/grape/validations/validators/default.rb +2 -1
  138. data/lib/grape/validations/validators/exactly_one_of.rb +6 -2
  139. data/lib/grape/validations/validators/except_values.rb +3 -1
  140. data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
  141. data/lib/grape/validations/validators/mutual_exclusion.rb +3 -1
  142. data/lib/grape/validations/validators/presence.rb +3 -1
  143. data/lib/grape/validations/validators/regexp.rb +4 -2
  144. data/lib/grape/validations/validators/same_as.rb +6 -3
  145. data/lib/grape/validations/validators/values.rb +17 -5
  146. data/lib/grape/version.rb +3 -1
  147. data/spec/grape/api/custom_validations_spec.rb +5 -3
  148. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  149. data/spec/grape/api/defines_boolean_in_params_spec.rb +5 -3
  150. data/spec/grape/api/inherited_helpers_spec.rb +2 -0
  151. data/spec/grape/api/instance_spec.rb +104 -0
  152. data/spec/grape/api/invalid_format_spec.rb +2 -0
  153. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  154. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  155. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  156. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  157. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  158. data/spec/grape/api/recognize_path_spec.rb +2 -0
  159. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  160. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  161. data/spec/grape/api/routes_with_requirements_spec.rb +2 -0
  162. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
  163. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  164. data/spec/grape/api_remount_spec.rb +2 -0
  165. data/spec/grape/api_spec.rb +99 -11
  166. data/spec/grape/config_spec.rb +2 -0
  167. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  168. data/spec/grape/dsl/configuration_spec.rb +2 -0
  169. data/spec/grape/dsl/desc_spec.rb +2 -0
  170. data/spec/grape/dsl/headers_spec.rb +2 -0
  171. data/spec/grape/dsl/helpers_spec.rb +4 -2
  172. data/spec/grape/dsl/inside_route_spec.rb +177 -33
  173. data/spec/grape/dsl/logger_spec.rb +2 -0
  174. data/spec/grape/dsl/middleware_spec.rb +2 -0
  175. data/spec/grape/dsl/parameters_spec.rb +2 -0
  176. data/spec/grape/dsl/request_response_spec.rb +2 -0
  177. data/spec/grape/dsl/routing_spec.rb +2 -0
  178. data/spec/grape/dsl/settings_spec.rb +2 -0
  179. data/spec/grape/dsl/validations_spec.rb +2 -0
  180. data/spec/grape/endpoint_spec.rb +21 -6
  181. data/spec/grape/entity_spec.rb +2 -0
  182. data/spec/grape/exceptions/base_spec.rb +3 -1
  183. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  184. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  185. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
  186. data/spec/grape/exceptions/invalid_response_spec.rb +2 -0
  187. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  188. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  189. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  190. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  191. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  192. data/spec/grape/exceptions/validation_errors_spec.rb +4 -2
  193. data/spec/grape/exceptions/validation_spec.rb +3 -1
  194. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  195. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  196. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  197. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  198. data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
  199. data/spec/grape/integration/rack_spec.rb +3 -1
  200. data/spec/grape/loading_spec.rb +2 -0
  201. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  202. data/spec/grape/middleware/auth/dsl_spec.rb +2 -0
  203. data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
  204. data/spec/grape/middleware/base_spec.rb +2 -0
  205. data/spec/grape/middleware/error_spec.rb +2 -0
  206. data/spec/grape/middleware/exception_spec.rb +3 -1
  207. data/spec/grape/middleware/formatter_spec.rb +19 -12
  208. data/spec/grape/middleware/globals_spec.rb +2 -0
  209. data/spec/grape/middleware/stack_spec.rb +11 -0
  210. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  211. data/spec/grape/middleware/versioner/header_spec.rb +3 -1
  212. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  213. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  214. data/spec/grape/middleware/versioner_spec.rb +2 -0
  215. data/spec/grape/named_api_spec.rb +2 -0
  216. data/spec/grape/parser_spec.rb +7 -5
  217. data/spec/grape/path_spec.rb +6 -4
  218. data/spec/grape/presenters/presenter_spec.rb +2 -0
  219. data/spec/grape/request_spec.rb +2 -0
  220. data/spec/grape/util/inheritable_setting_spec.rb +2 -0
  221. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  222. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  223. data/spec/grape/util/stackable_values_spec.rb +3 -1
  224. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  225. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  226. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  227. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +2 -0
  228. data/spec/grape/validations/params_scope_spec.rb +3 -1
  229. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -4
  230. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  231. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  232. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  233. data/spec/grape/validations/types_spec.rb +9 -36
  234. data/spec/grape/validations/validators/all_or_none_spec.rb +2 -0
  235. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  236. data/spec/grape/validations/validators/at_least_one_of_spec.rb +2 -0
  237. data/spec/grape/validations/validators/coerce_spec.rb +341 -136
  238. data/spec/grape/validations/validators/default_spec.rb +123 -0
  239. data/spec/grape/validations/validators/exactly_one_of_spec.rb +14 -12
  240. data/spec/grape/validations/validators/except_values_spec.rb +3 -1
  241. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +2 -0
  242. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  243. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  244. data/spec/grape/validations/validators/same_as_spec.rb +2 -0
  245. data/spec/grape/validations/validators/values_spec.rb +30 -5
  246. data/spec/grape/validations_spec.rb +91 -33
  247. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  248. data/spec/integration/multi_json/json_spec.rb +2 -0
  249. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  250. data/spec/shared/versioning_examples.rb +2 -0
  251. data/spec/spec_helper.rb +18 -0
  252. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  253. data/spec/support/content_type_helpers.rb +2 -0
  254. data/spec/support/eager_load.rb +19 -0
  255. data/spec/support/endpoint_faker.rb +2 -0
  256. data/spec/support/file_streamer.rb +2 -0
  257. data/spec/support/integer_helpers.rb +2 -0
  258. data/spec/support/versioned_helpers.rb +4 -2
  259. metadata +48 -28
  260. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  261. data/lib/grape/util/content_types.rb +0 -26
  262. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/middleware/base'
2
4
 
3
5
  module Grape
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Middleware
3
5
  # Common methods for all types of Grape middleware
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Middleware
3
5
  # Class to handle the stack of middlewares based on ActionDispatch::MiddlewareStack
@@ -76,7 +78,8 @@ module Grape
76
78
  def merge_with(middleware_specs)
77
79
  middleware_specs.each do |operation, *args|
78
80
  if args.last.is_a?(Proc)
79
- public_send(operation, *args, &args.pop)
81
+ last_proc = args.pop
82
+ public_send(operation, *args, &last_proc)
80
83
  else
81
84
  public_send(operation, *args)
82
85
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Versioners set env['api.version'] when a version is defined on an API and
2
4
  # on the requests. The current methods for determining version are:
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/middleware/base'
2
4
 
3
5
  module Grape
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/middleware/base'
2
4
  require 'grape/middleware/versioner/parse_media_type_patch'
3
5
 
@@ -24,10 +26,10 @@ module Grape
24
26
  # route.
25
27
  class Header < Base
26
28
  VENDOR_VERSION_HEADER_REGEX =
27
- /\Avnd\.([a-z0-9.\-_!#\$&\^]+?)(?:-([a-z0-9*.]+))?(?:\+([a-z0-9*\-.]+))?\z/
29
+ /\Avnd\.([a-z0-9.\-_!#\$&\^]+?)(?:-([a-z0-9*.]+))?(?:\+([a-z0-9*\-.]+))?\z/.freeze
28
30
 
29
- HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_!#\$&\^]+/
30
- HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_!#\$&\^]+?)(?:-([a-z0-9*.]+))+/
31
+ HAS_VENDOR_REGEX = /\Avnd\.[a-z0-9.\-_!#\$&\^]+/.freeze
32
+ HAS_VERSION_REGEX = /\Avnd\.([a-z0-9.\-_!#\$&\^]+?)(?:-([a-z0-9*.]+))+/.freeze
31
33
 
32
34
  def before
33
35
  strict_header_checks if strict?
@@ -61,7 +63,7 @@ module Grape
61
63
 
62
64
  def an_accept_header_with_version_and_vendor_is_present?
63
65
  header.qvalues.keys.any? do |h|
64
- VENDOR_VERSION_HEADER_REGEX =~ h.sub('application/', '')
66
+ VENDOR_VERSION_HEADER_REGEX.match?(h.sub('application/', ''))
65
67
  end
66
68
  end
67
69
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/middleware/base'
2
4
 
3
5
  module Grape
@@ -22,7 +24,7 @@ module Grape
22
24
  def default_options
23
25
  {
24
26
  version_options: {
25
- parameter: 'apiver'.freeze
27
+ parameter: 'apiver'
26
28
  }
27
29
  }
28
30
  end
@@ -1,11 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  module Accept
3
5
  module Header
6
+ ALLOWED_CHARACTERS = %r{^([a-z*]+)\/([a-z0-9*\&\^\-_#\$!.+]+)(?:;([a-z0-9=;]+))?$}.freeze
4
7
  class << self
5
8
  # Corrected version of https://github.com/mjackson/rack-accept/blob/master/lib/rack/accept/header.rb#L40-L44
6
9
  def parse_media_type(media_type)
7
10
  # see http://tools.ietf.org/html/rfc6838#section-4.2 for allowed characters in media type names
8
- m = media_type.to_s.match(%r{^([a-z*]+)\/([a-z0-9*\&\^\-_#\$!.+]+)(?:;([a-z0-9=;]+))?$})
11
+ m = media_type&.match(ALLOWED_CHARACTERS)
9
12
  m ? [m[1], m[2], m[3] || ''] : []
10
13
  end
11
14
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/middleware/base'
2
4
 
3
5
  module Grape
@@ -34,7 +36,7 @@ module Grape
34
36
 
35
37
  pieces = path.split('/')
36
38
  potential_version = pieces[1]
37
- return unless potential_version =~ options[:pattern]
39
+ return unless potential_version&.match?(options[:pattern])
38
40
  throw :error, status: 404, message: '404 API Version Not Found' if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
39
41
  env[Grape::Env::API_VERSION] = potential_version
40
42
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grape/util/cache'
4
+
1
5
  module Grape
2
6
  # A container for endpoints or other namespaces, which allows for both
3
7
  # logical grouping of endpoints as well as sharing common configuration.
@@ -23,13 +27,21 @@ module Grape
23
27
 
24
28
  # (see ::joined_space_path)
25
29
  def self.joined_space(settings)
26
- (settings || []).map(&:space).join('/')
30
+ settings&.map(&:space)
27
31
  end
28
32
 
29
33
  # Join the namespaces from a list of settings to create a path prefix.
30
34
  # @param settings [Array] list of Grape::Util::InheritableSettings.
31
35
  def self.joined_space_path(settings)
32
- Grape::Router.normalize_path(joined_space(settings))
36
+ Grape::Router.normalize_path(JoinedSpaceCache[joined_space(settings)])
37
+ end
38
+
39
+ class JoinedSpaceCache < Grape::Util::Cache
40
+ def initialize
41
+ @cache = Hash.new do |h, joined_space|
42
+ h[joined_space] = -joined_space.join('/')
43
+ end
44
+ end
33
45
  end
34
46
  end
35
47
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Parser
3
5
  extend Util::Registrable
@@ -11,7 +13,7 @@ module Grape
11
13
  }
12
14
  end
13
15
 
14
- def parsers(options)
16
+ def parsers(**options)
15
17
  builtin_parsers.merge(default_elements).merge!(options[:parsers] || {})
16
18
  end
17
19
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Parser
3
5
  module Json
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Parser
3
5
  module Xml
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grape/util/cache'
4
+
1
5
  module Grape
2
6
  # Represents a path to an endpoint.
3
7
  class Path
@@ -38,11 +42,11 @@ module Grape
38
42
  end
39
43
 
40
44
  def namespace?
41
- namespace && namespace.to_s =~ /^\S/ && namespace != '/'
45
+ namespace&.match?(/^\S/) && namespace != '/'
42
46
  end
43
47
 
44
48
  def path?
45
- raw_path && raw_path.to_s =~ /^\S/ && raw_path != '/'
49
+ raw_path&.match?(/^\S/) && raw_path != '/'
46
50
  end
47
51
 
48
52
  def suffix
@@ -56,7 +60,7 @@ module Grape
56
60
  end
57
61
 
58
62
  def path
59
- Grape::Router.normalize_path(parts.join('/'))
63
+ Grape::Router.normalize_path(PartsCache[parts])
60
64
  end
61
65
 
62
66
  def path_with_suffix
@@ -69,6 +73,14 @@ module Grape
69
73
 
70
74
  private
71
75
 
76
+ class PartsCache < Grape::Util::Cache
77
+ def initialize
78
+ @cache = Hash.new do |h, parts|
79
+ h[parts] = -parts.join('/')
80
+ end
81
+ end
82
+ end
83
+
72
84
  def parts
73
85
  parts = [mount_path, root_prefix].compact
74
86
  parts << ':version' if uses_path_versioning?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Presenters
3
5
  class Presenter
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grape/util/lazy_object'
4
+
1
5
  module Grape
2
6
  class Request < Rack::Request
3
- HTTP_PREFIX = 'HTTP_'.freeze
7
+ HTTP_PREFIX = 'HTTP_'
4
8
 
5
9
  alias rack_params params
6
10
 
@@ -28,14 +32,17 @@ module Grape
28
32
  end
29
33
 
30
34
  def build_headers
31
- headers = {}
32
- env.each_pair do |k, v|
33
- next unless k.to_s.start_with? HTTP_PREFIX
34
-
35
- k = k[5..-1].split('_').each(&:capitalize!).join('-')
36
- headers[k] = v
35
+ Grape::Util::LazyObject.new do
36
+ env.each_pair.with_object({}) do |(k, v), headers|
37
+ next unless k.to_s.start_with? HTTP_PREFIX
38
+ transformed_header = Grape::Http::Headers::HTTP_HEADERS[k] || transform_header(k)
39
+ headers[transformed_header] = v
40
+ end
37
41
  end
38
- headers
42
+ end
43
+
44
+ def transform_header(header)
45
+ -header[5..-1].split('_').each(&:capitalize!).join('-')
39
46
  end
40
47
  end
41
48
  end
@@ -1,18 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/router/route'
4
+ require 'grape/util/cache'
2
5
 
3
6
  module Grape
4
7
  class Router
5
8
  attr_reader :map, :compiled
6
9
 
7
- class Any < AttributeTranslator
8
- def initialize(pattern, **attributes)
9
- @pattern = pattern
10
- super(attributes)
11
- end
12
- end
13
-
14
10
  def self.normalize_path(path)
15
- path = "/#{path}"
11
+ path = +"/#{path}"
16
12
  path.squeeze!('/')
17
13
  path.sub!(%r{/+\Z}, '')
18
14
  path = '/' if path == ''
@@ -25,18 +21,20 @@ module Grape
25
21
 
26
22
  def initialize
27
23
  @neutral_map = []
24
+ @neutral_regexes = []
28
25
  @map = Hash.new { |hash, key| hash[key] = [] }
29
26
  @optimized_map = Hash.new { |hash, key| hash[key] = // }
30
27
  end
31
28
 
32
29
  def compile!
33
30
  return if compiled
34
- @union = Regexp.union(@neutral_map.map(&:regexp))
31
+ @union = Regexp.union(@neutral_regexes)
32
+ @neutral_regexes = nil
35
33
  self.class.supported_methods.each do |method|
36
34
  routes = map[method]
37
35
  @optimized_map[method] = routes.map.with_index do |route, index|
38
36
  route.index = index
39
- route.regexp = /(?<_#{index}>#{route.pattern.to_regexp})/
37
+ Regexp.new("(?<_#{index}>#{route.pattern.to_regexp})")
40
38
  end
41
39
  @optimized_map[method] = Regexp.union(@optimized_map[method])
42
40
  end
@@ -44,12 +42,12 @@ module Grape
44
42
  end
45
43
 
46
44
  def append(route)
47
- map[route.request_method.to_s.upcase] << route
45
+ map[route.request_method] << route
48
46
  end
49
47
 
50
48
  def associate_routes(pattern, **options)
51
- regexp = /(?<_#{@neutral_map.length}>)#{pattern.to_regexp}/
52
- @neutral_map << Any.new(pattern, regexp: regexp, index: @neutral_map.length, **options)
49
+ @neutral_regexes << Regexp.new("(?<_#{@neutral_map.length}>)#{pattern.to_regexp}")
50
+ @neutral_map << Grape::Router::AttributeTranslator.new(options.merge(pattern: pattern, index: @neutral_map.length))
53
51
  end
54
52
 
55
53
  def call(env)
@@ -93,37 +91,34 @@ module Grape
93
91
  response = yield(input, method)
94
92
 
95
93
  return response if response && !(cascade = cascade?(response))
96
- neighbor = greedy_match?(input)
94
+ last_neighbor_route = greedy_match?(input)
97
95
 
98
- # If neighbor exists and request method is OPTIONS,
96
+ # If last_neighbor_route exists and request method is OPTIONS,
99
97
  # return response by using #call_with_allow_headers.
100
- return call_with_allow_headers(
101
- env,
102
- neighbor.allow_header,
103
- neighbor.endpoint
104
- ) if neighbor && method == 'OPTIONS' && !cascade
98
+ return call_with_allow_headers(env, last_neighbor_route) if last_neighbor_route && method == Grape::Http::Headers::OPTIONS && !cascade
105
99
 
106
100
  route = match?(input, '*')
107
- return neighbor.endpoint.call(env) if neighbor && cascade && route
101
+
102
+ return last_neighbor_route.endpoint.call(env) if last_neighbor_route && cascade && route
108
103
 
109
104
  if route
110
105
  response = process_route(route, env)
111
106
  return response if response && !(cascade = cascade?(response))
112
107
  end
113
108
 
114
- !cascade && neighbor ? call_with_allow_headers(env, neighbor.allow_header, neighbor.endpoint) : nil
109
+ return call_with_allow_headers(env, last_neighbor_route) if !cascade && last_neighbor_route
110
+
111
+ nil
115
112
  end
116
113
 
117
114
  def process_route(route, env)
118
- input, = *extract_input_and_method(env)
119
- routing_args = env[Grape::Env::GRAPE_ROUTING_ARGS]
120
- env[Grape::Env::GRAPE_ROUTING_ARGS] = make_routing_args(routing_args, route, input)
115
+ prepare_env_from_route(env, route)
121
116
  route.exec(env)
122
117
  end
123
118
 
124
119
  def make_routing_args(default_args, route, input)
125
120
  args = default_args || { route_info: route }
126
- args.merge(route.params(input))
121
+ args.merge(route.params(input) || {})
127
122
  end
128
123
 
129
124
  def extract_input_and_method(env)
@@ -154,9 +149,15 @@ module Grape
154
149
  @neutral_map.detect { |route| last_match["_#{route.index}"] }
155
150
  end
156
151
 
157
- def call_with_allow_headers(env, methods, endpoint)
158
- env[Grape::Env::GRAPE_ALLOWED_METHODS] = methods
159
- endpoint.call(env)
152
+ def call_with_allow_headers(env, route)
153
+ prepare_env_from_route(env, route)
154
+ env[Grape::Env::GRAPE_ALLOWED_METHODS] = route.allow_header.join(', ').freeze
155
+ route.endpoint.call(env)
156
+ end
157
+
158
+ def prepare_env_from_route(env, route)
159
+ input, = *extract_input_and_method(env)
160
+ env[Grape::Env::GRAPE_ROUTING_ARGS] = make_routing_args(env[Grape::Env::GRAPE_ROUTING_ARGS], route, input)
160
161
  end
161
162
 
162
163
  def cascade?(response)
@@ -1,32 +1,63 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  class Router
3
5
  # this could be an OpenStruct, but doesn't work in Ruby 2.3.0, see https://bugs.ruby-lang.org/issues/12251
4
6
  class AttributeTranslator
7
+ attr_reader :attributes, :request_method, :requirements
8
+
9
+ ROUTE_ATTRIBUTES = %i[
10
+ prefix
11
+ version
12
+ settings
13
+ format
14
+ description
15
+ http_codes
16
+ headers
17
+ entity
18
+ details
19
+ requirements
20
+ request_method
21
+ namespace
22
+ ].freeze
23
+
24
+ ROUTER_ATTRIBUTES = %i[pattern index].freeze
25
+
5
26
  def initialize(attributes = {})
6
27
  @attributes = attributes
7
28
  end
8
29
 
30
+ (ROUTER_ATTRIBUTES + ROUTE_ATTRIBUTES).each do |attr|
31
+ define_method attr do
32
+ attributes[attr]
33
+ end
34
+ end
35
+
9
36
  def to_h
10
- @attributes
37
+ attributes
11
38
  end
12
39
 
13
- def method_missing(m, *args)
14
- if m[-1] == '='
15
- @attributes[m[0..-1]] = *args
16
- elsif m[-1] != '='
17
- @attributes[m]
40
+ def method_missing(method_name, *args) # rubocop:disable Style/MethodMissing
41
+ if setter?(method_name[-1])
42
+ attributes[method_name[0..-1]] = *args
18
43
  else
19
- super
44
+ attributes[method_name]
20
45
  end
21
46
  end
22
47
 
23
48
  def respond_to_missing?(method_name, _include_private = false)
24
- if method_name[-1] == '='
49
+ if setter?(method_name[-1])
25
50
  true
26
51
  else
27
52
  @attributes.key?(method_name)
28
53
  end
29
54
  end
55
+
56
+ private
57
+
58
+ def setter?(method_name)
59
+ method_name[-1] == '='
60
+ end
30
61
  end
31
62
  end
32
63
  end