grape 1.2.5 → 1.3.1

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 (258) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/README.md +7 -6
  4. data/UPGRADING.md +43 -0
  5. data/grape.gemspec +10 -1
  6. data/lib/grape/api/helpers.rb +2 -0
  7. data/lib/grape/api/instance.rb +8 -6
  8. data/lib/grape/api.rb +4 -2
  9. data/lib/grape/config.rb +2 -0
  10. data/lib/grape/content_types.rb +34 -0
  11. data/lib/grape/cookies.rb +2 -0
  12. data/lib/grape/dsl/api.rb +2 -0
  13. data/lib/grape/dsl/callbacks.rb +2 -0
  14. data/lib/grape/dsl/configuration.rb +2 -0
  15. data/lib/grape/dsl/desc.rb +2 -0
  16. data/lib/grape/dsl/headers.rb +2 -0
  17. data/lib/grape/dsl/helpers.rb +4 -2
  18. data/lib/grape/dsl/inside_route.rb +15 -11
  19. data/lib/grape/dsl/logger.rb +2 -0
  20. data/lib/grape/dsl/middleware.rb +2 -0
  21. data/lib/grape/dsl/parameters.rb +8 -6
  22. data/lib/grape/dsl/request_response.rb +4 -2
  23. data/lib/grape/dsl/routing.rb +9 -5
  24. data/lib/grape/dsl/settings.rb +7 -1
  25. data/lib/grape/dsl/validations.rb +2 -0
  26. data/lib/grape/eager_load.rb +2 -0
  27. data/lib/grape/endpoint.rb +13 -7
  28. data/lib/grape/error_formatter/base.rb +2 -0
  29. data/lib/grape/error_formatter/json.rb +2 -0
  30. data/lib/grape/error_formatter/txt.rb +2 -0
  31. data/lib/grape/error_formatter/xml.rb +2 -0
  32. data/lib/grape/error_formatter.rb +3 -1
  33. data/lib/grape/exceptions/base.rb +11 -13
  34. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  35. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  36. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  37. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  38. data/lib/grape/exceptions/invalid_response.rb +2 -0
  39. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  40. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  41. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  42. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  43. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  44. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  45. data/lib/grape/exceptions/missing_option.rb +2 -0
  46. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  47. data/lib/grape/exceptions/unknown_options.rb +2 -0
  48. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  49. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  50. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  51. data/lib/grape/exceptions/validation.rb +3 -1
  52. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  53. data/lib/grape/exceptions/validation_errors.rb +13 -12
  54. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  55. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  56. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  57. data/lib/grape/extensions/hash.rb +2 -0
  58. data/lib/grape/extensions/hashie/mash.rb +2 -0
  59. data/lib/grape/formatter/json.rb +2 -0
  60. data/lib/grape/formatter/serializable_hash.rb +2 -0
  61. data/lib/grape/formatter/txt.rb +2 -0
  62. data/lib/grape/formatter/xml.rb +2 -0
  63. data/lib/grape/formatter.rb +5 -3
  64. data/lib/grape/http/headers.rb +49 -18
  65. data/lib/grape/middleware/auth/base.rb +2 -0
  66. data/lib/grape/middleware/auth/dsl.rb +2 -0
  67. data/lib/grape/middleware/auth/strategies.rb +2 -0
  68. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  69. data/lib/grape/middleware/base.rb +5 -5
  70. data/lib/grape/middleware/error.rb +3 -1
  71. data/lib/grape/middleware/filter.rb +2 -0
  72. data/lib/grape/middleware/formatter.rb +5 -3
  73. data/lib/grape/middleware/globals.rb +2 -0
  74. data/lib/grape/middleware/helpers.rb +2 -0
  75. data/lib/grape/middleware/stack.rb +4 -1
  76. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  77. data/lib/grape/middleware/versioner/header.rb +5 -3
  78. data/lib/grape/middleware/versioner/param.rb +3 -1
  79. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -0
  80. data/lib/grape/middleware/versioner/path.rb +3 -1
  81. data/lib/grape/middleware/versioner.rb +2 -0
  82. data/lib/grape/namespace.rb +14 -2
  83. data/lib/grape/parser/json.rb +2 -0
  84. data/lib/grape/parser/xml.rb +2 -0
  85. data/lib/grape/parser.rb +3 -1
  86. data/lib/grape/path.rb +13 -1
  87. data/lib/grape/presenters/presenter.rb +2 -0
  88. data/lib/grape/request.rb +15 -8
  89. data/lib/grape/router/attribute_translator.rb +18 -8
  90. data/lib/grape/router/pattern.rb +20 -16
  91. data/lib/grape/router/route.rb +9 -4
  92. data/lib/grape/router.rb +26 -12
  93. data/lib/grape/serve_file/file_body.rb +2 -0
  94. data/lib/grape/serve_file/file_response.rb +2 -0
  95. data/lib/grape/serve_file/sendfile_response.rb +2 -0
  96. data/lib/grape/util/base_inheritable.rb +6 -0
  97. data/lib/grape/util/cache.rb +20 -0
  98. data/lib/grape/util/endpoint_configuration.rb +2 -0
  99. data/lib/grape/util/env.rb +19 -17
  100. data/lib/grape/util/inheritable_setting.rb +2 -0
  101. data/lib/grape/util/inheritable_values.rb +2 -0
  102. data/lib/grape/util/json.rb +2 -0
  103. data/lib/grape/util/lazy_block.rb +2 -0
  104. data/lib/grape/util/lazy_object.rb +43 -0
  105. data/lib/grape/util/lazy_value.rb +2 -0
  106. data/lib/grape/util/registrable.rb +2 -0
  107. data/lib/grape/util/reverse_stackable_values.rb +3 -1
  108. data/lib/grape/util/stackable_values.rb +9 -21
  109. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  110. data/lib/grape/util/xml.rb +2 -0
  111. data/lib/grape/validations/attributes_iterator.rb +3 -3
  112. data/lib/grape/validations/multiple_attributes_iterator.rb +2 -0
  113. data/lib/grape/validations/params_scope.rb +24 -11
  114. data/lib/grape/validations/single_attribute_iterator.rb +13 -2
  115. data/lib/grape/validations/types/array_coercer.rb +56 -0
  116. data/lib/grape/validations/types/build_coercer.rb +49 -48
  117. data/lib/grape/validations/types/custom_type_coercer.rb +15 -49
  118. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  119. data/lib/grape/validations/types/dry_type_coercer.rb +41 -0
  120. data/lib/grape/validations/types/file.rb +11 -9
  121. data/lib/grape/validations/types/json.rb +11 -8
  122. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  123. data/lib/grape/validations/types/primitive_coercer.rb +61 -0
  124. data/lib/grape/validations/types/set_coercer.rb +38 -0
  125. data/lib/grape/validations/types/variant_collection_coercer.rb +4 -12
  126. data/lib/grape/validations/types.rb +7 -30
  127. data/lib/grape/validations/validator_factory.rb +2 -0
  128. data/lib/grape/validations/validators/all_or_none.rb +3 -1
  129. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  130. data/lib/grape/validations/validators/as.rb +2 -0
  131. data/lib/grape/validations/validators/at_least_one_of.rb +3 -1
  132. data/lib/grape/validations/validators/base.rb +8 -5
  133. data/lib/grape/validations/validators/coerce.rb +44 -27
  134. data/lib/grape/validations/validators/default.rb +2 -0
  135. data/lib/grape/validations/validators/exactly_one_of.rb +6 -2
  136. data/lib/grape/validations/validators/except_values.rb +3 -1
  137. data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
  138. data/lib/grape/validations/validators/mutual_exclusion.rb +3 -1
  139. data/lib/grape/validations/validators/presence.rb +3 -1
  140. data/lib/grape/validations/validators/regexp.rb +3 -1
  141. data/lib/grape/validations/validators/same_as.rb +6 -3
  142. data/lib/grape/validations/validators/values.rb +17 -5
  143. data/lib/grape/validations.rb +2 -0
  144. data/lib/grape/version.rb +3 -1
  145. data/lib/grape.rb +4 -5
  146. data/spec/grape/api/custom_validations_spec.rb +5 -3
  147. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  148. data/spec/grape/api/defines_boolean_in_params_spec.rb +5 -3
  149. data/spec/grape/api/inherited_helpers_spec.rb +2 -0
  150. data/spec/grape/api/instance_spec.rb +54 -0
  151. data/spec/grape/api/invalid_format_spec.rb +2 -0
  152. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  153. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  154. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  155. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  156. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  157. data/spec/grape/api/recognize_path_spec.rb +2 -0
  158. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  159. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  160. data/spec/grape/api/routes_with_requirements_spec.rb +2 -0
  161. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
  162. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  163. data/spec/grape/api_remount_spec.rb +2 -0
  164. data/spec/grape/api_spec.rb +34 -11
  165. data/spec/grape/config_spec.rb +2 -0
  166. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  167. data/spec/grape/dsl/configuration_spec.rb +2 -0
  168. data/spec/grape/dsl/desc_spec.rb +2 -0
  169. data/spec/grape/dsl/headers_spec.rb +2 -0
  170. data/spec/grape/dsl/helpers_spec.rb +4 -2
  171. data/spec/grape/dsl/inside_route_spec.rb +2 -0
  172. data/spec/grape/dsl/logger_spec.rb +2 -0
  173. data/spec/grape/dsl/middleware_spec.rb +2 -0
  174. data/spec/grape/dsl/parameters_spec.rb +2 -0
  175. data/spec/grape/dsl/request_response_spec.rb +2 -0
  176. data/spec/grape/dsl/routing_spec.rb +2 -0
  177. data/spec/grape/dsl/settings_spec.rb +2 -0
  178. data/spec/grape/dsl/validations_spec.rb +2 -0
  179. data/spec/grape/endpoint_spec.rb +3 -1
  180. data/spec/grape/entity_spec.rb +2 -0
  181. data/spec/grape/exceptions/base_spec.rb +3 -1
  182. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  183. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  184. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
  185. data/spec/grape/exceptions/invalid_response_spec.rb +2 -0
  186. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  187. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  188. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  189. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  190. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  191. data/spec/grape/exceptions/validation_errors_spec.rb +4 -2
  192. data/spec/grape/exceptions/validation_spec.rb +3 -1
  193. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  194. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  195. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  196. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  197. data/spec/grape/integration/rack_sendfile_spec.rb +2 -0
  198. data/spec/grape/integration/rack_spec.rb +3 -1
  199. data/spec/grape/loading_spec.rb +2 -0
  200. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  201. data/spec/grape/middleware/auth/dsl_spec.rb +2 -0
  202. data/spec/grape/middleware/auth/strategies_spec.rb +2 -0
  203. data/spec/grape/middleware/base_spec.rb +2 -0
  204. data/spec/grape/middleware/error_spec.rb +2 -0
  205. data/spec/grape/middleware/exception_spec.rb +3 -1
  206. data/spec/grape/middleware/formatter_spec.rb +19 -12
  207. data/spec/grape/middleware/globals_spec.rb +2 -0
  208. data/spec/grape/middleware/stack_spec.rb +11 -0
  209. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  210. data/spec/grape/middleware/versioner/header_spec.rb +3 -1
  211. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  212. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  213. data/spec/grape/middleware/versioner_spec.rb +2 -0
  214. data/spec/grape/named_api_spec.rb +2 -0
  215. data/spec/grape/parser_spec.rb +7 -5
  216. data/spec/grape/path_spec.rb +2 -0
  217. data/spec/grape/presenters/presenter_spec.rb +2 -0
  218. data/spec/grape/request_spec.rb +2 -0
  219. data/spec/grape/util/inheritable_setting_spec.rb +2 -0
  220. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  221. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  222. data/spec/grape/util/stackable_values_spec.rb +3 -1
  223. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  224. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  225. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  226. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +2 -0
  227. data/spec/grape/validations/params_scope_spec.rb +3 -1
  228. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -4
  229. data/spec/grape/validations/types/primitive_coercer_spec.rb +75 -0
  230. data/spec/grape/validations/types_spec.rb +8 -35
  231. data/spec/grape/validations/validators/all_or_none_spec.rb +2 -0
  232. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  233. data/spec/grape/validations/validators/at_least_one_of_spec.rb +2 -0
  234. data/spec/grape/validations/validators/coerce_spec.rb +51 -110
  235. data/spec/grape/validations/validators/default_spec.rb +2 -0
  236. data/spec/grape/validations/validators/exactly_one_of_spec.rb +14 -12
  237. data/spec/grape/validations/validators/except_values_spec.rb +3 -1
  238. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +2 -0
  239. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  240. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  241. data/spec/grape/validations/validators/same_as_spec.rb +2 -0
  242. data/spec/grape/validations/validators/values_spec.rb +29 -4
  243. data/spec/grape/validations_spec.rb +69 -15
  244. data/spec/integration/multi_json/json_spec.rb +2 -0
  245. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  246. data/spec/shared/versioning_examples.rb +2 -0
  247. data/spec/spec_helper.rb +18 -0
  248. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  249. data/spec/support/content_type_helpers.rb +2 -0
  250. data/spec/support/eager_load.rb +19 -0
  251. data/spec/support/endpoint_faker.rb +2 -0
  252. data/spec/support/file_streamer.rb +2 -0
  253. data/spec/support/integer_helpers.rb +2 -0
  254. data/spec/support/versioned_helpers.rb +4 -2
  255. metadata +126 -112
  256. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  257. data/lib/grape/util/content_types.rb +0 -26
  258. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
