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
@@ -3,228 +3,303 @@
3
3
  module Grape
4
4
  module DSL
5
5
  module Routing
6
- extend ActiveSupport::Concern
7
- include Grape::DSL::Configuration
8
-
9
- module ClassMethods
10
- attr_reader :endpoints
11
-
12
- # Specify an API version.
13
- #
14
- # @example API with legacy support.
15
- # class MyAPI < Grape::API
16
- # version 'v2'
17
- #
18
- # get '/main' do
19
- # {some: 'data'}
20
- # end
21
- #
22
- # version 'v1' do
23
- # get '/main' do
24
- # {legacy: 'data'}
25
- # end
26
- # end
27
- # end
28
- #
29
- def version(*args, &block)
30
- if args.any?
31
- options = args.extract_options!
32
- options = options.reverse_merge(using: :path)
33
- requested_versions = args.flatten
34
-
35
- raise Grape::Exceptions::MissingVendorOption.new if options[:using] == :header && !options.key?(:vendor)
36
-
37
- @versions = versions | requested_versions
38
-
39
- if block
40
- within_namespace do
41
- namespace_inheritable(:version, requested_versions)
42
- namespace_inheritable(:version_options, options)
43
-
44
- instance_eval(&block)
45
- end
46
- else
47
- namespace_inheritable(:version, requested_versions)
48
- namespace_inheritable(:version_options, options)
49
- end
50
- end
6
+ attr_reader :endpoints
51
7
 
52
- @versions.last if instance_variable_defined?(:@versions) && @versions
53
- end
8
+ def given(conditional_option, &)
9
+ return unless conditional_option
54
10
 
55
- # Define a root URL prefix for your entire API.
56
- def prefix(prefix = nil)
57
- namespace_inheritable(:root_prefix, prefix)
58
- end
11
+ mounted(&)
12
+ end
13
+
14
+ def mounted(&block)
15
+ evaluate_as_instance_with_configuration(block, lazy: true)
16
+ end
59
17
 
60
- # Create a scope without affecting the URL.
61
- #
62
- # @param _name [Symbol] Purely placebo, just allows to name the scope to
63
- # make the code more readable.
64
- def scope(_name = nil, &block)
18
+ def cascade(value = nil)
19
+ return inheritable_setting.namespace_inheritable.key?(:cascade) ? !inheritable_setting.namespace_inheritable[:cascade].nil? : true if value.nil?
20
+
21
+ inheritable_setting.namespace_inheritable[:cascade] = value
22
+ end
23
+
24
+ # Specify an API version.
25
+ #
26
+ # Called without arguments, returns the most recently declared version
27
+ # (or +nil+). Called with one or more version strings, registers them
28
+ # and stores a {Grape::DSL::VersionOptions} value object on the
29
+ # inheritable settings; when given a block, the registration applies
30
+ # within a nested namespace.
31
+ #
32
+ # @example API with legacy support.
33
+ # class MyAPI < Grape::API
34
+ # version 'v2'
35
+ #
36
+ # get '/main' do
37
+ # {some: 'data'}
38
+ # end
39
+ #
40
+ # version 'v1' do
41
+ # get '/main' do
42
+ # {legacy: 'data'}
43
+ # end
44
+ # end
45
+ # end
46
+ #
47
+ # @param args [Array<String, Symbol>] one or more version identifiers.
48
+ # @param using [Symbol] versioning strategy — one of +:path+ (default),
49
+ # +:header+, +:param+, or +:accept_version_header+.
50
+ # @param cascade [Boolean] forward to subsequent routes via the
51
+ # +X-Cascade+ header on version mismatch. Defaults to +true+.
52
+ # @param parameter [String] name of the query/body parameter that
53
+ # carries the version when +using: :param+. Defaults to +'apiver'+.
54
+ # @param strict [Boolean] reject requests that don't supply a usable
55
+ # version (header strategies). Defaults to +false+.
56
+ # @param vendor [String, nil] vendor segment for the +:header+
57
+ # strategy (+application/vnd.<vendor>-<version>+); required when
58
+ # +using: :header+.
59
+ # @yield optional block to scope routes under this version.
60
+ # @return [String, nil] the most recently declared version.
61
+ # @raise [Grape::Exceptions::MissingVendorOption] when +using: :header+
62
+ # is supplied without a +:vendor+.
63
+ def version(*args, using: :path, cascade: true, parameter: 'apiver', strict: false, vendor: nil, &block)
64
+ return @versions&.last if args.empty?
65
+
66
+ raise Grape::Exceptions::MissingVendorOption.new if using == :header && vendor.nil?
67
+
68
+ requested_versions = args.flatten.map(&:to_s)
69
+ options = VersionOptions.new(using:, cascade:, parameter:, strict:, vendor:)
70
+
71
+ @versions = versions | requested_versions
72
+
73
+ if block
65
74
  within_namespace do
