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
@@ -6,65 +6,40 @@ module Grape
6
6
  # on the instance level of this class may be called
7
7
  # from inside a `get`, `post`, etc.
8
8
  class Endpoint
9
+ extend Forwardable
9
10
  include Grape::DSL::Settings
11
+ include Grape::DSL::Headers
10
12
  include Grape::DSL::InsideRoute
11
13
 
12
- attr_accessor :block, :source, :options
13
- attr_reader :env, :request, :headers, :params
14
+ attr_reader :env, :request, :source, :options, :endpoints
15
+ attr_accessor :options_route_enabled
14
16
 
15
- class << self
16
- def new(*args, &block)
17
- self == Endpoint ? Class.new(Endpoint).new(*args, &block) : super
18
- end
17
+ def_delegators :request, :params, :headers, :cookies
18
+ def_delegator :cookies, :response_cookies
19
19
 
20
- def before_each(new_setup = false, &block)
21
- @before_each ||= []
22
- if new_setup == false
23
- return @before_each unless block
24
-
25
- @before_each << block
26
- else
27
- @before_each = [new_setup]
28
- end
29
- end
20
+ # The logger configured on the API this endpoint belongs to. Available
21
+ # inside route handlers, +before+/+after+/+after_validation+/+finally+
22
+ # filters, and +rescue_from+ blocks.
23
+ def logger
24
+ config.for.logger
25
+ end
30
26
 
31
- def run_before_each(endpoint)
32
- superclass.run_before_each(endpoint) unless self == Endpoint
33
- before_each.each { |blk| blk.call(endpoint) if blk.respond_to?(:call) }
34
- end
27
+ class << self
28
+ def block_to_unbound_method(block)
29
+ return unless block
35
30
 
36
- # @api private
37
- #
38
- # Create an UnboundMethod that is appropriate for executing an endpoint
39
- # route.
40
- #
41
- # The unbound method allows explicit calls to +return+ without raising a
42
- # +LocalJumpError+. The method will be removed, but a +Proc+ reference to
43
- # it will be returned. The returned +Proc+ expects a single argument: the
44
- # instance of +Endpoint+ to bind to the method during the call.
45
- #
46
- # @param [String, Symbol] method_name
47
- # @return [Proc]
48
- # @raise [NameError] an instance method with the same name already exists
49
- def generate_api_method(method_name, &block)
50
- raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name") if method_defined?(method_name)
51
-
52
- define_method(method_name, &block)
53
- method = instance_method(method_name)
54
- remove_method(method_name)
55
-
56
- proc do |endpoint_instance|
57
- ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: endpoint_instance) do
58
- method.bind(endpoint_instance).call
59
- end
60
- end
31
+ define_method :temp_unbound_method, block
32
+ method = instance_method(:temp_unbound_method)
33
+ remove_method :temp_unbound_method
34
+ method
61
35
  end
62
36
  end
63
37
 
64
38
  # Create a new endpoint.
65
39
  # @param new_settings [InheritableSetting] settings to determine the params,
66
40
  # validations, and other properties from.
67
- # @param options [Hash] attributes of this endpoint
41
+ # @param options [Hash] attributes of this endpoint, normalized into a
42
+ # +Grape::Endpoint::Options+ value object.
68
43
  # @option options path [String or Array] the path to this endpoint, within
69
44
  # the current scope.
70
45
  # @option options method [String or Array] which HTTP method(s) can be used
@@ -73,69 +48,46 @@ module Grape
73
48
  # @note This happens at the time of API definition, so in this context the
74
49
  # endpoint does not know if it will be mounted under a different endpoint.
75
50
  # @yield a block defining what your API should do when this endpoint is hit
76
- def initialize(new_settings, options = {}, &block)
77
- require_option(options, :path)
78
- require_option(options, :method)
79
-
51
+ def initialize(new_settings, **options, &block)
80
52
  self.inheritable_setting = new_settings.point_in_time_copy
81
53
 
82
54
  # now +namespace_stackable(:declared_params)+ contains all params defined for
83
55
  # this endpoint and its parents, but later it will be cleaned up,
84
56
  # see +reset_validations!+ in lib/grape/dsl/validations.rb