@@ -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
  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?
@@ -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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Rack
2
4
  module Accept
3
5
  module Header
@@ -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,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,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
  module Json
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Parser
3
5
  module Xml
data/lib/grape/parser.rb CHANGED
@@ -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
 
data/lib/grape/path.rb CHANGED
@@ -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
@@ -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
data/lib/grape/request.rb CHANGED
@@ -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,32 +1,42 @@
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
+
5
9
  def initialize(attributes = {})
6
10
  @attributes = attributes
11
+ @request_method = attributes[:request_method]
12
+ @requirements = attributes[:requirements]
7
13
  end
8
14
 
9
15
  def to_h
10
- @attributes
16
+ attributes
11
17
  end
12
18
 
13
- def method_missing(m, *args)
14
- if m[-1] == '='
15
- @attributes[m[0..-1]] = *args
16
- elsif m[-1] != '='
17
- @attributes[m]
19
+ def method_missing(method_name, *args) # rubocop:disable Style/MethodMissing
20
+ if setter?(method_name[-1])
21
+ attributes[method_name[0..-1]] = *args
18
22
  else
19
- super
23
+ attributes[method_name]
20
24
  end
21
25
  end
22
26
 
23
27
  def respond_to_missing?(method_name, _include_private = false)
24
- if method_name[-1] == '='
28
+ if setter?(method_name[-1])
25
29
  true