66
- nest(block)
75
+ inheritable_setting.namespace_inheritable[:version] = requested_versions
76
+ inheritable_setting.namespace_inheritable[:version_options] = options
77
+ instance_eval(&block)
67
78
  end
79
+ else
80
+ inheritable_setting.namespace_inheritable[:version] = requested_versions
81
+ inheritable_setting.namespace_inheritable[:version_options] = options
68
82
  end
69
83
 
70
- # Do not route HEAD requests to GET requests automatically.
71
- def do_not_route_head!
72
- namespace_inheritable(:do_not_route_head, true)
73
- end
84
+ @versions&.last
85
+ end
74
86
 
75
- # Do not automatically route OPTIONS.
76
- def do_not_route_options!
77
- namespace_inheritable(:do_not_route_options, true)
78
- end
87
+ # Define a root URL prefix for your entire API.
88
+ def prefix(prefix = nil)
89
+ return inheritable_setting.namespace_inheritable[:root_prefix] if prefix.nil?
79
90
 
80
- def do_not_document!
81
- namespace_inheritable(:do_not_document, true)
91
+ inheritable_setting.namespace_inheritable[:root_prefix] = prefix.to_s
92
+ end
93
+
94
+ # Create a scope without affecting the URL.
95
+ #
96
+ # @param _name [Symbol] Purely placebo, just allows to name the scope to
97
+ # make the code more readable.
98
+ def scope(_name = nil, &block)
99
+ within_namespace do
100
+ nest(block)
82
101
  end
102
+ end
83
103
 
84
- def mount(mounts, *opts)
85
- mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
86
- mounts.each_pair do |app, path|
87
- if app.respond_to?(:mount_instance)
88
- opts_with = opts.any? ? opts.shift[:with] : {}
89
- mount({ app.mount_instance(configuration: opts_with) => path })
90
- next
91
- end
92
- in_setting = inheritable_setting
104
+ def build_with(build_with)
105
+ inheritable_setting.namespace_inheritable[:build_params_with] = build_with
106
+ end
93
107
 
94
- if app.respond_to?(:inheritable_setting, true)
95
- mount_path = Grape::Router.normalize_path(path)
96
- app.top_level_setting.namespace_stackable[:mount_path] = mount_path
108
+ # Do not route HEAD requests to GET requests automatically.
109
+ def do_not_route_head!
110
+ inheritable_setting.namespace_inheritable[:do_not_route_head] = true
111
+ end
97
112
 
98
- app.inherit_settings(inheritable_setting)
113
+ # Do not automatically route OPTIONS.
114
+ def do_not_route_options!
115
+ inheritable_setting.namespace_inheritable[:do_not_route_options] = true
116
+ end
99
117
 
100
- in_setting = app.top_level_setting
118
+ def lint!
119
+ inheritable_setting.namespace_inheritable[:lint] = true
120
+ end
101
121
 
102
- app.change!
103
- change!
104
- end
122
+ def do_not_document!
123
+ inheritable_setting.namespace_inheritable[:do_not_document] = true
124
+ end
105
125
 
106
- endpoints << Grape::Endpoint.new(
107
- in_setting,
108
- method: :any,
109
- path: path,
110
- app: app,
111
- route_options: { anchor: false },
112
- forward_match: !app.respond_to?(:inheritable_setting),
113
- for: self
114
- )
126
+ def mount(mounts, *opts)
127
+ mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
128
+ mounts.each_pair do |app, path|
129
+ if app.respond_to?(:mount_instance)
130
+ opts_with = opts.any? ? opts.first[:with] : {}
131
+ mount({ app.mount_instance(configuration: opts_with) => path }, *opts)
132
+ next
115
133
  end
116
- end
134
+ in_setting = inheritable_setting
117
135
 