85
- route_setting(:declared_params, namespace_stackable(:declared_params).flatten)
86
- route_setting(:saved_validations, namespace_stackable(:validations))
57
+ inheritable_setting.route[:declared_params] = inheritable_setting.namespace_stackable[:declared_params].flatten
58
+ inheritable_setting.route[:saved_validations] = inheritable_setting.namespace_stackable[:validations].dup
87
59
 
88
- namespace_stackable(:representations, []) unless namespace_stackable(:representations)
89
- namespace_inheritable(:default_error_status, 500) unless namespace_inheritable(:default_error_status)
60
+ inheritable_setting.namespace_stackable[:representations] ||= []
61
+ inheritable_setting.namespace_inheritable[:default_error_status] ||= 500
90
62
 
91
63
  @options = options
92
-
93
- @options[:path] = Array(options[:path])
94
- @options[:path] << '/' if options[:path].empty?
95
-
96
- @options[:method] = Array(options[:method])
97
- @options[:route_options] ||= {}
98
-
99
- @lazy_initialize_lock = Mutex.new
100
- @lazy_initialized = nil
101
- @block = nil
64
+ @options[:path] = Array(@options[:path])
65
+ @options[:path] << '/' if @options[:path].empty?
66
+ @options[:method] = Array(@options[:method])
67
+ @config = Options.new(**options)
102
68
 
103
69
  @status = nil
104
70
  @stream = nil
105
71
  @body = nil
106
- @proc = nil
107
-
108
- return unless block
109
-
110
- @source = block
111
- @block = self.class.generate_api_method(method_name, &block)
72
+ @source = self.class.block_to_unbound_method(block)
73
+ @before_filter_passed = false
74
+ @options_route_enabled = false
75
+ @endpoints = @config.app.endpoints if @config.app.respond_to?(:endpoints)
112
76
  end
113
77
 
114
78
  # Update our settings from a given set of stackable parameters. Used when
115
79
  # the endpoint's API is mounted under another one.
116
80
  def inherit_settings(namespace_stackable)
117
- inheritable_setting.route[:saved_validations] += namespace_stackable[:validations]
81
+ parent_validations = namespace_stackable[:validations]
82
+ inheritable_setting.route[:saved_validations].concat(parent_validations) if parent_validations.any?
118
83
  parent_declared_params = namespace_stackable[:declared_params]
119
-
120
- inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
84
+ inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params.any?
121
85
 
122
86
  endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
123
87
  end
124
88
 
125
- def require_option(options, key)
126
- raise Grape::Exceptions::MissingOption.new(key) unless options.key?(key)
127
- end
128
-
129
- def method_name
130
- [options[:method],
131
- Namespace.joined_space(namespace_stackable(:namespace)),
132
- (namespace_stackable(:mount_path) || []).join('/'),
133
- options[:path].join('/')]
134
- .join(' ')
135
- end
136
-
137
89
  def routes
138
- @routes ||= endpoints ? endpoints.collect(&:routes).flatten : to_routes
90
+ @routes ||= endpoints&.flat_map(&:routes) || to_routes
139
91
  end
140
92
 
141
93
  def reset_routes!
@@ -146,122 +98,79 @@ module Grape
146
98
 
147
99
  def mount_in(router)
148
100
  if endpoints
149
- endpoints.each { |e| e.mount_in(router) }
150
- else
151
- reset_routes!
152
- routes.each do |route|
153
- methods = [route.request_method]
154
- methods << Grape::Http::Headers::HEAD if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
155
- methods.each do |method|
156
- route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h) unless route.request_method == method
157
- router.append(route.apply(self))
158
- end
159
- end
160
- end
161
- end
162
-
163
- def to_routes
164
- route_options = prepare_default_route_attributes
165
- map_routes do |method, path|
166
- path = prepare_path(path)
167
- params = merge_route_options(**route_options.merge(suffix: path.suffix))
168
- route = Router::Route.new(method, path.path, **params)
169
- route.apply(self)
170
- end.flatten
171
- end
172
-
173
- def prepare_routes_requirements
174
- {}.merge!(*namespace_stackable(:namespace).map(&:requirements)).tap do |requirements|
175
- endpoint_requirements = options.dig(:route_options, :requirements)
176
- requirements.merge!(endpoint_requirements) if endpoint_requirements
101
+ compile!
102
+ return endpoints.each { |e| e.mount_in(router) }
177
103
  end