26
30
  else
27
31
  @attributes.key?(method_name)
28
32
  end
29
33
  end
34
+
35
+ private
36
+
37
+ def setter?(method_name)
38
+ method_name[-1] == '='
39
+ end
30
40
  end
31
41
  end
32
42
  end
@@ -1,34 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
  require 'mustermann/grape'
5
+ require 'grape/util/cache'
3
6
 
4
7
  module Grape
5
8
  class Router
6
9
  class Pattern
7
- DEFAULT_PATTERN_OPTIONS = { uri_decode: true, type: :grape }.freeze
10
+ DEFAULT_PATTERN_OPTIONS = { uri_decode: true }.freeze
8
11
  DEFAULT_SUPPORTED_CAPTURE = %i[format version].freeze
9
12
 
10
- attr_reader :origin, :path, :capture, :pattern
13
+ attr_reader :origin, :path, :pattern, :to_regexp
11
14
 
12
15
  extend Forwardable
13
16
  def_delegators :pattern, :named_captures, :params
14
- def_delegators :@regexp, :===
17
+ def_delegators :to_regexp, :===
15
18
  alias match? ===
16
19
 
17
20
  def initialize(pattern, **options)
18
21
  @origin = pattern
19
22
  @path = build_path(pattern, **options)
20
- @capture = extract_capture(options)
21
- @pattern = Mustermann.new(@path, pattern_options)
22
- @regexp = to_regexp
23
- end
24
-
25
- def to_regexp
26
- @to_regexp ||= @pattern.to_regexp
23
+ @pattern = Mustermann::Grape.new(@path, **pattern_options(options))
24
+ @to_regexp = @pattern.to_regexp
27
25
  end
