grape 1.8.0 → 3.3.5

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 (312) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +391 -1
  3. data/CONTRIBUTING.md +3 -11
  4. data/README.md +648 -552
  5. data/UPGRADING.md +803 -7
  6. data/grape.gemspec +11 -14
  7. data/lib/grape/api/instance.rb +68 -158
  8. data/lib/grape/api.rb +71 -107
  9. data/lib/grape/content_types.rb +23 -11
  10. data/lib/grape/cookies.rb +28 -25
  11. data/lib/grape/declared_params_handler.rb +116 -0
  12. data/lib/grape/dry_types.rb +48 -6
  13. data/lib/grape/dsl/callbacks.rb +14 -58
  14. data/lib/grape/dsl/declared.rb +35 -0
  15. data/lib/grape/dsl/desc.rb +15 -63
  16. data/lib/grape/dsl/entity.rb +88 -0
  17. data/lib/grape/dsl/headers.rb +2 -2
  18. data/lib/grape/dsl/helpers.rb +83 -64
  19. data/lib/grape/dsl/inside_route.rb +56 -295
  20. data/lib/grape/dsl/logger.rb +4 -9
  21. data/lib/grape/dsl/middleware.rb +22 -40
  22. data/lib/grape/dsl/parameters.rb +50 -82
  23. data/lib/grape/dsl/request_response.rb +144 -146
  24. data/lib/grape/dsl/rescue_options.rb +24 -0
  25. data/lib/grape/dsl/routing.rb +261 -186
  26. data/lib/grape/dsl/settings.rb +29 -135
  27. data/lib/grape/dsl/validations.rb +39 -32
  28. data/lib/grape/dsl/version_options.rb +23 -0
  29. data/lib/grape/endpoint/options.rb +19 -0
  30. data/lib/grape/endpoint.rb +277 -250
  31. data/lib/grape/{util/env.rb → env.rb} +1 -8
  32. data/lib/grape/error_formatter/base.rb +56 -21
  33. data/lib/grape/error_formatter/json.rb +13 -15
  34. data/lib/grape/error_formatter/serializable_hash.rb +7 -0
  35. data/lib/grape/error_formatter/txt.rb +12 -18
  36. data/lib/grape/error_formatter/xml.rb +3 -13
  37. data/lib/grape/error_formatter.rb +5 -25
  38. data/lib/grape/exceptions/base.rb +22 -58
  39. data/lib/grape/exceptions/error_response.rb +45 -0
  40. data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
  41. data/lib/grape/exceptions/internal_server_error.rb +16 -0
  42. data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
  43. data/lib/grape/exceptions/invalid_formatter.rb +1 -1
  44. data/lib/grape/exceptions/invalid_message_body.rb +1 -1
  45. data/lib/grape/exceptions/invalid_version_header.rb +1 -1
  46. data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
  47. data/lib/grape/exceptions/method_not_allowed.rb +1 -1
  48. data/lib/grape/exceptions/missing_group_type.rb +0 -2
  49. data/lib/grape/exceptions/missing_mime_type.rb +1 -1
  50. data/lib/grape/exceptions/request_error.rb +11 -0
  51. data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
  52. data/lib/grape/exceptions/unknown_parameter.rb +1 -1
  53. data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
  54. data/lib/grape/exceptions/unknown_validator.rb +1 -1
  55. data/lib/grape/exceptions/unsupported_group_type.rb +0 -2
  56. data/lib/grape/exceptions/validation.rb +25 -9
  57. data/lib/grape/exceptions/validation_array_errors.rb +5 -0
  58. data/lib/grape/exceptions/validation_errors.rb +22 -26
  59. data/lib/grape/formatter/base.rb +16 -0
  60. data/lib/grape/formatter/json.rb +4 -6
  61. data/lib/grape/formatter/serializable_hash.rb +6 -10
  62. data/lib/grape/formatter/txt.rb +3 -5
  63. data/lib/grape/formatter/xml.rb +4 -6
  64. data/lib/grape/formatter.rb +7 -25
  65. data/lib/grape/json.rb +46 -0
  66. data/lib/grape/locale/en.yml +43 -42
  67. data/lib/grape/middleware/auth/base.rb +11 -33
  68. data/lib/grape/middleware/auth/dsl.rb +37 -31
  69. data/lib/grape/middleware/auth/strategies.rb +1 -2
  70. data/lib/grape/middleware/base.rb +56 -33
  71. data/lib/grape/middleware/deprecated_options_hash_access.rb +19 -0
  72. data/lib/grape/middleware/error.rb +183 -91
  73. data/lib/grape/middleware/formatter.rb +94 -105
  74. data/lib/grape/middleware/precomputed_content_types.rb +46 -0
  75. data/lib/grape/middleware/stack.rb +27 -38
  76. data/lib/grape/middleware/versioner/accept_version_header.rb +6 -33
  77. data/lib/grape/middleware/versioner/base.rb +72 -0
  78. data/lib/grape/middleware/versioner/header.rb +44 -129
  79. data/lib/grape/middleware/versioner/param.rb +4 -25
  80. data/lib/grape/middleware/versioner/path.rb +13 -32
  81. data/lib/grape/middleware/versioner.rb +7 -14
  82. data/lib/grape/mountable.rb +22 -0
  83. data/lib/grape/namespace.rb +18 -12
  84. data/lib/grape/params_builder/base.rb +20 -0
  85. data/lib/grape/params_builder/hash.rb +11 -0
  86. data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
  87. data/lib/grape/params_builder/hashie_mash.rb +11 -0
  88. data/lib/grape/params_builder.rb +15 -0
  89. data/lib/grape/parser/base.rb +16 -0
  90. data/lib/grape/parser/json.rb +6 -8
  91. data/lib/grape/parser/xml.rb +6 -8
  92. data/lib/grape/parser.rb +5 -23
  93. data/lib/grape/path.rb +38 -63
  94. data/lib/grape/railtie.rb +9 -0
  95. data/lib/grape/request.rb +168 -26
  96. data/lib/grape/router/base_route.rb +48 -0
  97. data/lib/grape/router/greedy_route.rb +26 -0
  98. data/lib/grape/router/mustermann_pattern.rb +44 -0
  99. data/lib/grape/router/pattern.rb +30 -34
  100. data/lib/grape/router/route.rb +28 -61
  101. data/lib/grape/router.rb +71 -86
  102. data/lib/grape/serve_stream/file_body.rb +1 -0
  103. data/lib/grape/serve_stream/sendfile_response.rb +3 -5
  104. data/lib/grape/serve_stream/stream_response.rb +1 -0
  105. data/lib/grape/testing.rb +33 -0
  106. data/lib/grape/util/api_description.rb +58 -0
  107. data/lib/grape/util/base_inheritable.rb +14 -14
  108. data/lib/grape/util/cache.rb +1 -3
  109. data/lib/grape/util/deep_freeze.rb +35 -0
  110. data/lib/grape/util/endpoint_configuration.rb +1 -1
  111. data/lib/grape/util/header.rb +13 -0
  112. data/lib/grape/util/inheritable_setting.rb +51 -27
  113. data/lib/grape/util/inheritable_values.rb +7 -5
  114. data/lib/grape/util/lazy/base.rb +16 -0
  115. data/lib/grape/util/lazy/block.rb +22 -0
  116. data/lib/grape/util/lazy/value.rb +31 -0
  117. data/lib/grape/util/lazy/value_array.rb +21 -0
  118. data/lib/grape/util/lazy/value_enumerable.rb +31 -0
  119. data/lib/grape/util/lazy/value_hash.rb +21 -0
  120. data/lib/grape/util/media_type.rb +67 -0
  121. data/lib/grape/util/path_normalizer.rb +34 -0
  122. data/lib/grape/util/registry.rb +27 -0
  123. data/lib/grape/util/reverse_stackable_values.rb +1 -6
  124. data/lib/grape/util/stackable_values.rb +12 -14
  125. data/lib/grape/util/translation.rb +42 -0
  126. data/lib/grape/validations/attributes_iterator.rb +43 -27
  127. data/lib/grape/validations/coerce_options.rb +21 -0
  128. data/lib/grape/validations/contract_scope.rb +28 -0
  129. data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
  130. data/lib/grape/validations/oneof_collector.rb +39 -0
  131. data/lib/grape/validations/param_scope_tracker.rb +62 -0
  132. data/lib/grape/validations/params_documentation.rb +52 -0
  133. data/lib/grape/validations/params_scope.rb +180 -303
  134. data/lib/grape/validations/shared_options.rb +19 -0
  135. data/lib/grape/validations/single_attribute_iterator.rb +2 -2
  136. data/lib/grape/validations/types/array_coercer.rb +4 -7
  137. data/lib/grape/validations/types/custom_type_coercer.rb +41 -85
  138. data/lib/grape/validations/types/custom_type_collection_coercer.rb +1 -1
  139. data/lib/grape/validations/types/dry_type_coercer.rb +15 -28
  140. data/lib/grape/validations/types/json.rb +0 -2
  141. data/lib/grape/validations/types/primitive_coercer.rb +11 -35
  142. data/lib/grape/validations/types/set_coercer.rb +1 -4
  143. data/lib/grape/validations/types/variant_collection_coercer.rb +11 -0
  144. data/lib/grape/validations/types.rb +29 -54
  145. data/lib/grape/validations/validations_spec.rb +164 -0
  146. data/lib/grape/validations/validators/all_or_none_of_validator.rb +6 -3
  147. data/lib/grape/validations/validators/allow_blank_validator.rb +10 -5
  148. data/lib/grape/validations/validators/at_least_one_of_validator.rb +5 -2
  149. data/lib/grape/validations/validators/base.rb +120 -36
  150. data/lib/grape/validations/validators/coerce_validator.rb +18 -36
  151. data/lib/grape/validations/validators/contract_scope_validator.rb +45 -0
  152. data/lib/grape/validations/validators/default_validator.rb +13 -16
  153. data/lib/grape/validations/validators/exactly_one_of_validator.rb +10 -3
  154. data/lib/grape/validations/validators/except_values_validator.rb +15 -5
  155. data/lib/grape/validations/validators/length_validator.rb +48 -0
  156. data/lib/grape/validations/validators/multiple_params_base.rb +12 -9
  157. data/lib/grape/validations/validators/{mutual_exclusion_validator.rb → mutually_exclusive_validator.rb} +4 -2
  158. data/lib/grape/validations/validators/oneof_validator.rb +49 -0
  159. data/lib/grape/validations/validators/presence_validator.rb +4 -2
  160. data/lib/grape/validations/validators/regexp_validator.rb +11 -3
  161. data/lib/grape/validations/validators/same_as_validator.rb +6 -15
  162. data/lib/grape/validations/validators/values_validator.rb +36 -65
  163. data/lib/grape/validations.rb +8 -21
  164. data/lib/grape/version.rb +1 -2
  165. data/lib/grape/xml.rb +17 -0
  166. data/lib/grape.rb +75 -288
  167. metadata +79 -291
  168. data/lib/grape/api/helpers.rb +0 -9
  169. data/lib/grape/dsl/api.rb +0 -19
  170. data/lib/grape/dsl/configuration.rb +0 -15
  171. data/lib/grape/eager_load.rb +0 -20
  172. data/lib/grape/exceptions/empty_message_body.rb +0 -11
  173. data/lib/grape/exceptions/missing_option.rb +0 -11
  174. data/lib/grape/exceptions/too_many_multipart_files.rb +0 -11
  175. data/lib/grape/exceptions/unknown_options.rb +0 -11
  176. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +0 -27
  177. data/lib/grape/extensions/hash.rb +0 -22
  178. data/lib/grape/extensions/hashie/mash.rb +0 -26
  179. data/lib/grape/http/headers.rb +0 -61
  180. data/lib/grape/middleware/globals.rb +0 -16
  181. data/lib/grape/middleware/helpers.rb +0 -12
  182. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +0 -24
  183. data/lib/grape/router/attribute_translator.rb +0 -63
  184. data/lib/grape/types/invalid_value.rb +0 -8
  185. data/lib/grape/util/json.rb +0 -12
  186. data/lib/grape/util/lazy_block.rb +0 -27
  187. data/lib/grape/util/lazy_object.rb +0 -43
  188. data/lib/grape/util/lazy_value.rb +0 -91
  189. data/lib/grape/util/registrable.rb +0 -15
  190. data/lib/grape/util/strict_hash_configuration.rb +0 -108
  191. data/lib/grape/util/xml.rb +0 -10
  192. data/lib/grape/validations/attributes_doc.rb +0 -58
  193. data/lib/grape/validations/types/build_coercer.rb +0 -94
  194. data/lib/grape/validations/validator_factory.rb +0 -15
  195. data/spec/grape/api/custom_validations_spec.rb +0 -213
  196. data/spec/grape/api/deeply_included_options_spec.rb +0 -56
  197. data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
  198. data/spec/grape/api/documentation_spec.rb +0 -59
  199. data/spec/grape/api/inherited_helpers_spec.rb +0 -114
  200. data/spec/grape/api/instance_spec.rb +0 -103
  201. data/spec/grape/api/invalid_format_spec.rb +0 -45
  202. data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
  203. data/spec/grape/api/nested_helpers_spec.rb +0 -50
  204. data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
  205. data/spec/grape/api/parameters_modification_spec.rb +0 -41
  206. data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
  207. data/spec/grape/api/recognize_path_spec.rb +0 -21
  208. data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
  209. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
  210. data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
  211. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
  212. data/spec/grape/api/shared_helpers_spec.rb +0 -36
  213. data/spec/grape/api_remount_spec.rb +0 -509
  214. data/spec/grape/api_spec.rb +0 -4356
  215. data/spec/grape/dsl/callbacks_spec.rb +0 -45
  216. data/spec/grape/dsl/desc_spec.rb +0 -98
  217. data/spec/grape/dsl/headers_spec.rb +0 -62
  218. data/spec/grape/dsl/helpers_spec.rb +0 -100
  219. data/spec/grape/dsl/inside_route_spec.rb +0 -531
  220. data/spec/grape/dsl/logger_spec.rb +0 -24
  221. data/spec/grape/dsl/middleware_spec.rb +0 -60
  222. data/spec/grape/dsl/parameters_spec.rb +0 -180
  223. data/spec/grape/dsl/request_response_spec.rb +0 -225
  224. data/spec/grape/dsl/routing_spec.rb +0 -275
  225. data/spec/grape/dsl/settings_spec.rb +0 -261
  226. data/spec/grape/dsl/validations_spec.rb +0 -55
  227. data/spec/grape/endpoint/declared_spec.rb +0 -846
  228. data/spec/grape/endpoint_spec.rb +0 -1085
  229. data/spec/grape/entity_spec.rb +0 -336
  230. data/spec/grape/exceptions/base_spec.rb +0 -81
  231. data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -185
  232. data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
  233. data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
  234. data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
  235. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
  236. data/spec/grape/exceptions/missing_group_type_spec.rb +0 -17
  237. data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
  238. data/spec/grape/exceptions/missing_option_spec.rb +0 -15
  239. data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
  240. data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
  241. data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -19
  242. data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
  243. data/spec/grape/exceptions/validation_spec.rb +0 -19
  244. data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
  245. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
  246. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
  247. data/spec/grape/grape_spec.rb +0 -9
  248. data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
  249. data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
  250. data/spec/grape/integration/rack_spec.rb +0 -51
  251. data/spec/grape/loading_spec.rb +0 -44
  252. data/spec/grape/middleware/auth/base_spec.rb +0 -31
  253. data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
  254. data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
  255. data/spec/grape/middleware/base_spec.rb +0 -221
  256. data/spec/grape/middleware/error_spec.rb +0 -85
  257. data/spec/grape/middleware/exception_spec.rb +0 -294
  258. data/spec/grape/middleware/formatter_spec.rb +0 -461
  259. data/spec/grape/middleware/globals_spec.rb +0 -30
  260. data/spec/grape/middleware/stack_spec.rb +0 -155
  261. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
  262. data/spec/grape/middleware/versioner/header_spec.rb +0 -345
  263. data/spec/grape/middleware/versioner/param_spec.rb +0 -171
  264. data/spec/grape/middleware/versioner/path_spec.rb +0 -62
  265. data/spec/grape/middleware/versioner_spec.rb +0 -21
  266. data/spec/grape/named_api_spec.rb +0 -19
  267. data/spec/grape/parser_spec.rb +0 -86
  268. data/spec/grape/path_spec.rb +0 -252
  269. data/spec/grape/presenters/presenter_spec.rb +0 -71
  270. data/spec/grape/request_spec.rb +0 -126
  271. data/spec/grape/util/inheritable_setting_spec.rb +0 -242
  272. data/spec/grape/util/inheritable_values_spec.rb +0 -79
  273. data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
  274. data/spec/grape/util/stackable_values_spec.rb +0 -128
  275. data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
  276. data/spec/grape/validations/attributes_doc_spec.rb +0 -153
  277. data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
  278. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -38
  279. data/spec/grape/validations/params_scope_spec.rb +0 -1420
  280. data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -56
  281. data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
  282. data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
  283. data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
  284. data/spec/grape/validations/types_spec.rb +0 -111
  285. data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
  286. data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
  287. data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
  288. data/spec/grape/validations/validators/base_spec.rb +0 -38
  289. data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
  290. data/spec/grape/validations/validators/default_spec.rb +0 -463
  291. data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
  292. data/spec/grape/validations/validators/except_values_spec.rb +0 -192
  293. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
  294. data/spec/grape/validations/validators/presence_spec.rb +0 -315
  295. data/spec/grape/validations/validators/regexp_spec.rb +0 -161
  296. data/spec/grape/validations/validators/same_as_spec.rb +0 -57
  297. data/spec/grape/validations/validators/values_spec.rb +0 -733
  298. data/spec/grape/validations/validators/zh-CN.yml +0 -10
  299. data/spec/grape/validations_spec.rb +0 -2030
  300. data/spec/integration/eager_load/eager_load_spec.rb +0 -15
  301. data/spec/integration/multi_json/json_spec.rb +0 -7
  302. data/spec/integration/multi_xml/xml_spec.rb +0 -7
  303. data/spec/shared/deprecated_class_examples.rb +0 -16
  304. data/spec/shared/versioning_examples.rb +0 -215
  305. data/spec/spec_helper.rb +0 -52
  306. data/spec/support/basic_auth_encode_helpers.rb +0 -11
  307. data/spec/support/chunks.rb +0 -14
  308. data/spec/support/content_type_helpers.rb +0 -15
  309. data/spec/support/endpoint_faker.rb +0 -25
  310. data/spec/support/file_streamer.rb +0 -13
  311. data/spec/support/integer_helpers.rb +0 -13
  312. data/spec/support/versioned_helpers.rb +0 -55