178
- end
179
-
180
- def prepare_default_route_attributes
181
- {
182
- namespace: namespace,
183
- version: prepare_version,
184
- requirements: prepare_routes_requirements,
185
- prefix: namespace_inheritable(:root_prefix),
186
- anchor: options[:route_options].fetch(:anchor, true),
187
- settings: inheritable_setting.route.except(:declared_params, :saved_validations),
188
- forward_match: options[:forward_match]
189
- }
190
- end
191
104
 
192
- def prepare_version
193
- version = namespace_inheritable(:version) || []
194
- return if version.empty?
195
-
196
- version.length == 1 ? version.first.to_s : version
197
- end
198
-
199
- def merge_route_options(**default)
200
- options[:route_options].clone.merge!(**default)
201
- end
202
-
203
- def map_routes
204
- options[:method].map { |method| options[:path].map { |path| yield method, path } }
205
- end
105
+ reset_routes!
106
+ compile!
107
+ routes.each do |route|
108
+ router.append(route.apply(self))
109
+ next if inheritable_setting.namespace_inheritable[:do_not_route_head] || route.request_method != Rack::GET
206
110
 
207
- def prepare_path(path)
208
- path_settings = inheritable_setting.to_hash[:namespace_stackable].merge(inheritable_setting.to_hash[:namespace_inheritable])
209
- Path.prepare(path, namespace, path_settings)
111
+ route.dup.then do |head_route|
112
+ head_route.convert_to_head_request!
113
+ router.append(head_route.apply(self))
114
+ end
115
+ end
210
116
  end
211
117
 
212
118
  def namespace
213
- @namespace ||= Namespace.joined_space_path(namespace_stackable(:namespace))
119
+ @namespace ||= Namespace.joined_space_path(inheritable_setting.namespace_stackable[:namespace])
214
120
  end
215
121
 
216
122
  def call(env)
217
- lazy_initialize!
218
123
  dup.call!(env)
219
124
  end
220
125
 
221
126
  def call!(env)
222
127
  env[Grape::Env::API_ENDPOINT] = self
223
128
  @env = env
129
+ # this adds the helpers only to the instance
130
+ singleton_class.include(@helpers) if @helpers
224
131
  @app.call(env)
225
132
  end
226
133
 
227
- # Return the collection of endpoints within this endpoint.
228
- # This is the case when an Grape::API mounts another Grape::API.
229
- def endpoints
230
- options[:app].endpoints if options[:app].respond_to?(:endpoints)
134
+ def ==(other)
135
+ other.is_a?(self.class) &&
136
+ config == other.config &&
137
+ inheritable_setting == other.inheritable_setting
231
138
  end
139
+ alias eql? ==
232
140
 
233
- def equals?(e)
234
- (options == e.options) && (inheritable_setting.to_hash == e.inheritable_setting.to_hash)
141
+ # The purpose of this override is solely for stripping internals when an error occurs while calling
142
+ # an endpoint through an api. See https://github.com/ruby-grape/grape/issues/2398
143
+ # Otherwise, it calls super.
144
+ def inspect
145
+ return super unless env
146
+
147
+ "#{self.class} in '#{route.origin}' endpoint"
235
148
  end
236
149
 
237
150
  protected
238
151
 
239
152
  def run
240
- ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env: env) do
241
- @header = {}
242
- @request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_params_with))
243
- @params = @request.params
244
- @headers = @request.headers
153
+ instrument_run do
154
+ @request = Grape::Request.new(env, build_params_with: @build_params_with)
245
155
  begin
246
- cookies.read(@request)
247
- self.class.run_before_each(self)
248
156
  run_filters befores, :before
157
+ @before_filter_passed = true
249
158
 
