grape 1.1.0 → 1.5.3

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 (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +278 -44
  3. data/LICENSE +1 -1
  4. data/README.md +514 -69
  5. data/UPGRADING.md +424 -17
  6. data/grape.gemspec +13 -2
  7. data/lib/grape.rb +104 -71
  8. data/lib/grape/api.rb +138 -175
  9. data/lib/grape/api/helpers.rb +2 -0
  10. data/lib/grape/api/instance.rb +283 -0
  11. data/lib/grape/config.rb +34 -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 +22 -0
  16. data/lib/grape/dsl/configuration.rb +2 -0
  17. data/lib/grape/dsl/desc.rb +41 -7
  18. data/lib/grape/dsl/headers.rb +2 -0
  19. data/lib/grape/dsl/helpers.rb +5 -2
  20. data/lib/grape/dsl/inside_route.rb +92 -49
  21. data/lib/grape/dsl/logger.rb +2 -0
  22. data/lib/grape/dsl/middleware.rb +9 -0
  23. data/lib/grape/dsl/parameters.rb +25 -14
  24. data/lib/grape/dsl/request_response.rb +4 -2
  25. data/lib/grape/dsl/routing.rb +17 -10
  26. data/lib/grape/dsl/settings.rb +7 -1
  27. data/lib/grape/dsl/validations.rb +24 -4
  28. data/lib/grape/eager_load.rb +20 -0
  29. data/lib/grape/endpoint.rb +59 -35
  30. data/lib/grape/error_formatter.rb +4 -2
  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 +20 -14
  36. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  37. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  38. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  39. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  40. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  41. data/lib/grape/exceptions/invalid_response.rb +11 -0
  42. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  43. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  44. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  45. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  46. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  47. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  48. data/lib/grape/exceptions/missing_option.rb +2 -0
  49. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  50. data/lib/grape/exceptions/unknown_options.rb +2 -0
  51. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  52. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  53. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  54. data/lib/grape/exceptions/validation.rb +4 -2
  55. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  56. data/lib/grape/exceptions/validation_errors.rb +16 -13
  57. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  58. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  59. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  60. data/lib/grape/extensions/hash.rb +2 -0
  61. data/lib/grape/extensions/hashie/mash.rb +2 -0
  62. data/lib/grape/formatter.rb +5 -3
  63. data/lib/grape/formatter/json.rb +2 -0
  64. data/lib/grape/formatter/serializable_hash.rb +2 -0
  65. data/lib/grape/formatter/txt.rb +2 -0
  66. data/lib/grape/formatter/xml.rb +2 -0
  67. data/lib/grape/http/headers.rb +50 -18
  68. data/lib/grape/locale/en.yml +3 -1
  69. data/lib/grape/middleware/auth/base.rb +7 -7
  70. data/lib/grape/middleware/auth/dsl.rb +2 -0
  71. data/lib/grape/middleware/auth/strategies.rb +2 -0
  72. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  73. data/lib/grape/middleware/base.rb +10 -7
  74. data/lib/grape/middleware/error.rb +21 -16
  75. data/lib/grape/middleware/filter.rb +2 -0
  76. data/lib/grape/middleware/formatter.rb +8 -6
  77. data/lib/grape/middleware/globals.rb +2 -0
  78. data/lib/grape/middleware/helpers.rb +12 -0
  79. data/lib/grape/middleware/stack.rb +13 -3
  80. data/lib/grape/middleware/versioner.rb +2 -0
  81. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  82. data/lib/grape/middleware/versioner/header.rb +10 -8
  83. data/lib/grape/middleware/versioner/param.rb +3 -1
  84. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
  85. data/lib/grape/middleware/versioner/path.rb +3 -1
  86. data/lib/grape/namespace.rb +14 -2
  87. data/lib/grape/parser.rb +4 -2
  88. data/lib/grape/parser/json.rb +3 -1
  89. data/lib/grape/parser/xml.rb +3 -1
  90. data/lib/grape/path.rb +15 -3
  91. data/lib/grape/presenters/presenter.rb +2 -0
  92. data/lib/grape/request.rb +19 -10
  93. data/lib/grape/router.rb +30 -29
  94. data/lib/grape/router/attribute_translator.rb +41 -8
  95. data/lib/grape/router/pattern.rb +20 -16
  96. data/lib/grape/router/route.rb +14 -28
  97. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  98. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  99. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  100. data/lib/grape/util/base_inheritable.rb +43 -0
  101. data/lib/grape/util/cache.rb +20 -0
  102. data/lib/grape/util/endpoint_configuration.rb +8 -0
  103. data/lib/grape/util/env.rb +19 -17
  104. data/lib/grape/util/inheritable_setting.rb +2 -0
  105. data/lib/grape/util/inheritable_values.rb +7 -25
  106. data/lib/grape/util/json.rb +2 -0
  107. data/lib/grape/util/lazy_block.rb +27 -0
  108. data/lib/grape/util/lazy_object.rb +43 -0
  109. data/lib/grape/util/lazy_value.rb +98 -0
  110. data/lib/grape/util/registrable.rb +2 -0
  111. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  112. data/lib/grape/util/stackable_values.rb +21 -34
  113. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  114. data/lib/grape/util/xml.rb +2 -0
  115. data/lib/grape/validations.rb +2 -0
  116. data/lib/grape/validations/attributes_iterator.rb +16 -6
  117. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  118. data/lib/grape/validations/params_scope.rb +51 -30
  119. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  120. data/lib/grape/validations/types.rb +13 -38
  121. data/lib/grape/validations/types/array_coercer.rb +65 -0
  122. data/lib/grape/validations/types/build_coercer.rb +47 -49
  123. data/lib/grape/validations/types/custom_type_coercer.rb +29 -51
  124. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  125. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  126. data/lib/grape/validations/types/file.rb +22 -18
  127. data/lib/grape/validations/types/invalid_value.rb +24 -0
  128. data/lib/grape/validations/types/json.rb +46 -39
  129. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  130. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  131. data/lib/grape/validations/types/set_coercer.rb +40 -0
  132. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  133. data/lib/grape/validations/validator_factory.rb +8 -11
  134. data/lib/grape/validations/validators/all_or_none.rb +8 -13
  135. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  136. data/lib/grape/validations/validators/as.rb +5 -4
  137. data/lib/grape/validations/validators/at_least_one_of.rb +7 -13
  138. data/lib/grape/validations/validators/base.rb +20 -16
  139. data/lib/grape/validations/validators/coerce.rb +46 -29
  140. data/lib/grape/validations/validators/default.rb +6 -6
  141. data/lib/grape/validations/validators/exactly_one_of.rb +10 -23
  142. data/lib/grape/validations/validators/except_values.rb +4 -2
  143. data/lib/grape/validations/validators/multiple_params_base.rb +17 -10
  144. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -18
  145. data/lib/grape/validations/validators/presence.rb +3 -1
  146. data/lib/grape/validations/validators/regexp.rb +4 -2
  147. data/lib/grape/validations/validators/same_as.rb +26 -0
  148. data/lib/grape/validations/validators/values.rb +18 -6
  149. data/lib/grape/version.rb +3 -1
  150. data/spec/grape/api/custom_validations_spec.rb +5 -3
  151. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  152. data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
  153. data/spec/grape/api/inherited_helpers_spec.rb +2 -0
  154. data/spec/grape/api/instance_spec.rb +104 -0
  155. data/spec/grape/api/invalid_format_spec.rb +2 -0
  156. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  157. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  158. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  159. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  160. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  161. data/spec/grape/api/recognize_path_spec.rb +2 -0
  162. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  163. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  164. data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
  165. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
  166. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  167. data/spec/grape/api_remount_spec.rb +473 -0
  168. data/spec/grape/api_spec.rb +565 -12
  169. data/spec/grape/config_spec.rb +19 -0
  170. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  171. data/spec/grape/dsl/configuration_spec.rb +2 -0
  172. data/spec/grape/dsl/desc_spec.rb +42 -16
  173. data/spec/grape/dsl/headers_spec.rb +2 -0
  174. data/spec/grape/dsl/helpers_spec.rb +4 -2
  175. data/spec/grape/dsl/inside_route_spec.rb +184 -33
  176. data/spec/grape/dsl/logger_spec.rb +2 -0
  177. data/spec/grape/dsl/middleware_spec.rb +10 -0
  178. data/spec/grape/dsl/parameters_spec.rb +2 -0
  179. data/spec/grape/dsl/request_response_spec.rb +2 -0
  180. data/spec/grape/dsl/routing_spec.rb +12 -0
  181. data/spec/grape/dsl/settings_spec.rb +2 -0
  182. data/spec/grape/dsl/validations_spec.rb +2 -0
  183. data/spec/grape/endpoint/declared_spec.rb +601 -0
  184. data/spec/grape/endpoint_spec.rb +53 -523
  185. data/spec/grape/entity_spec.rb +9 -1
  186. data/spec/grape/exceptions/base_spec.rb +67 -0
  187. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  188. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  189. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
  190. data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
  191. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  192. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  193. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  194. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  195. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  196. data/spec/grape/exceptions/validation_errors_spec.rb +8 -4
  197. data/spec/grape/exceptions/validation_spec.rb +3 -1
  198. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  199. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  200. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  201. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  202. data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
  203. data/spec/grape/integration/rack_spec.rb +25 -7
  204. data/spec/grape/loading_spec.rb +2 -0
  205. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  206. data/spec/grape/middleware/auth/dsl_spec.rb +5 -3
  207. data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
  208. data/spec/grape/middleware/base_spec.rb +10 -0
  209. data/spec/grape/middleware/error_spec.rb +3 -1
  210. data/spec/grape/middleware/exception_spec.rb +4 -2
  211. data/spec/grape/middleware/formatter_spec.rb +33 -16
  212. data/spec/grape/middleware/globals_spec.rb +2 -0
  213. data/spec/grape/middleware/stack_spec.rb +12 -0
  214. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  215. data/spec/grape/middleware/versioner/header_spec.rb +9 -1
  216. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  217. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  218. data/spec/grape/middleware/versioner_spec.rb +2 -0
  219. data/spec/grape/named_api_spec.rb +21 -0
  220. data/spec/grape/parser_spec.rb +7 -5
  221. data/spec/grape/path_spec.rb +6 -4
  222. data/spec/grape/presenters/presenter_spec.rb +2 -0
  223. data/spec/grape/request_spec.rb +26 -0
  224. data/spec/grape/util/inheritable_setting_spec.rb +2 -0
  225. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  226. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  227. data/spec/grape/util/stackable_values_spec.rb +3 -1
  228. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  229. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  230. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  231. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
  232. data/spec/grape/validations/params_scope_spec.rb +213 -9
  233. data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
  234. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  235. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  236. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  237. data/spec/grape/validations/types_spec.rb +9 -36
  238. data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
  239. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  240. data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
  241. data/spec/grape/validations/validators/coerce_spec.rb +476 -135
  242. data/spec/grape/validations/validators/default_spec.rb +172 -0
  243. data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
  244. data/spec/grape/validations/validators/except_values_spec.rb +4 -1
  245. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
  246. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  247. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  248. data/spec/grape/validations/validators/same_as_spec.rb +65 -0
  249. data/spec/grape/validations/validators/values_spec.rb +30 -5
  250. data/spec/grape/validations_spec.rb +388 -50
  251. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  252. data/spec/integration/multi_json/json_spec.rb +2 -0
  253. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  254. data/spec/shared/versioning_examples.rb +22 -20
  255. data/spec/spec_helper.rb +12 -1
  256. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  257. data/spec/support/chunks.rb +14 -0
  258. data/spec/support/content_type_helpers.rb +2 -0
  259. data/spec/support/eager_load.rb +19 -0
  260. data/spec/support/endpoint_faker.rb +2 -0
  261. data/spec/support/file_streamer.rb +2 -0
  262. data/spec/support/integer_helpers.rb +2 -0
  263. data/spec/support/versioned_helpers.rb +8 -8
  264. metadata +86 -48
  265. data/Appraisals +0 -32
  266. data/Dangerfile +0 -2
  267. data/Gemfile +0 -33
  268. data/Gemfile.lock +0 -231
  269. data/Guardfile +0 -10
  270. data/RELEASING.md +0 -111
  271. data/Rakefile +0 -25
  272. data/benchmark/simple.rb +0 -27
  273. data/benchmark/simple_with_type_coercer.rb +0 -22
  274. data/gemfiles/multi_json.gemfile +0 -35
  275. data/gemfiles/multi_xml.gemfile +0 -35
  276. data/gemfiles/rack_1.5.2.gemfile +0 -35
  277. data/gemfiles/rack_edge.gemfile +0 -35
  278. data/gemfiles/rails_3.gemfile +0 -36
  279. data/gemfiles/rails_4.gemfile +0 -35
  280. data/gemfiles/rails_5.gemfile +0 -35
  281. data/gemfiles/rails_edge.gemfile +0 -35
  282. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  283. data/lib/grape/util/content_types.rb +0 -26
  284. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
  285. data/pkg/grape-0.17.0.gem +0 -0
  286. data/pkg/grape-0.19.0.gem +0 -0
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,8 +13,8 @@ module Grape
11
13
  }