data/grape.gemspec CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- $LOAD_PATH.unshift File.expand_path('lib', __dir__)
4
- require 'grape/version'
3
+ require_relative 'lib/grape/version'
5
4
 
6
5
  Gem::Specification.new do |s|
7
6
  s.name = 'grape'
@@ -17,20 +16,18 @@ Gem::Specification.new do |s|
17
16
  'bug_tracker_uri' => 'https://github.com/ruby-grape/grape/issues',
18
17
  'changelog_uri' => "https://github.com/ruby-grape/grape/blob/v#{s.version}/CHANGELOG.md",
19
18
  'documentation_uri' => "https://www.rubydoc.info/gems/grape/#{s.version}",
20
- 'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}"
19
+ 'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}",
20
+ 'rubygems_mfa_required' => 'true'
21
21
  }
22
22
 
23
- s.add_runtime_dependency 'activesupport', '>= 5'
24
- s.add_runtime_dependency 'builder'
25
- s.add_runtime_dependency 'dry-types', '>= 1.1'
26
- s.add_runtime_dependency 'mustermann-grape', '~> 1.0.0'
27
- s.add_runtime_dependency 'rack', '>= 1.3.0'
28
- s.add_runtime_dependency 'rack-accept'
23
+ s.add_dependency 'activesupport', '>= 7.2'
24
+ s.add_dependency 'dry-configurable', '>= 1.0'
25
+ s.add_dependency 'dry-types', '>= 1.1'
26
+ s.add_dependency 'mustermann', '>= 4.0'
27
+ s.add_dependency 'rack', '>= 2.2.4'
28
+ s.add_dependency 'zeitwerk', '>= 2.6'
29
29
 