250
- if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
251
- raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allowed_methods)) unless options?
159
+ if env.key?(Grape::Env::GRAPE_ALLOWED_METHODS)
160
+ header['Allow'] = env[Grape::Env::GRAPE_ALLOWED_METHODS].join(', ')
161
+ raise Grape::Exceptions::MethodNotAllowed.new(header) unless options?
252
162
 
253
- header 'Allow', allowed_methods
254
163
  response_object = ''
255
164
  status 204
256
165
  else
257
166
  run_filters before_validations, :before_validation
258
- run_validators validations, request
167
+ run_validators(request:)
259
168
  run_filters after_validations, :after_validation
260
169
  response_object = execute
261
170
  end
262
171
 
263
172
  run_filters afters, :after
264
- cookies.write(header)
173
+ build_response_cookies
265
174
 
266
175
  # status verifies body presence when DELETE
267
176
  @body ||= response_object
@@ -276,132 +185,250 @@ module Grape
276
185
  end
277
186
  end
278
187
 
279
- def build_stack(helpers)
280
- stack = Grape::Middleware::Stack.new
188
+ def execute
189
+ return unless source
281
190
 
282
- stack.use Rack::Head
283
- stack.use Class.new(Grape::Middleware::Error),
284
- helpers: helpers,
285
- format: namespace_inheritable(:format),
286
- content_types: namespace_stackable_with_hash(:content_types),
287
- default_status: namespace_inheritable(:default_error_status),
288
- rescue_all: namespace_inheritable(:rescue_all),
289
- rescue_grape_exceptions: namespace_inheritable(:rescue_grape_exceptions),
290
- default_error_formatter: namespace_inheritable(:default_error_formatter),
291
- error_formatters: namespace_stackable_with_hash(:error_formatters),
292
- rescue_options: namespace_stackable_with_hash(:rescue_options) || {},
293
- rescue_handlers: namespace_reverse_stackable_with_hash(:rescue_handlers) || {},
294
- base_only_rescue_handlers: namespace_stackable_with_hash(:base_only_rescue_handlers) || {},
295
- all_rescue_handler: namespace_inheritable(:all_rescue_handler),
296
- grape_exceptions_rescue_handler: namespace_inheritable(:grape_exceptions_rescue_handler)
297
-
298
- stack.concat namespace_stackable(:middleware)
299
-
300
- if namespace_inheritable(:version)
301
- stack.use Grape::Middleware::Versioner.using(namespace_inheritable(:version_options)[:using]),
302
- versions: namespace_inheritable(:version)&.flatten,
303
- version_options: namespace_inheritable(:version_options),
304
- prefix: namespace_inheritable(:root_prefix),
305
- mount_path: namespace_stackable(:mount_path).first
191
+ instrument_render do
192
+ source.bind_call(self)
306
193
  end
194
+ end
307
195
 
308
- stack.use Grape::Middleware::Formatter,
309
- format: namespace_inheritable(:format),
310
- default_format: namespace_inheritable(:default_format) || :txt,
311
- content_types: namespace_stackable_with_hash(:content_types),
312
- formatters: namespace_stackable_with_hash(:formatters),
313
- parsers: namespace_stackable_with_hash(:parsers)
196
+ def run_validators(request:)
197
+ validators = inheritable_setting.route[:saved_validations]
198
+ return if validators.blank?
314
199
 
315
- builder = stack.build
316
- builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
317
- builder.to_app
200
+ validation_exceptions = nil
201
+
202
+ Grape::Validations::ParamScopeTracker.track do
203
+ instrument_run_validators(validators, request) do
204
+ validators.each do |validator|
205
+ validator.validate(request)
206
+ rescue Grape::Exceptions::Validation, Grape::Exceptions::ValidationArrayErrors => e
207
+ (validation_exceptions ||= []) << e
208
+ break if validator.fail_fast?
209
+ end
210
+ end
211
+ end
212
+
213
+ raise Grape::Exceptions::ValidationErrors.new(exceptions: validation_exceptions, headers: header) if validation_exceptions
318
214
  end
319
215
 
320
- def build_helpers
321
- helpers = namespace_stackable(:helpers)
322
- Module.new { helpers&.each { |mod_to_include| include mod_to_include } }
216
+ def run_filters(filters, type = :other)
217
+ return if filters.blank?
218
+
219
+ instrument_run_filters(filters, type) do
220
+ filters.each { |filter| instance_eval(&filter) }
221
+ end
323
222
  end