118
- # Defines a route that will be recognized
119
- # by the Grape API.
120
- #
121
- # @param methods [HTTP Verb] One or more HTTP verbs that are accepted by this route. Set to `:any` if you want any verb to be accepted.
122
- # @param paths [String] One or more strings representing the URL segment(s) for this route.
123
- #
124
- # @example Defining a basic route.
125
- # class MyAPI < Grape::API
126
- # route(:any, '/hello') do
127
- # {hello: 'world'}
128
- # end
129
- # end
130
- def route(methods, paths = ['/'], route_options = {}, &block)
131
- methods = '*' if methods == :any
132
- endpoint_options = {
133
- method: methods,
134
- path: paths,
135
- for: self,
136
- route_options: {
137
- params: namespace_stackable_with_hash(:params) || {}
138
- }.deep_merge(route_setting(:description) || {}).deep_merge(route_options || {})
139
- }
140
-
141
- new_endpoint = Grape::Endpoint.new(inheritable_setting, endpoint_options, &block)
142
- endpoints << new_endpoint unless endpoints.any? { |e| e.equals?(new_endpoint) }
143
-
144
- route_end
145
- reset_validations!
146
- end
136
+ # Past the mount_instance branch above, a Grape app here is an already
137
+ # instantiated Grape::API::Instance (vs. a bare Rack app).
138
+ if app.is_a?(Grape::Mountable)
139
+ mount_path = Grape::Util::PathNormalizer.call(path)
140
+ app.top_level_setting.namespace_stackable[:mount_path] = mount_path
141
+
142
+ app.inherit_settings(inheritable_setting)
147
143
 
148
- Grape::Http::Headers::SUPPORTED_METHODS.each do |supported_method|
149
- define_method supported_method.downcase do |*args, &block|
150
- options = args.extract_options!
151
- paths = args.first || ['/']
152
- route(supported_method, paths, options, &block)
144
+ in_setting = app.top_level_setting
145
+
146
+ app.change!
147
+ change!
153
148
  end
154
- end
155
149
 
156
- # Declare a "namespace", which prefixes all subordinate routes with its
157
- # name. Any endpoints within a namespace, group, resource or segment,
158
- # etc., will share their parent context as well as any configuration
159
- # done in the namespace context.
160
- #
161
- # @example
162
- #
163
- # namespace :foo do
164
- # get 'bar' do
165
- # # defines the endpoint: GET /foo/bar
166
- # end
167
- # end
168
- def namespace(space = nil, options = {}, &block)
169
- @namespace_description = nil unless instance_variable_defined?(:@namespace_description) && @namespace_description
170
-
171
- if space || block
172
- within_namespace do
173
- previous_namespace_description = @namespace_description
174
- @namespace_description = (@namespace_description || {}).deep_merge(namespace_setting(:description) || {})
175
- nest(block) do
176
- namespace_stackable(:namespace, Namespace.new(space, **options)) if space
177
- end
178
- @namespace_description = previous_namespace_description
150
+ # When trying to mount multiple times the same endpoint, remove the previous ones
151
+ # from the list of endpoints if refresh_already_mounted parameter is true
152
+ refresh_already_mounted = opts.any? ? opts.first[:refresh_already_mounted] : false
153
+ if refresh_already_mounted && !endpoints.empty?
154
+ endpoints.delete_if do |endpoint|
155
+ endpoint.options[:app].to_s == app.to_s
179
156
  end
180
- else
181
- Namespace.joined_space_path(namespace_stackable(:namespace))
182
157
  end
158
+
159
+ endpoints << Grape::Endpoint.new(
160
+ in_setting,
161
+ method: :any,
162
+ path:,
163
+ app:,
164
+ route_options: { anchor: false },
165
+ forward_match: !app.respond_to?(:inheritable_setting),
166
+ for: self
167
+ )
183
168
  end
169
+ end
184
170
 
185
- alias group namespace
186
- alias resource namespace
187
- alias resources namespace
188
- alias segment namespace
171
+ # Defines a route that will be recognized
172
+ # by the Grape API.
173
+ #
174
+ # @param methods [HTTP Verb] One or more HTTP verbs that are accepted by this route. Set to `:any` if you want any verb to be accepted.
175
+ # @param paths [String] One or more strings representing the URL segment(s) for this route.
176
+ #
177
+ # @example Defining a basic route.
178
+ # class MyAPI < Grape::API
179
+ # route(:any, '/hello') do
180
+ # {hello: 'world'}
181
+ # end
182
+ # end
183
+ def route(methods, paths = ['/'], route_options = {}, &)
184
+ method = methods == :any ? '*' : methods
185
+ endpoint_params = inheritable_setting.namespace_stackable_with_hash(:params) || {}
186
+ endpoint_description = inheritable_setting.route[:description]
187
+ all_route_options = { params: endpoint_params }
188
+ all_route_options.deep_merge!(endpoint_description) if endpoint_description
189
+ all_route_options.deep_merge!(route_options) if route_options.present?
190
+
191
+ new_endpoint = Grape::Endpoint.new(
192
+ inheritable_setting,
193
+ method:,
194
+ path: paths,
195
+ for: self,
196
+ route_options: all_route_options,
197
+ &
198
+ )
199
+ endpoints << new_endpoint unless endpoints.include?(new_endpoint)
200
+
201
+ inheritable_setting.route_end
202
+ reset_validations!
203
+ end
189
204
 