30
- s.files = %w[CHANGELOG.md CONTRIBUTING.md README.md grape.png UPGRADING.md LICENSE]
31
- s.files += %w[grape.gemspec]
32
- s.files += Dir['lib/**/*']
33
- s.test_files = Dir['spec/**/*']
30
+ s.files = Dir['lib/**/*', 'CHANGELOG.md', 'CONTRIBUTING.md', 'README.md', 'grape.png', 'UPGRADING.md', 'LICENSE', 'grape.gemspec']
34
31
  s.require_paths = ['lib']
35
- s.required_ruby_version = '>= 2.6.0'
32
+ s.required_ruby_version = '>= 3.3'
36
33
  end
@@ -1,24 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/router'
4
-
5
3
  module Grape
6
4
  class API
7
5
  # The API Instance class, is the engine behind Grape::API. Each class that inherits
8
6
  # from this will represent a different API instance
9
7
  class Instance
10
- include Grape::DSL::API
8
+ # Marks this and every mounted instance as a mountable Grape app (see Grape::Mountable).
9
+ extend Grape::Mountable
10
+ extend Grape::DSL::Settings
11
+ extend Grape::DSL::Desc
12
+ extend Grape::DSL::Validations
13
+ extend Grape::DSL::Callbacks
14
+ extend Grape::DSL::Logger
15
+ extend Grape::DSL::Middleware
16
+ extend Grape::DSL::RequestResponse
17
+ extend Grape::DSL::Routing
18
+ extend Grape::DSL::Helpers
19
+ extend Grape::Middleware::Auth::DSL
20
+
21
+ Boolean = Grape::API::Boolean
11
22
 