28
26
 
29
27
  private
30
28
 
31
- def pattern_options
29
+ def pattern_options(options)
30
+ capture = extract_capture(**options)
32
31
  options = DEFAULT_PATTERN_OPTIONS.dup
33
32
  options[:capture] = capture if capture.present?
34
33
  options
@@ -36,27 +35,32 @@ module Grape
36
35
 
37
36
  def build_path(pattern, anchor: false, suffix: nil, **_options)
38
37
  unless anchor || pattern.end_with?('*path')
38
+ pattern = +pattern
39
39
  pattern << '/' unless pattern.end_with?('/')
40
40
  pattern << '*path'
41
41
  end
42
42
 
43
- pattern = pattern.split('/').tap do |parts|
43
+ pattern = -pattern.split('/').tap do |parts|
44
44
  parts[parts.length - 1] = '?' + parts.last
45
45
  end.join('/') if pattern.end_with?('*path')
46
46
 
47
- pattern + suffix.to_s
47
+ PatternCache[[pattern, suffix]]
48
48
  end
49
49
 
50
50
  def extract_capture(requirements: {}, **options)
51
51
  requirements = {}.merge(requirements)
52
- supported_capture.each_with_object(requirements) do |field, capture|
52
+ DEFAULT_SUPPORTED_CAPTURE.each_with_object(requirements) do |field, capture|
53
53
  option = Array(options[field])