324
223
 
325
- private :build_stack, :build_helpers
224
+ attr_reader :befores, :before_validations, :after_validations, :afters, :finallies, :config
326
225
 
327
- def execute
328
- @block&.call(self)
226
+ def options?
227
+ options_route_enabled && env[Rack::REQUEST_METHOD] == Rack::OPTIONS
228
+ end
229
+
230
+ private
231
+
232
+ attr_reader :before_filter_passed
233
+
234
+ # Instrument helpers. Each guards on +listening?+ so that with no subscriber
235
+ # the payload Hash and notification machinery are skipped and the block runs
236
+ # directly (no added allocations); the block is forwarded anonymously so
237
+ # nothing is allocated unless a subscriber is present.
238
+ def instrument_run(&)
239
+ return yield unless ActiveSupport::Notifications.notifier.listening?('endpoint_run.grape')
240
+
241
+ ActiveSupport::Notifications.instrument('endpoint_run.grape', endpoint: self, env:, &)
329
242
  end
330
243
 
331
- def helpers
332
- lazy_initialize! && @helpers
244
+ def instrument_render(&)
245
+ return yield unless ActiveSupport::Notifications.notifier.listening?('endpoint_render.grape')
246
+
247
+ ActiveSupport::Notifications.instrument('endpoint_render.grape', endpoint: self, &)
333
248
  end
334
249
 
335
- def lazy_initialize!
336
- return true if @lazy_initialized
250
+ def instrument_run_validators(validators, request, &)
251
+ return yield unless ActiveSupport::Notifications.notifier.listening?('endpoint_run_validators.grape')
337
252
 
338
- @lazy_initialize_lock.synchronize do
339
- return true if @lazy_initialized
253
+ ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators:, request:, &)
254
+ end
340
255
 
341
- @helpers = build_helpers.tap { |mod| self.class.send(:include, mod) }
342
- @app = options[:app] || build_stack(@helpers)
256
+ def instrument_run_filters(filters, type, &)
257
+ return yield unless ActiveSupport::Notifications.notifier.listening?('endpoint_run_filters.grape')
343
258
 
344
- @lazy_initialized = true
345
- end
259
+ ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters:, type:, &)
346
260
  end
347
261
 
348
- def run_validators(validators, request)
349
- validation_errors = []
262
+ def compile!
263
+ @app = config.app || build_stack
264
+ warn_unauthenticated_mounted_app
265
+ @helpers = build_helpers
266
+ stackable = inheritable_setting.namespace_stackable
267
+ @befores = stackable[:befores]
268
+ @before_validations = stackable[:before_validations]
269
+ @after_validations = stackable[:after_validations]
270
+ @afters = stackable[:afters]
271
+ @finallies = stackable[:finallies]
272
+ @build_params_with = inheritable_setting.namespace_inheritable[:build_params_with]
273
+ end
350
274
 
351
- ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators: validators, request: request) do
352
- validators.each do |validator|
353
- validator.validate(request)
354
- rescue Grape::Exceptions::Validation => e
355
- validation_errors << e
356
- break if validator.fail_fast?
357
- rescue Grape::Exceptions::ValidationArrayErrors => e
358
- validation_errors.concat e.errors
359
- break if validator.fail_fast?
275
+ def to_routes
276
+ route_options = config.route_options
277
+ default_route_options = prepare_default_route_attributes(route_options)
278
+ complete_route_options = route_options.merge(default_route_options)
279
+ path_settings = prepare_default_path_settings
280
+
281
+ config.http_methods.flat_map do |method|
282
+ config.path.map do |path|
283
+ prepared_path = Path.new(path, default_route_options[:namespace], path_settings)
284
+ pattern = Grape::Router::Pattern.new(
285
+ origin: prepared_path.origin,
286
+ suffix: prepared_path.suffix,
287
+ anchor: default_route_options[:anchor],
288
+ params: route_options[:params],
289
+ format: config.format,
290
+ version: default_route_options[:version],
291
+ requirements: default_route_options[:requirements]
292
+ )
293
+ Grape::Router::Route.new(self, method, pattern, complete_route_options)
360
294
  end