12
14
  end
13
15
 
14
- def parsers(options)
15
- builtin_parsers.merge(default_elements).merge(options[:parsers] || {})
16
+ def parsers(**options)
17
+ builtin_parsers.merge(default_elements).merge!(options[:parsers] || {})
16
18
  end
17
19
 
18
20
  def parser_for(api_format, **options)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Parser
3
5
  module Json
@@ -6,7 +8,7 @@ module Grape
6
8
  ::Grape::Json.load(object)
7
9
  rescue ::Grape::Json::ParseError
8
10
  # handle JSON parsing errors via the rescue handlers or provide error message
9
- raise Grape::Exceptions::InvalidMessageBody, 'application/json'
11
+ raise Grape::Exceptions::InvalidMessageBody.new('application/json')
10
12
  end
11
13
  end
12
14
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Parser
3
5
  module Xml
@@ -6,7 +8,7 @@ module Grape
6
8
  ::Grape::Xml.parse(object)
7
9
  rescue ::Grape::Xml::ParseError
8
10
  # handle XML parsing errors via the rescue handlers or provide error message
9
- raise Grape::Exceptions::InvalidMessageBody, 'application/xml'
11
+ raise Grape::Exceptions::InvalidMessageBody.new('application/xml')
10
12
  end
11
13
  end