190
- # An array of API routes.
191
- def routes
192
- @routes ||= prepare_routes
205
+ Grape::HTTP_SUPPORTED_METHODS.each do |supported_method|
206
+ define_method supported_method.downcase do |path = '/', **options, &block|
207
+ route(supported_method, path, options, &block)
193
208
  end
209
+ end
194
210
 
195
- # Remove all defined routes.
196
- def reset_routes!
197
- endpoints.each(&:reset_routes!)
198
- @routes = nil
211
+ # Declare a "namespace", which prefixes all subordinate routes with its
212
+ # name. Any endpoints within a namespace, group, resource or segment,
213
+ # etc., will share their parent context as well as any configuration
214
+ # done in the namespace context.
215
+ #
216
+ # @example
217
+ #
218
+ # namespace :foo do
219
+ # get 'bar' do
220
+ # # defines the endpoint: GET /foo/bar
221
+ # end
222
+ # end
223
+ def namespace(space = nil, requirements: nil, **options, &block)
224
+ return Namespace.joined_space_path(inheritable_setting.namespace_stackable[:namespace]) unless space || block
225
+
226
+ within_namespace do
227
+ nest(block) do
228
+ inheritable_setting.namespace_stackable[:namespace] = Grape::Namespace.new(space, requirements:, **options) if space
229
+ end
199
230
  end
231
+ end
200
232
 
201
- def reset_endpoints!
202
- @endpoints = []
203
- end
233
+ alias group namespace
234
+ alias resource namespace
235
+ alias resources namespace
236
+ alias segment namespace
204
237
 
205
- # This method allows you to quickly define a parameter route segment
206
- # in your API.
207
- #
208
- # @param param [Symbol] The name of the parameter you wish to declare.
209
- # @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
210
- def route_param(param, options = {}, &block)
211
- options = options.dup
238
+ # An array of API routes.
239
+ def routes
240
+ @routes ||= endpoints.map(&:routes).flatten
241
+ end
212
242
 
213
- options[:requirements] = {
214
- param.to_sym => options[:requirements]
215
- } if options[:requirements].is_a?(Regexp)
243
+ # This method allows you to quickly define a parameter route segment
244
+ # in your API.
245
+ #
246
+ # @param param [Symbol] The name of the parameter you wish to declare.
247
+ # @option options [Regexp] You may supply a regular expression that the declared parameter must meet.
248
+ def route_param(param, requirements: nil, type: nil, **, &)
249
+ requirements = { param.to_sym => requirements } if requirements.is_a?(Regexp)
216
250
 
217
- Grape::Validations::ParamsScope.new(api: self) do
218
- requires param, type: options[:type]
219
- end if options.key?(:type)
251
+ Grape::Validations::ParamsScope.new(api: self) do
252
+ requires param, type: type
253
+ end if type
220
254
 
221
- namespace(":#{param}", options, &block)
222
- end
255
+ namespace(":#{param}", requirements:, **, &)
256
+ end
223
257
 
224
- # @return array of defined versions
225
- def versions
226
- @versions ||= []
258
+ # @return array of defined versions
259
+ def versions
260
+ @versions ||= []
261
+ end
262
+
263
+ private
264
+
265
+ # Remove all defined routes.
266
+ def reset_routes!
267
+ endpoints.each(&:reset_routes!)
268
+ @routes = nil
269
+ end
270
+
271
+ def reset_endpoints!
272
+ @endpoints = []
273
+ end
274
+
275
+ def refresh_mounted_api(mounts, *opts)
276
+ opts << { refresh_already_mounted: true }
277
+ mount(mounts, *opts)
278
+ end
279
+
280
+ # Execute first the provided block, then each of the
281
+ # block passed in. Allows for simple 'before' setups
282
+ # of settings stack pushes.
283
+ def nest(*blocks, &block)
284
+ blocks.compact!
285
+ return instance_eval(&block) if blocks.empty?
286
+
287
+ evaluate_as_instance_with_configuration(block) if block
288
+ blocks.each { |b| evaluate_as_instance_with_configuration(b) }
289
+ reset_validations!
290
+ end
291
+
292
+ def evaluate_as_instance_with_configuration(block, lazy: false)
293
+ lazy_block = Grape::Util::Lazy::Block.new do |configuration|
294
+ value_for_configuration = configuration
295
+ self.configuration = value_for_configuration.evaluate if value_for_configuration.is_a?(Grape::Util::Lazy::Base)
296
+ response = instance_eval(&block)
297
+ self.configuration = value_for_configuration
298
+ response
227
299
  end
300
+ return lazy_block if @base && base_instance? && lazy
301
+
302
+ lazy_block.evaluate_from(configuration)
228
303
  end
229
304
  end
230
305
  end