12
23
  class << self
13
- attr_reader :instance, :base
14
24
  attr_accessor :configuration
15
25
 
16
- def given(conditional_option, &block)
17
- evaluate_as_instance_with_configuration(block, lazy: true) if conditional_option && block
18
- end
19
-
20
- def mounted(&block)
21
- evaluate_as_instance_with_configuration(block, lazy: true)
26
+ def to_s
27
+ @base&.to_s || super
22
28
  end
23
29
 
24
30
  def base=(grape_api)
@@ -26,12 +32,8 @@ module Grape
26
32
  grape_api.instances << self
27
33
  end
28
34
 
29
- def to_s
30
- base&.to_s || super
31
- end
32
-
33
35
  def base_instance?
34
- self == base.base_instance
36
+ self == @base.base_instance
35
37
  end
36
38
 
37
39
  # A class-level lock to ensure the API is not compiled by multiple
@@ -45,91 +47,33 @@ module Grape
45
47
  reset_validations!
46
48
  end
47
49
 
48
- # Parses the API's definition and compiles it into an instance of
49
- # Grape::API.
50
- def compile
51
- @instance ||= new
52
- end
53
-
54
- # Wipe the compiled API so we can recompile after changes were made.
55
- def change!
56
- @instance = nil
57
- end
58
-
59
50
  # This is the interface point between Rack and Grape; it accepts a request