54
54
  capture[field] = option.map(&:to_s) if option.present?
55
55
  end
56
56
  end
57
57
 
58
- def supported_capture
59
- DEFAULT_SUPPORTED_CAPTURE
58
+ class PatternCache < Grape::Util::Cache
59
+ def initialize
60
+ @cache = Hash.new do |h, (pattern, suffix)|
61
+ h[[pattern, suffix]] = -"#{pattern}#{suffix}"
62
+ end
63
+ end
60
64
  end
61
65
  end
62
66
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/router/pattern'
2
4
  require 'grape/router/attribute_translator'
3
5
  require 'forwardable'
@@ -6,8 +8,8 @@ require 'pathname'
6
8
  module Grape
7
9
  class Router
8
10
  class Route
9
- ROUTE_ATTRIBUTE_REGEXP = /route_([_a-zA-Z]\w*)/
10
- SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in `.+?')?$/
11
+ ROUTE_ATTRIBUTE_REGEXP = /route_([_a-zA-Z]\w*)/.freeze
12
+ SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in `.+?')?$/.freeze
11
13
  FIXED_NAMED_CAPTURES = %w[format version].freeze
12
14
 
13
15
  attr_accessor :pattern, :translator, :app, :index, :regexp, :options
@@ -62,10 +64,13 @@ module Grape
62
64
  end
63
65
 
64
66
  def initialize(method, pattern, **options)
67
+ method_s = method.to_s
68
+ method_upcase = Grape::Http::Headers.find_supported_method(method_s) || method_s.upcase
69
+
65
70
  @suffix = options[:suffix]