12
14
  end
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
@@ -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
data/lib/grape/request.rb CHANGED
@@ -1,16 +1,22 @@
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
 
7
- def initialize(env, options = {})
8
- extend options[:build_params_with] || Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
11
+ def initialize(env, **options)
12
+ extend options[:build_params_with] || Grape.config.param_builder
9
13
  super(env)
10
14
  end
11
15
 
12
16
  def params
13
17
  @params ||= build_params
18
+ rescue EOFError
19
+ raise Grape::Exceptions::EmptyMessageBody.new(content_type)
14
20
  end
15
21
 
16
22
  def headers
@@ -28,14 +34,17 @@ module Grape
28
34
  end
29
35
 
30
36
  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
37
+ Grape::Util::LazyObject.new do
38
+ env.each_pair.with_object({}) do |(k, v), headers|
39
+ next unless k.to_s.start_with? HTTP_PREFIX
40
+ transformed_header = Grape::Http::Headers::HTTP_HEADERS[k] || transform_header(k)
41
+ headers[transformed_header] = v
42
+ end
37
43
  end
38
- headers
44
+ end
45
+
46
+ def transform_header(header)
47
+ -header[5..-1].split('_').each(&:capitalize!).join('-')
39
48
  end
40
49
  end
41
50
  end
data/lib/grape/router.rb CHANGED
@@ -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, 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,30 +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
5
- def initialize(attributes = {})
7
+ attr_reader :attributes
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
+
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)
41
+ if setter?(method_name[-1])
42
+ attributes[method_name[0..-1]] = *args
43
+ else
44
+ attributes[method_name]
18
45
  end