60
51
  # from Rack and ultimately returns an array of three values: the status,
61
52
  # the headers, and the body. See [the rack specification]
62
53
  # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
63
54
  def call(env)
64
55
  compile!
65
- call!(env)
66
- end
67
-
68
- # A non-synchronized version of ::call.
69
- def call!(env)
70
- instance.call(env)
71
- end
72
-
73
- # (see #cascade?)
74
- def cascade(value = nil)
75
- if value.nil?
76
- inheritable_setting.namespace_inheritable.key?(:cascade) ? !namespace_inheritable(:cascade).nil? : true
77
- else
78
- namespace_inheritable(:cascade, value)
79
- end
56
+ @instance.call(env)
80
57
  end
81
58
 
82
59
  def compile!
83
- return if instance
60
+ return if @instance
84
61
 
85
- LOCK.synchronize { compile unless instance }
62
+ LOCK.synchronize { @instance ||= new }
86
63
  end
87
64
 
88
65
  # see Grape::Router#recognize_path
89
66
  def recognize_path(path)
90
67
  compile!
91
- instance.router.recognize_path(path)
92
- end
93
-
94
- protected
95
-
96
- def prepare_routes
97
- endpoints.map(&:routes).flatten
68
+ @instance.router.recognize_path(path)
98
69
  end
99
70
 
100
- # Execute first the provided block, then each of the
101
- # block passed in. Allows for simple 'before' setups
102
- # of settings stack pushes.
103
- def nest(*blocks, &block)
104
- blocks.compact!
105
- if blocks.any?
106
- evaluate_as_instance_with_configuration(block) if block
107
- blocks.each { |b| evaluate_as_instance_with_configuration(b) }
108
- reset_validations!
109
- else
110
- instance_eval(&block)
111
- end
112
- end
113
-
114
- def evaluate_as_instance_with_configuration(block, lazy: false)
115
- lazy_block = Grape::Util::LazyBlock.new do |configuration|
116
- value_for_configuration = configuration
117
- self.configuration = value_for_configuration.evaluate if value_for_configuration.respond_to?(:lazy?) && value_for_configuration.lazy?
118
- response = instance_eval(&block)
119
- self.configuration = value_for_configuration
120
- response
121
- end
122
- if base && base_instance? && lazy
123
- lazy_block
124
- else
125
- lazy_block.evaluate_from(configuration)
126
- end
71
+ # Wipe the compiled API so we can recompile after changes were made.
72
+ def change!
73
+ @instance = nil
127
74
  end
128
75
 
129
- def inherited(subclass)
130
- subclass.reset!
131
- subclass.logger = logger.clone
132
- end
76
+ protected
133
77
 
134
78
  def inherit_settings(other_settings)
135
79
  top_level_setting.inherit_from other_settings.point_in_time_copy
@@ -143,6 +87,14 @@ module Grape
143
87
 
144
88
  reset_routes!
145
89
  end
90
+
91
+ private
92
+
93
+ def inherited(subclass)
94
+ super
95
+ subclass.reset!
96
+ subclass.logger logger.clone
97
+ end
146
98
  end
147
99
 
148
100
  attr_reader :router
@@ -162,9 +114,13 @@ module Grape
162
114
 
163
115
  # Handle a request. See Rack documentation for what `env` is.
164
116
  def call(env)
165
- result = @router.call(env)
166
- result[1].delete(Grape::Http::Headers::X_CASCADE) unless cascade?
167
- result
117
+ status, headers, response = @router.call(env)
118
+ unless cascade?
119
+ headers = Grape::Util::Header.new.merge(headers)
120
+ headers.delete('X-Cascade')
121
+ end
122
+
123
+ [status, headers, response]
168
124
  end
169
125
 
170
126
  # Some requests may return a HTTP 404 error if grape cannot find a matching
@@ -176,8 +132,9 @@ module Grape
176
132
  # errors from reaching upstream. This is effectivelly done by unsetting
177
133
  # X-Cascade. Default :cascade is true.
178
134
  def cascade?
179
- return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.key?(:cascade)
180
- return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options)&.key?(:cascade)
135
+ namespace_inheritable = self.class.inheritable_setting.namespace_inheritable
136
+ return namespace_inheritable[:cascade] if namespace_inheritable.key?(:cascade)
137
+ return namespace_inheritable[:version_options].cascade if namespace_inheritable[:version_options]
181
138
 
182
139
  true
183
140
  end
@@ -191,89 +148,42 @@ module Grape
191
148
  # will return an HTTP 405 response for any HTTP method that the resource
192
149
  # cannot handle.
193
150
  def add_head_not_allowed_methods_and_options_methods
194
- versioned_route_configs = collect_route_config_per_pattern
195
151
  # The paths we collected are prepared (cf. Path#prepare), so they
196
152
  # contain already versioning information when using path versioning.