66
- @options = options.merge(method: method.to_s.upcase)
71
+ @options = options.merge(method: method_upcase)
67
72
  @pattern = Pattern.new(pattern, **options)
68
- @translator = AttributeTranslator.new(**options, request_method: method.to_s.upcase)
73
+ @translator = AttributeTranslator.new(**options, request_method: method_upcase)
69
74
  end
70
75
 
71
76
  def exec(env)
data/lib/grape/router.rb CHANGED
@@ -1,22 +1,36 @@
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
10
  class Any < AttributeTranslator
8
- def initialize(pattern, **attributes)
11
+ attr_reader :pattern, :regexp, :index
12
+ def initialize(pattern, regexp, index, **attributes)
9
13
  @pattern = pattern
14
+ @regexp = regexp
15
+ @index = index
10
16
  super(attributes)
11
17
  end
12
18
  end
13
19
 
20
+ class NormalizePathCache < Grape::Util::Cache
21
+ def initialize
22
+ @cache = Hash.new do |h, path|
23
+ normalized_path = +"/#{path}"
24
+ normalized_path.squeeze!('/')
25
+ normalized_path.sub!(%r{/+\Z}, '')
26
+ normalized_path = '/' if normalized_path.empty?
27
+ h[path] = -normalized_path
28
+ end
29
+ end
30
+ end
31
+
14
32
  def self.normalize_path(path)
15
- path = "/#{path}"
16
- path.squeeze!('/')
17
- path.sub!(%r{/+\Z}, '')
18
- path = '/' if path == ''
19
- path
33
+ NormalizePathCache[path]
20
34
  end
21
35
 
22
36
  def self.supported_methods
@@ -36,7 +50,7 @@ module Grape
36
50
  routes = map[method]
37
51
  @optimized_map[method] = routes.map.with_index do |route, index|
38
52
  route.index = index
39
- route.regexp = /(?<_#{index}>#{route.pattern.to_regexp})/
53
+ route.regexp = Regexp.new("(?<_#{index}>#{route.pattern.to_regexp})")
40
54
  end
41
55
  @optimized_map[method] = Regexp.union(@optimized_map[method])
42
56
  end
@@ -44,12 +58,12 @@ module Grape
44
58
  end
45
59
 
46
60
  def append(route)
47
- map[route.request_method.to_s.upcase] << route
61
+ map[route.request_method] << route
48
62
  end
49
63
 
50
64
  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)
65
+ regexp = Regexp.new("(?<_#{@neutral_map.length}>)#{pattern.to_regexp}")
66
+ @neutral_map << Any.new(pattern, regexp, @neutral_map.length, **options)
53
67
  end
54
68
 
55
69
  def call(env)
@@ -101,7 +115,7 @@ module Grape
101
115
  env,
102
116
  neighbor.allow_header,
103
117
  neighbor.endpoint
104
- ) if neighbor && method == 'OPTIONS' && !cascade
118
+ ) if neighbor && method == Grape::Http::Headers::OPTIONS && !cascade
105
119
 
106
120
  route = match?(input, '*')
107
121
  return neighbor.endpoint.call(env) if neighbor && cascade && route
@@ -155,7 +169,7 @@ module Grape
155
169
  end
156
170
 
157
171
  def call_with_allow_headers(env, methods, endpoint)
158
- env[Grape::Env::GRAPE_ALLOWED_METHODS] = methods
172
+ env[Grape::Env::GRAPE_ALLOWED_METHODS] = methods.join(', ').freeze
159
173
  endpoint.call(env)
160
174
  end
161
175
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module ServeFile
3
5
  CHUNK_SIZE = 16_384
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module ServeFile
3
5
  # A simple class used to identify responses which represent files and do not
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module ServeFile
3
5
  # Response should respond to to_path method
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Util
3
5
  # Base for classes which need to operate with own values kept
@@ -29,6 +31,10 @@ module Grape
29
31
  combined.uniq!
30
32
  combined
31
33
  end
34
+
35
+ def key?(name)
36
+ inherited_values.key?(name) || new_values.key?(name)
37
+ end
32
38
  end