361
295
  end
296
+ end
362
297
 
363
- validation_errors.any? && raise(Grape::Exceptions::ValidationErrors.new(errors: validation_errors, headers: header))
298
+ # True when a bare Rack app (anything that isn't a Grape app) is mounted at
299
+ # this endpoint. Such an app is called directly and matched by path prefix
300
+ # rather than an anchored route.
301
+ def bare_rack_app?
302
+ config.app && !config.app.is_a?(Grape::Mountable)
364
303
  end
365
304
 
366
- def run_filters(filters, type = :other)
367
- ActiveSupport::Notifications.instrument('endpoint_run_filters.grape', endpoint: self, filters: filters, type: type) do
368
- filters&.each { |filter| instance_eval(&filter) }
369
- end
370
- post_extension = DSL::InsideRoute.post_filter_methods(type)
371
- extend post_extension if post_extension
305
+ def prepare_default_route_attributes(route_options)
306
+ {
307
+ namespace:,
308
+ version: prepare_version(inheritable_setting.namespace_inheritable[:version]),
309
+ requirements: prepare_routes_requirements(route_options[:requirements]),
310
+ prefix: inheritable_setting.namespace_inheritable[:root_prefix],
311
+ anchor: route_options.fetch(:anchor, true),
312
+ settings: inheritable_setting.route.except(:declared_params, :saved_validations),
313
+ forward_match: config.forward_match
314
+ }
372
315
  end
373
316
 
374
- def befores
375
- namespace_stackable(:befores)
317
+ def prepare_default_path_settings
318
+ namespace_stackable_hash = inheritable_setting.namespace_stackable.to_hash
319
+ namespace_inheritable_hash = inheritable_setting.namespace_inheritable.to_hash
320
+ namespace_stackable_hash.merge!(namespace_inheritable_hash)
376
321
  end
377
322
 
378
- def before_validations
379
- namespace_stackable(:before_validations)
323
+ def prepare_routes_requirements(route_options_requirements)
324
+ namespace_requirements = inheritable_setting.namespace_stackable[:namespace].filter_map(&:requirements)
325
+ namespace_requirements << route_options_requirements if route_options_requirements.present?
326
+ namespace_requirements.reduce({}, :merge)
380
327
  end
381
328
 
382
- def after_validations
383
- namespace_stackable(:after_validations)
329
+ def prepare_version(namespace_inheritable_version)
330
+ return if namespace_inheritable_version.blank?
331
+
332
+ namespace_inheritable_version.length == 1 ? namespace_inheritable_version.first : namespace_inheritable_version
384
333
  end
385
334
 
386
- def afters
387
- namespace_stackable(:afters)
335
+ def build_stack
336
+ stack = Grape::Middleware::Stack.new
337
+
338
+ content_types = inheritable_setting.namespace_stackable_with_hash(:content_types)
339
+ format = inheritable_setting.namespace_inheritable[:format]
340
+
341
+ stack.use Rack::Head
342
+ stack.use Rack::Lint if lint?
343
+ stack.use Grape::Middleware::Error, **error_middleware_options(format, content_types)
344
+
345
+ stack.concat inheritable_setting.namespace_stackable[:middleware]
346
+
347
+ if inheritable_setting.namespace_inheritable[:version].present?
348
+ version_options = inheritable_setting.namespace_inheritable[:version_options]
349
+ stack.use Grape::Middleware::Versioner.using(version_options.using),
350
+ versions: inheritable_setting.namespace_inheritable[:version].flatten,
351
+ version_options:,
352
+ prefix: inheritable_setting.namespace_inheritable[:root_prefix],
353
+ mount_path: inheritable_setting.namespace_stackable[:mount_path].first
354
+ end
355
+
356
+ stack.use Grape::Middleware::Formatter,
357
+ format:,
358
+ default_format: inheritable_setting.namespace_inheritable[:default_format] || :txt,
359
+ content_types:,
360
+ formatters: inheritable_setting.namespace_stackable_with_hash(:formatters),
361
+ parsers: inheritable_setting.namespace_stackable_with_hash(:parsers)
362
+
363
+ builder = stack.build
364
+ builder.run ->(env) { env[Grape::Env::API_ENDPOINT].run }
365
+ builder.to_app
388
366
  end