197
- # Disable versioning so adding a route won't prepend versioning
198
- # informations again.
199
- without_root_prefix do
200
- without_versioning do
201
- versioned_route_configs.each do |config|
202
- next if config[:options][:matching_wildchar]
203
-
204
- allowed_methods = config[:methods].dup
205
-
206
- allowed_methods |= [Grape::Http::Headers::HEAD] if !self.class.namespace_inheritable(:do_not_route_head) && allowed_methods.include?(Grape::Http::Headers::GET)
207
-
208
- allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods)
209
-
210
- config[:endpoint].options[:options_route_enabled] = true unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
211
-
212
- attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
213
- generate_not_allowed_method(config[:pattern], **attributes)
214
- end
215
- end
216
- end
153
+ all_routes = self.class.endpoints.flat_map(&:routes)
154
+
155
+ # Read the settings the helper routes need from a *copy* with the
156
+ # root-prefix/versioning keys stripped, so adding these routes won't
157
+ # prepend versioning information again. This used to delete those keys
158
+ # from the shared class-level settings and restore them in an ensure;
159
+ # a request served concurrently on another instance during a runtime
160
+ # recompile could observe the missing keys (e.g. via #cascade?). A
161
+ # local copy keeps that mutation off the shared object.
162
+ namespace_inheritable = self.class.inheritable_setting.namespace_inheritable.to_hash.except(*ROOT_PREFIX_VERSIONING_KEYS)
163
+ collect_route_config_per_pattern(all_routes, namespace_inheritable)
217
164
  end
218
165
 
219
- def collect_route_config_per_pattern
220
- all_routes = self.class.endpoints.map(&:routes).flatten
221
- routes_by_regexp = all_routes.group_by { |route| route.pattern.to_regexp }
166
+ def collect_route_config_per_pattern(all_routes, namespace_inheritable)
167
+ routes_by_regexp = all_routes.group_by(&:pattern_regexp)
222
168
 
223
169
  # Build the configuration based on the first endpoint and the collection of methods supported.
224
- routes_by_regexp.values.map do |routes|
225
- last_route = routes.last # Most of the configuration is taken from the last endpoint
226
- matching_wildchar = routes.any? { |route| route.request_method == '*' }
227
- {
228
- options: { matching_wildchar: matching_wildchar },
229
- pattern: last_route.pattern,
230
- requirements: last_route.requirements,
231
- path: last_route.origin,
232
- endpoint: last_route.app,
233
- methods: matching_wildchar ? Grape::Http::Headers::SUPPORTED_METHODS : routes.map(&:request_method)
234
- }
235
- end
236
- end
170
+ routes_by_regexp.each_value do |routes|
171
+ next if routes.any? { |route| route.request_method == '*' }
237
172
 
238
- # Generate a route that returns an HTTP 405 response for a user defined
239
- # path on methods not specified
240
- def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
241
- supported_methods =
242
- if self.class.namespace_inheritable(:do_not_route_options)
243
- Grape::Http::Headers::SUPPORTED_METHODS
244
- else
245
- Grape::Http::Headers::SUPPORTED_METHODS_WITHOUT_OPTIONS
246
- end
247
- not_allowed_methods = supported_methods - allowed_methods
248
- @router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
249
- end
173
+ last_route = routes.last # Most of the configuration is taken from the last endpoint
174
+ allowed_methods = routes.map(&:request_method)
175
+ allowed_methods |= [Rack::HEAD] if !namespace_inheritable[:do_not_route_head] && allowed_methods.include?(Rack::GET)
250
176
 
251
- # Allows definition of endpoints that ignore the versioning configuration
252
- # used by the rest of your API.
253
- def without_versioning(&_block)
254
- old_version = self.class.namespace_inheritable(:version)
255
- old_version_options = self.class.namespace_inheritable(:version_options)
177
+ allow_header = namespace_inheritable[:do_not_route_options] ? allowed_methods : [Rack::OPTIONS] | allowed_methods
178
+ last_route.app.options_route_enabled = true unless namespace_inheritable[:do_not_route_options] || allowed_methods.include?(Rack::OPTIONS)
256
179
 
257
- self.class.namespace_inheritable_to_nil(:version)
258
- self.class.namespace_inheritable_to_nil(:version_options)
259
-
260
- yield
261
-
262
- self.class.namespace_inheritable(:version, old_version)
263
- self.class.namespace_inheritable(:version_options, old_version_options)
180
+ greedy_route = Grape::Router::GreedyRoute.new(last_route.pattern, endpoint: last_route.app, allow_header:)
181
+ @router.associate_routes(greedy_route)
182
+ end
264
183
  end
265
184
 
266
- # Allows definition of endpoints that ignore the root prefix used by the
267
- # rest of your API.
268
- def without_root_prefix(&_block)
269
- old_prefix = self.class.namespace_inheritable(:root_prefix)
270
-
271
- self.class.namespace_inheritable_to_nil(:root_prefix)
272
-
273
- yield
274
-
275
- self.class.namespace_inheritable(:root_prefix, old_prefix)
276
- end
185
+ ROOT_PREFIX_VERSIONING_KEYS = %i[version version_options root_prefix].freeze
186
+ private_constant :ROOT_PREFIX_VERSIONING_KEYS
277
187
  end
278
188
  end
279
189
  end
data/lib/grape/api.rb CHANGED
@@ -1,49 +1,47 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/router'
4
- require 'grape/api/instance'
5
-
6
3
  module Grape
7
4
  # The API class is the primary entry point for creating Grape APIs. Users
8
5
  # should subclass this class in order to build an API.
9
6
  class API
7
+ # Marks this and every subclass as a mountable Grape app (see Grape::Mountable).
8
+ extend Grape::Mountable
9
+
10
10
  # Class methods that we want to call on the API rather than on the API object
11
- NON_OVERRIDABLE = %i[call call! configuration compile! inherited].freeze
11
+ NON_OVERRIDABLE = %i[base= base_instance? call change! configuration compile! inherit_settings recognize_path reset! routes top_level_setting= top_level_setting].freeze
12
+
13
+ Helpers = Grape::DSL::Helpers::BaseHelper
12
14
 
13
15
  class Boolean