33
39
  end
34
40
  end
@@ -0,0 +1,20 @@
1
+ # frozen_String_literal: true
2
+
3
+ require 'singleton'
4
+ require 'forwardable'
5
+
6
+ module Grape
7
+ module Util
8
+ class Cache
9
+ include Singleton
10
+
11
+ attr_reader :cache
12
+
13
+ class << self
14
+ extend Forwardable
15
+ def_delegators :cache, :[]
16
+ def_delegators :instance, :cache
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Util
3
5
  class EndpointConfiguration < LazyValueHash
@@ -1,23 +1,25 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Env
3
- API_VERSION = 'api.version'.freeze
4
- API_ENDPOINT = 'api.endpoint'.freeze
5
- API_REQUEST_INPUT = 'api.request.input'.freeze
6
- API_REQUEST_BODY = 'api.request.body'.freeze
7
- API_TYPE = 'api.type'.freeze
8
- API_SUBTYPE = 'api.subtype'.freeze
9
- API_VENDOR = 'api.vendor'.freeze
10
- API_FORMAT = 'api.format'.freeze
5
+ API_VERSION = 'api.version'
6
+ API_ENDPOINT = 'api.endpoint'
7
+ API_REQUEST_INPUT = 'api.request.input'
8
+ API_REQUEST_BODY = 'api.request.body'
9
+ API_TYPE = 'api.type'
10
+ API_SUBTYPE = 'api.subtype'
11
+ API_VENDOR = 'api.vendor'
12
+ API_FORMAT = 'api.format'
11
13
 
12
- RACK_INPUT = 'rack.input'.freeze
13
- RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'.freeze
14
- RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'.freeze
15
- RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'.freeze
14
+ RACK_INPUT = 'rack.input'
15
+ RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'
16
+ RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'
17
+ RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'
16
18
 
17
- GRAPE_REQUEST = 'grape.request'.freeze
18
- GRAPE_REQUEST_HEADERS = 'grape.request.headers'.freeze
19
- GRAPE_REQUEST_PARAMS = 'grape.request.params'.freeze
20
- GRAPE_ROUTING_ARGS = 'grape.routing_args'.freeze
21
- GRAPE_ALLOWED_METHODS = 'grape.allowed_methods'.freeze
19
+ GRAPE_REQUEST = 'grape.request'
20
+ GRAPE_REQUEST_HEADERS = 'grape.request.headers'
21
+ GRAPE_REQUEST_PARAMS = 'grape.request.params'
22
+ GRAPE_ROUTING_ARGS = 'grape.routing_args'
23
+ GRAPE_ALLOWED_METHODS = 'grape.allowed_methods'
22
24
  end
23
25
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Util
3
5
  # A branchable, inheritable settings object which can store both stackable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'base_inheritable'
2
4
 
3
5
  module Grape
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  if Object.const_defined? :MultiJson
3
5
  Json = ::MultiJson
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Util
3
5
  class LazyBlock
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Based on https://github.com/HornsAndHooves/lazy_object
4
+
5
+ module Grape
6
+ module Util
7
+ class LazyObject < BasicObject
8
+ attr_reader :callable
9
+
10
+ def initialize(&callable)
11
+ @callable = callable
12
+ end
13
+
14
+ def __target_object__
15
+ @__target_object__ ||= callable.call
16
+ end
17
+
18
+ def ==(other)
19
+ __target_object__ == other
20
+ end
21
+
22
+ def !=(other)
23
+ __target_object__ != other
24
+ end
25
+
26
+ def !
27
+ !__target_object__
28
+ end
29
+
30
+ def method_missing(method_name, *args, &block)
31
+ if __target_object__.respond_to?(method_name)
32
+ __target_object__.send(method_name, *args, &block)
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def respond_to_missing?(method_name, include_priv = false)
39
+ __target_object__.respond_to?(method_name, include_priv)
40
+ end
41
+ end
42
+ end
43
+ end