389
367
 
390
- def finallies
391
- namespace_stackable(:finallies)
368
+ def error_middleware_options(format, content_types)
369
+ ns_inh = inheritable_setting.namespace_inheritable
370
+ ns_stack = inheritable_setting
371
+ {
372
+ format:,
373
+ content_types:,
374
+ default_status: ns_inh[:default_error_status],
375
+ rescue_all: ns_inh[:rescue_all],
376
+ rescue_grape_exceptions: ns_inh[:rescue_grape_exceptions],
377
+ default_error_formatter: ns_inh[:default_error_formatter],
378
+ error_formatters: ns_stack.namespace_stackable_with_hash(:error_formatters),
379
+ rescue_options: ns_stack.namespace_stackable[:rescue_options]&.last,
380
+ rescue_handlers: merged_reverse_stackable(:rescue_handlers),
381
+ base_only_rescue_handlers: merged_reverse_stackable(:base_only_rescue_handlers),
382
+ all_rescue_handler: ns_inh[:all_rescue_handler],
383
+ grape_exceptions_rescue_handler: ns_inh[:grape_exceptions_rescue_handler],
384
+ internal_grape_exceptions_rescue_handler: ns_inh[:internal_grape_exceptions_rescue_handler]
385
+ }
392
386
  end
393
387
 
394
- def validations
395
- return enum_for(:validations) unless block_given?
388
+ def build_helpers
389
+ helpers = inheritable_setting.namespace_stackable[:helpers]
390
+ return if helpers.empty?
391
+
392
+ Module.new { helpers.each { |mod_to_include| include mod_to_include } }
393
+ end
394
+
395
+ # A bare Rack app mounted with +mount+ is called directly (see +compile!+):
396
+ # it does not go through +build_stack+, so the API's authentication
397
+ # middleware never runs and the mount is reachable unauthenticated. Mounted
398
+ # Grape APIs are unaffected because they rebuild their own stack from the
399
+ # inherited settings. Warn so this bypass isn't silent.
400
+ def warn_unauthenticated_mounted_app
401
+ return unless bare_rack_app?
402
+ return unless inheritable_setting.namespace_inheritable[:auth]
403
+
404
+ warn "Grape: #{config.app} is mounted under an API that declares authentication, but authentication " \
405
+ 'middleware does not wrap mounted Rack applications. Requests to this mount are not authenticated by Grape.'
406
+ end
396
407
 
397
- route_setting(:saved_validations)&.each do |saved_validation|
398
- yield Grape::Validations::ValidatorFactory.create_validator(**saved_validation)
408
+ def build_response_cookies
409
+ return unless request.cookies?
410
+
411
+ response_cookies do |name, value|
412
+ cookie_value = value.is_a?(Hash) ? value : { value: }
413
+ Rack::Utils.set_cookie_header! header, name, cookie_value
399
414
  end
400
415
  end
401
416
 
402
- def options?
403
- options[:options_route_enabled] &&
404
- env[Grape::Http::Headers::REQUEST_METHOD] == Grape::Http::Headers::OPTIONS
417
+ def lint?
418
+ inheritable_setting.namespace_inheritable[:lint] || Grape.config.lint
419
+ end
420
+
421
+ # Merge a reverse-stackable handler map (as written by +rescue_from+) into a
422
+ # single Hash. The reverse store lists child-scope handlers before inherited
423
+ # ones, and the first-wins merge keeps the child's handler for a given
424
+ # class, so a nested +rescue_from+ overrides an outer one.
425
+ def merged_reverse_stackable(key)
426
+ handlers = inheritable_setting.namespace_reverse_stackable[key]
427
+ return if handlers.blank?
428
+
429
+ handlers.each_with_object({}) do |handler, result|
430
+ result.merge!(handler) { |_k, s1, _s2| s1 }
431
+ end
405
432
  end
406
433
  end
407
434
  end