16
+ VALUES = [true, false].freeze
17
+ private_constant :VALUES
18
+
14
19
  def self.build(val)
15
- return nil if val != true && val != false
20
+ return unless VALUES.include?(val)
16
21
 
17
22
  new
18
23
  end
19
24
  end
20
25
 
21
- class Instance
22
- Boolean = Grape::API::Boolean
23
- end
24
-
25
26
  class << self
26
- attr_accessor :base_instance, :instances
27
+ extend Forwardable
27
28
 
28
- # Rather than initializing an object of type Grape::API, create an object of type Instance
29
- def new(*args, &block)
30
- base_instance.new(*args, &block)
31
- end
29
+ attr_accessor :base_instance, :instances
32
30
 
33
- # When inherited, will create a list of all instances (times the API was mounted)
34
- # It will listen to the setup required to mount that endpoint, and replicate it on any new instance
35
- def inherited(api)
36
- super
31
+ delegate_missing_to :base_instance
37
32
 
38
- api.initial_setup(Grape::API == self ? Grape::API::Instance : @base_instance)
39
- api.override_all_methods!
40
- end
33
+ # This is the interface point between Rack and Grape; it accepts a request
34
+ # from Rack and ultimately returns an array of three values: the status,
35
+ # the headers, and the body. See [the rack specification]
36
+ # (https://github.com/rack/rack/blob/main/SPEC.rdoc) for more.
37
+ # NOTE: This will only be called on an API directly mounted on RACK
38
+ def_delegators :base_instance, :new, :configuration, :call, :change!, :compile!, :recognize_path, :routes
41
39
 
42
40
  # Initialize the instance variables on the remountable class, and the base_instance
43
41
  # an instance that will be used to create the set up but will not be mounted
44
42
  def initial_setup(base_instance_parent)
45
43
  @instances = []
46
- @setup = Set.new
44
+ @setup = []
47
45
  @base_parent = base_instance_parent
48
46
  @base_instance = mount_instance
49
47
  end
@@ -51,8 +49,8 @@ module Grape
51
49
  # Redefines all methods so that are forwarded to add_setup and be recorded
52
50
  def override_all_methods!
53
51
  (base_instance.methods - Class.methods - NON_OVERRIDABLE).each do |method_override|
54
- define_singleton_method(method_override) do |*args, &block|
55
- add_setup(method_override, *args, &block)
52
+ define_singleton_method(method_override) do |*args, **kwargs, &block|
53
+ add_setup(method: method_override, args:, kwargs:, block:)
56
54
  end
57
55
  end
58
56
  end
@@ -64,138 +62,104 @@ module Grape
64
62
  # `configuration` as normal.
65
63
  def configure
66
64
  config = @base_instance.configuration
67
- if block_given?
68
- yield config
69
- self
70
- else
71
- config
72
- end
73
- end
65
+ return config unless block_given?
74
66
 
75
- # This is the interface point between Rack and Grape; it accepts a request
76
- # from Rack and ultimately returns an array of three values: the status,
77
- # the headers, and the body. See [the rack specification]
78
- # (http://www.rubydoc.info/github/rack/rack/master/file/SPEC) for more.
79
- # NOTE: This will only be called on an API directly mounted on RACK
80
- def call(*args, &block)
81
- instance_for_rack.call(*args, &block)
82
- end
83
-
84
- # Alleviates problems with autoloading by tring to search for the constant
85
- def const_missing(*args)
86
- if base_instance.const_defined?(*args)
87
- base_instance.const_get(*args)
88
- else
89
- super
90
- end
67
+ yield config
68
+ self
91
69
  end
92
70
 
93
71
  # The remountable class can have a configuration hash to provide some dynamic class-level variables.
94
72
  # For instance, a description could be done using: `desc configuration[:description]` if it may vary
95
73
  # depending on where the endpoint is mounted. Use with care, if you find yourself using configuration
96
74
  # too much, you may actually want to provide a new API rather than remount it.
97
- def mount_instance(**opts)
75
+ def mount_instance(configuration: nil)
98
76
  instance = Class.new(@base_parent)
99
- instance.configuration = Grape::Util::EndpointConfiguration.new(opts[:configuration] || {})
77
+ instance.configuration = Grape::Util::EndpointConfiguration.new(configuration || {})
100
78
  instance.base = self
101
79
  replay_setup_on(instance)
102
80
  instance
103
81
  end
104
82
 
83
+ private
84
+
85
+ # When inherited, will create a list of all instances (times the API was mounted)
86
+ # It will listen to the setup required to mount that endpoint, and replicate it on any new instance
87
+ def inherited(api)
88
+ super
89
+
90
+ api.initial_setup(self == Grape::API ? Grape::API::Instance : @base_instance)
91
+ api.override_all_methods!
92
+ end
93
+
105
94
  # Replays the set up to produce an API as defined in this class, can be called
106
95
  # on classes that inherit from Grape::API
107
96
  def replay_setup_on(instance)
108
97
  @setup.each do |setup_step|
109
- replay_step_on(instance, setup_step)
98
+ replay_step_on(instance, **setup_step)
110
99
  end
111
100
  end
112
101
 
113
- def respond_to?(method, include_private = false)
114
- super(method, include_private) || base_instance.respond_to?(method, include_private)
115
- end
116
-
117
- def respond_to_missing?(method, include_private = false)
118
- base_instance.respond_to?(method, include_private)
119
- end
120
-
121
- def method_missing(method, *args, &block)
122
- # If there's a missing method, it may be defined on the base_instance instead.
123
- if respond_to_missing?(method)
124
- base_instance.send(method, *args, &block)
125
- else
126
- super
102
+ # Adds a new stage to the set up require to get a Grape::API up and running
103
+ def add_setup(**step)
104
+ @setup << step
105
+ last_response = nil
106
+ @instances.each do |instance|
107
+ last_response = replay_step_on(instance, **step)
127
108
  end