19
46
  end
20
47
 
21
48
  def respond_to_missing?(method_name, _include_private = false)
22
- if method_name[-1] == '='
49
+ if setter?(method_name[-1])
23
50
  true
24
51
  else
25
52
  @attributes.key?(method_name)
26
53
  end
27
54
  end
55
+
56
+ private
57
+
58
+ def setter?(method_name)
59
+ method_name[-1] == '='
60
+ end
28
61
  end
29
62
  end
30
63
  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,16 +8,17 @@ 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
- attr_accessor :pattern, :translator, :app, :index, :regexp, :options
15
+ attr_accessor :pattern, :translator, :app, :index, :options
14
16
 
15
17
  alias attributes translator
16
18
 
17
19
  extend Forwardable
18
20
  def_delegators :pattern, :path, :origin
21
+ delegate Grape::Router::AttributeTranslator::ROUTE_ATTRIBUTES => :attributes
19
22
 
20
23
  def method_missing(method_id, *arguments)
21
24
  match = ROUTE_ATTRIBUTE_REGEXP.match(method_id.to_s)
@@ -29,26 +32,7 @@ module Grape
29
32
  end
30
33
 
31
34
  def respond_to_missing?(method_id, _)
32
- ROUTE_ATTRIBUTE_REGEXP.match(method_id.to_s)
33
- end
34
-
35
- %i[
36
- prefix
37
- version
38
- settings
39
- format
40
- description
41
- http_codes
42
- headers
43
- entity
44
- details
45
- requirements
46
- request_method
47
- namespace
48
- ].each do |method_name|
49
- define_method method_name do
50
- attributes.public_send method_name
51
- end
35
+ ROUTE_ATTRIBUTE_REGEXP.match?(method_id.to_s)
52
36
  end
53
37
 
54
38
  def route_method
@@ -62,10 +46,12 @@ module Grape
62
46
  end
63
47
 
64
48
  def initialize(method, pattern, **options)
65
- @suffix = options[:suffix]
66
- @options = options.merge(method: method.to_s.upcase)
49
+ method_s = method.to_s
50
+ method_upcase = Grape::Http::Headers.find_supported_method(method_s) || method_s.upcase
51
+
52
+ @options = options.merge(method: method_upcase)
67
53
  @pattern = Pattern.new(pattern, **options)
68
- @translator = AttributeTranslator.new(**options, request_method: method.to_s.upcase)
54
+ @translator = AttributeTranslator.new(**options, request_method: method_upcase)
69
55
  end
70
56
 
71
57
  def exec(env)
@@ -98,9 +84,9 @@ module Grape
98
84
  path, line = *location.scan(SOURCE_LOCATION_REGEXP).first
99
85
  path = File.realpath(path) if Pathname.new(path).relative?
100
86
  expected ||= name
101
- warn <<-EOS
87
+ warn <<-WARNING
102
88
  #{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.
103
- EOS
89
+ WARNING
104
90
  end
105
91
  end
106
92
  end