128
- end
129
109
 
130
- def compile!
131
- require 'grape/eager_load'
132
- instance_for_rack.compile! # See API::Instance.compile!
110
+ refresh_mount_step if step[:method] != :mount
111
+ last_response
133
112
  end
134
113
 
135
- private
114
+ # Updating all previously mounted classes in the case that new methods have been executed.
115
+ def refresh_mount_step
116
+ @setup.each do |setup_step|
117
+ next if setup_step[:method] != :mount
136
118
 
137
- def instance_for_rack
138
- if never_mounted?
139
- base_instance
140
- else
141
- mounted_instances.first
119
+ refresh_mount_step = setup_step.merge(method: :refresh_mounted_api)
120
+ @setup << refresh_mount_step
121
+ @instances.each do |instance|
122
+ replay_step_on(instance, **refresh_mount_step)
123
+ end
142
124
  end
143
125
  end
144
126
 
145
- # Adds a new stage to the set up require to get a Grape::API up and running
146
- def add_setup(method, *args, &block)
147
- setup_step = { method: method, args: args, block: block }
148
- @setup += [setup_step]
149
- last_response = nil
150
- @instances.each do |instance|
151
- last_response = replay_step_on(instance, setup_step)
152
- end
153
- last_response
154
- end
127
+ def replay_step_on(instance, method:, args:, kwargs:, block:)
128
+ return if skip_immediate_run?(instance, args, kwargs)
155
129
 
156
- def replay_step_on(instance, setup_step)
157
- return if skip_immediate_run?(instance, setup_step[:args])
130
+ eval_args = evaluate_arguments(instance.configuration, *args)
131
+ eval_kwargs = kwargs.deep_transform_values { |v| evaluate_arguments(instance.configuration, v).first }
132
+ response = instance.__send__(method, *eval_args, **eval_kwargs, &block)
158
133
 
159
- args = evaluate_arguments(instance.configuration, *setup_step[:args])
160
- response = instance.send(setup_step[:method], *args, &setup_step[:block])
161
- if skip_immediate_run?(instance, [response])
162
- response
163
- else
164
- evaluate_arguments(instance.configuration, response).first
165
- end
134
+ return response if skip_immediate_run?(instance, [response], kwargs)
135
+
136
+ evaluate_arguments(instance.configuration, response).first
166
137
  end
167
138
 
168
139
  # Skips steps that contain arguments to be lazily executed (on re-mount time)
169
- def skip_immediate_run?(instance, args)
140
+ def skip_immediate_run?(instance, args, kwargs)
170
141
  instance.base_instance? &&
171
- (any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) })
142
+ (any_lazy?(args) || args.any? { |arg| arg.is_a?(Hash) && any_lazy?(arg.values) } || any_lazy?(kwargs.values))
172
143
  end
173
144
 
174
145
  def any_lazy?(args)
175
- args.any? { |argument| argument.respond_to?(:lazy?) && argument.lazy? }
146
+ args.any?(Grape::Util::Lazy::Base)
176
147
  end
177
148
 
178
149
  def evaluate_arguments(configuration, *args)
179
150
  args.map do |argument|
180
- if argument.respond_to?(:lazy?) && argument.lazy?
151
+ case argument
152
+ when Grape::Util::Lazy::Base
181
153
  argument.evaluate_from(configuration)
182
- elsif argument.is_a?(Hash)
154
+ when Hash
183
155
  argument.transform_values { |value| evaluate_arguments(configuration, value).first }
184
- elsif argument.is_a?(Array)
156
+ when Array
185
157
  evaluate_arguments(configuration, *argument)
186
158
  else
187
159
  argument
188
160
  end
189
161
  end
190
162
  end
191
-
192
- def never_mounted?
193
- mounted_instances.empty?
194
- end
195
-
196
- def mounted_instances
197
- instances - [base_instance]
198
- end
199
163
  end
200
164
  end
201
165
  end
@@ -1,13 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grape/util/registrable'
4
-
5
3
  module Grape
6
4
  module ContentTypes
7
- extend Util::Registrable
5
+ module_function
8
6
 
9
7
  # Content types are listed in order of preference.
10
- CONTENT_TYPES = {
8
+ DEFAULTS = {
11
9
  xml: 'application/xml',
12
10
  serializable_hash: 'application/json',
13
11
  json: 'application/json',
@@ -15,14 +13,28 @@ module Grape
15
13
  txt: 'text/plain'
16
14
  }.freeze
17
15
 
18
- class << self
19
- def content_types_for_settings(settings)
20
- settings&.inject(:merge!)
21
- end
16
+ MIME_TYPES = Grape::ContentTypes::DEFAULTS.except(:serializable_hash).invert.freeze
17
+
18
+ def content_types_for(from_settings)
19
+ from_settings.presence || DEFAULTS
20
+ end
21
+
22
+ def mime_types_for(from_settings)
23
+ return MIME_TYPES if from_settings == Grape::ContentTypes::DEFAULTS
24
+
25
+ from_settings.invert.transform_keys! { |k| media_type(k) }
26
+ end
27
+
28
+ # The media type of a content-type header: the part before any `;`
29
+ # parameters, with surrounding whitespace removed
30
+ # (e.g. `'text/html'` for `'text/html; charset=utf-8'`). Returns nil for a
31
+ # nil content type. Skips the split (and its allocation) when there are no
32
+ # parameters, which is the common case.
33
+ def media_type(content_type)
34
+ return if content_type.nil?
22
35
 
23
- def content_types_for(from_settings)
24
- from_settings.presence || Grape::ContentTypes::CONTENT_TYPES.merge(default_elements)
25
- end
36
+ base = content_type.include?(';') ? content_type.split(';', 2).first : content_type
37
+ base.strip
26
38
  end
27
39
  end
28
40
  end