grape 1.1.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (306) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +370 -44
  3. data/CONTRIBUTING.md +32 -1
  4. data/LICENSE +1 -1
  5. data/README.md +683 -87
  6. data/UPGRADING.md +481 -17
  7. data/grape.gemspec +15 -4
  8. data/lib/grape/api/helpers.rb +2 -0
  9. data/lib/grape/api/instance.rb +279 -0
  10. data/lib/grape/api.rb +144 -176
  11. data/lib/grape/config.rb +34 -0
  12. data/lib/grape/content_types.rb +34 -0
  13. data/lib/grape/cookies.rb +4 -0
  14. data/lib/grape/dry_types.rb +12 -0
  15. data/lib/grape/dsl/api.rb +1 -1
  16. data/lib/grape/dsl/callbacks.rb +21 -1
  17. data/lib/grape/dsl/configuration.rb +1 -1
  18. data/lib/grape/dsl/desc.rb +41 -23
  19. data/lib/grape/dsl/headers.rb +7 -2
  20. data/lib/grape/dsl/helpers.rb +10 -7
  21. data/lib/grape/dsl/inside_route.rb +118 -62
  22. data/lib/grape/dsl/logger.rb +2 -0
  23. data/lib/grape/dsl/middleware.rb +11 -4
  24. data/lib/grape/dsl/parameters.rb +33 -19
  25. data/lib/grape/dsl/request_response.rb +12 -9
  26. data/lib/grape/dsl/routing.rb +22 -13
  27. data/lib/grape/dsl/settings.rb +10 -6
  28. data/lib/grape/dsl/validations.rb +19 -14
  29. data/lib/grape/eager_load.rb +20 -0
  30. data/lib/grape/endpoint.rb +67 -58
  31. data/lib/grape/error_formatter/base.rb +2 -0
  32. data/lib/grape/error_formatter/json.rb +11 -7
  33. data/lib/grape/error_formatter/txt.rb +2 -0
  34. data/lib/grape/error_formatter/xml.rb +4 -6
  35. data/lib/grape/error_formatter.rb +4 -2
  36. data/lib/grape/exceptions/base.rb +23 -16
  37. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  38. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  39. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  40. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  41. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  42. data/lib/grape/exceptions/invalid_response.rb +11 -0
  43. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  44. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  45. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  46. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  47. data/lib/grape/exceptions/missing_group_type.rb +10 -1
  48. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  49. data/lib/grape/exceptions/missing_option.rb +2 -0
  50. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  51. data/lib/grape/exceptions/too_many_multipart_files.rb +11 -0
  52. data/lib/grape/exceptions/unknown_options.rb +2 -0
  53. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  54. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  55. data/lib/grape/exceptions/unsupported_group_type.rb +10 -1
  56. data/lib/grape/exceptions/validation.rb +5 -8
  57. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  58. data/lib/grape/exceptions/validation_errors.rb +16 -13
  59. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  60. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  61. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  62. data/lib/grape/extensions/hash.rb +2 -0
  63. data/lib/grape/extensions/hashie/mash.rb +2 -0
  64. data/lib/grape/formatter/json.rb +3 -0
  65. data/lib/grape/formatter/serializable_hash.rb +4 -1
  66. data/lib/grape/formatter/txt.rb +2 -0
  67. data/lib/grape/formatter/xml.rb +3 -0
  68. data/lib/grape/formatter.rb +5 -3
  69. data/lib/grape/http/headers.rb +50 -18
  70. data/lib/grape/locale/en.yml +11 -8
  71. data/lib/grape/middleware/auth/base.rb +7 -7
  72. data/lib/grape/middleware/auth/dsl.rb +9 -2
  73. data/lib/grape/middleware/auth/strategies.rb +2 -0
  74. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  75. data/lib/grape/middleware/base.rb +13 -8
  76. data/lib/grape/middleware/error.rb +22 -17
  77. data/lib/grape/middleware/filter.rb +2 -0
  78. data/lib/grape/middleware/formatter.rb +12 -10
  79. data/lib/grape/middleware/globals.rb +2 -0
  80. data/lib/grape/middleware/helpers.rb +12 -0
  81. data/lib/grape/middleware/stack.rb +16 -6
  82. data/lib/grape/middleware/versioner/accept_version_header.rb +5 -5
  83. data/lib/grape/middleware/versioner/header.rb +13 -9
  84. data/lib/grape/middleware/versioner/param.rb +4 -1
  85. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +5 -1
  86. data/lib/grape/middleware/versioner/path.rb +5 -1
  87. data/lib/grape/middleware/versioner.rb +2 -0
  88. data/lib/grape/namespace.rb +14 -2
  89. data/lib/grape/parser/json.rb +3 -1
  90. data/lib/grape/parser/xml.rb +3 -1
  91. data/lib/grape/parser.rb +4 -2
  92. data/lib/grape/path.rb +16 -3
  93. data/lib/grape/presenters/presenter.rb +2 -0
  94. data/lib/grape/request.rb +21 -9
  95. data/lib/grape/router/attribute_translator.rb +41 -8
  96. data/lib/grape/router/pattern.rb +21 -17
  97. data/lib/grape/router/route.rb +15 -29
  98. data/lib/grape/router.rb +36 -29
  99. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  100. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  101. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  102. data/lib/grape/types/invalid_value.rb +8 -0
  103. data/lib/grape/util/base_inheritable.rb +43 -0
  104. data/lib/grape/util/cache.rb +20 -0
  105. data/lib/grape/util/endpoint_configuration.rb +8 -0
  106. data/lib/grape/util/env.rb +19 -17
  107. data/lib/grape/util/inheritable_setting.rb +3 -3
  108. data/lib/grape/util/inheritable_values.rb +7 -25
  109. data/lib/grape/util/json.rb +4 -0
  110. data/lib/grape/util/lazy_block.rb +27 -0
  111. data/lib/grape/util/lazy_object.rb +43 -0
  112. data/lib/grape/util/lazy_value.rb +99 -0
  113. data/lib/grape/util/registrable.rb +2 -0
  114. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  115. data/lib/grape/util/stackable_values.rb +21 -34
  116. data/lib/grape/util/strict_hash_configuration.rb +3 -1
  117. data/lib/grape/util/xml.rb +2 -0
  118. data/lib/grape/validations/attributes_doc.rb +58 -0
  119. data/lib/grape/validations/attributes_iterator.rb +16 -6
  120. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  121. data/lib/grape/validations/params_scope.rb +174 -94
  122. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  123. data/lib/grape/validations/types/array_coercer.rb +63 -0
  124. data/lib/grape/validations/types/build_coercer.rb +47 -49
  125. data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
  126. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  127. data/lib/grape/validations/types/dry_type_coercer.rb +72 -0
  128. data/lib/grape/validations/types/file.rb +22 -18
  129. data/lib/grape/validations/types/invalid_value.rb +17 -0
  130. data/lib/grape/validations/types/json.rb +47 -39
  131. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  132. data/lib/grape/validations/types/primitive_coercer.rb +75 -0
  133. data/lib/grape/validations/types/set_coercer.rb +38 -0
  134. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  135. data/lib/grape/validations/types.rb +106 -63
  136. data/lib/grape/validations/validator_factory.rb +8 -11
  137. data/lib/grape/validations/validators/all_or_none_of_validator.rb +16 -0
  138. data/lib/grape/validations/validators/allow_blank_validator.rb +20 -0
  139. data/lib/grape/validations/validators/as_validator.rb +14 -0
  140. data/lib/grape/validations/validators/at_least_one_of_validator.rb +15 -0
  141. data/lib/grape/validations/validators/base.rb +84 -68
  142. data/lib/grape/validations/validators/coerce_validator.rb +75 -0
  143. data/lib/grape/validations/validators/default_validator.rb +51 -0
  144. data/lib/grape/validations/validators/exactly_one_of_validator.rb +17 -0
  145. data/lib/grape/validations/validators/except_values_validator.rb +24 -0
  146. data/lib/grape/validations/validators/multiple_params_base.rb +27 -16
  147. data/lib/grape/validations/validators/mutual_exclusion_validator.rb +16 -0
  148. data/lib/grape/validations/validators/presence_validator.rb +15 -0
  149. data/lib/grape/validations/validators/regexp_validator.rb +16 -0
  150. data/lib/grape/validations/validators/same_as_validator.rb +29 -0
  151. data/lib/grape/validations/validators/values_validator.rb +88 -0
  152. data/lib/grape/validations.rb +18 -6
  153. data/lib/grape/version.rb +3 -1
  154. data/lib/grape.rb +175 -94
  155. data/spec/grape/api/custom_validations_spec.rb +117 -44
  156. data/spec/grape/api/deeply_included_options_spec.rb +4 -4
  157. data/spec/grape/api/defines_boolean_in_params_spec.rb +38 -0
  158. data/spec/grape/api/documentation_spec.rb +59 -0
  159. data/spec/grape/api/inherited_helpers_spec.rb +1 -1
  160. data/spec/grape/api/instance_spec.rb +103 -0
  161. data/spec/grape/api/invalid_format_spec.rb +3 -1
  162. data/spec/grape/api/namespace_parameters_in_route_spec.rb +1 -1
  163. data/spec/grape/api/nested_helpers_spec.rb +1 -1
  164. data/spec/grape/api/optional_parameters_in_route_spec.rb +1 -1
  165. data/spec/grape/api/parameters_modification_spec.rb +2 -2
  166. data/spec/grape/api/patch_method_helpers_spec.rb +1 -1
  167. data/spec/grape/api/recognize_path_spec.rb +2 -2
  168. data/spec/grape/api/required_parameters_in_route_spec.rb +1 -1
  169. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +1 -1
  170. data/spec/grape/api/routes_with_requirements_spec.rb +59 -0
  171. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +10 -16
  172. data/spec/grape/api/shared_helpers_spec.rb +1 -1
  173. data/spec/grape/api_remount_spec.rb +473 -0
  174. data/spec/grape/api_spec.rb +995 -231
  175. data/spec/grape/config_spec.rb +17 -0
  176. data/spec/grape/dsl/callbacks_spec.rb +3 -2
  177. data/spec/grape/dsl/desc_spec.rb +43 -17
  178. data/spec/grape/dsl/headers_spec.rb +40 -10
  179. data/spec/grape/dsl/helpers_spec.rb +6 -5
  180. data/spec/grape/dsl/inside_route_spec.rb +189 -38
  181. data/spec/grape/dsl/logger_spec.rb +17 -19
  182. data/spec/grape/dsl/middleware_spec.rb +11 -2
  183. data/spec/grape/dsl/parameters_spec.rb +3 -1
  184. data/spec/grape/dsl/request_response_spec.rb +8 -7
  185. data/spec/grape/dsl/routing_spec.rb +22 -9
  186. data/spec/grape/dsl/settings_spec.rb +1 -1
  187. data/spec/grape/dsl/validations_spec.rb +1 -16
  188. data/spec/grape/endpoint/declared_spec.rb +846 -0
  189. data/spec/grape/endpoint_spec.rb +136 -577
  190. data/spec/grape/entity_spec.rb +31 -24
  191. data/spec/grape/exceptions/base_spec.rb +81 -0
  192. data/spec/grape/exceptions/body_parse_errors_spec.rb +4 -1
  193. data/spec/grape/exceptions/invalid_accept_header_spec.rb +65 -23
  194. data/spec/grape/exceptions/invalid_formatter_spec.rb +1 -1
  195. data/spec/grape/exceptions/invalid_response_spec.rb +11 -0
  196. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -2
  197. data/spec/grape/exceptions/missing_group_type_spec.rb +21 -0
  198. data/spec/grape/exceptions/missing_mime_type_spec.rb +1 -1
  199. data/spec/grape/exceptions/missing_option_spec.rb +2 -2
  200. data/spec/grape/exceptions/unknown_options_spec.rb +1 -1
  201. data/spec/grape/exceptions/unknown_validator_spec.rb +1 -1
  202. data/spec/grape/exceptions/unsupported_group_type_spec.rb +23 -0
  203. data/spec/grape/exceptions/validation_errors_spec.rb +21 -15
  204. data/spec/grape/exceptions/validation_spec.rb +6 -4
  205. data/spec/grape/extensions/param_builders/hash_spec.rb +8 -8
  206. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +9 -9
  207. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +9 -9
  208. data/spec/grape/integration/global_namespace_function_spec.rb +2 -2
  209. data/spec/grape/integration/rack_sendfile_spec.rb +14 -10
  210. data/spec/grape/integration/rack_spec.rb +25 -8
  211. data/spec/grape/loading_spec.rb +9 -9
  212. data/spec/grape/middleware/auth/base_spec.rb +2 -1
  213. data/spec/grape/middleware/auth/dsl_spec.rb +19 -10
  214. data/spec/grape/middleware/auth/strategies_spec.rb +62 -22
  215. data/spec/grape/middleware/base_spec.rb +36 -17
  216. data/spec/grape/middleware/error_spec.rb +11 -4
  217. data/spec/grape/middleware/exception_spec.rb +112 -162
  218. data/spec/grape/middleware/formatter_spec.rb +65 -29
  219. data/spec/grape/middleware/globals_spec.rb +8 -5
  220. data/spec/grape/middleware/stack_spec.rb +25 -13
  221. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -2
  222. data/spec/grape/middleware/versioner/header_spec.rb +37 -14
  223. data/spec/grape/middleware/versioner/param_spec.rb +8 -2
  224. data/spec/grape/middleware/versioner/path_spec.rb +6 -2
  225. data/spec/grape/middleware/versioner_spec.rb +2 -2
  226. data/spec/grape/named_api_spec.rb +19 -0
  227. data/spec/grape/parser_spec.rb +10 -6
  228. data/spec/grape/path_spec.rb +53 -53
  229. data/spec/grape/presenters/presenter_spec.rb +8 -7
  230. data/spec/grape/request_spec.rb +29 -3
  231. data/spec/grape/util/inheritable_setting_spec.rb +9 -8
  232. data/spec/grape/util/inheritable_values_spec.rb +5 -3
  233. data/spec/grape/util/reverse_stackable_values_spec.rb +5 -2
  234. data/spec/grape/util/stackable_values_spec.rb +10 -7
  235. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -1
  236. data/spec/grape/validations/attributes_doc_spec.rb +153 -0
  237. data/spec/grape/validations/instance_behaivour_spec.rb +13 -14
  238. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +40 -0
  239. data/spec/grape/validations/params_scope_spec.rb +568 -99
  240. data/spec/grape/validations/single_attribute_iterator_spec.rb +57 -0
  241. data/spec/grape/validations/types/array_coercer_spec.rb +33 -0
  242. data/spec/grape/validations/types/primitive_coercer_spec.rb +150 -0
  243. data/spec/grape/validations/types/set_coercer_spec.rb +32 -0
  244. data/spec/grape/validations/types_spec.rb +44 -45
  245. data/spec/grape/validations/validators/all_or_none_spec.rb +134 -32
  246. data/spec/grape/validations/validators/allow_blank_spec.rb +137 -141
  247. data/spec/grape/validations/validators/at_least_one_of_spec.rb +169 -31
  248. data/spec/grape/validations/validators/coerce_spec.rb +491 -151
  249. data/spec/grape/validations/validators/default_spec.rb +242 -78
  250. data/spec/grape/validations/validators/exactly_one_of_spec.rb +198 -40
  251. data/spec/grape/validations/validators/except_values_spec.rb +6 -5
  252. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +181 -30
  253. data/spec/grape/validations/validators/presence_spec.rb +45 -2
  254. data/spec/grape/validations/validators/regexp_spec.rb +27 -33
  255. data/spec/grape/validations/validators/same_as_spec.rb +57 -0
  256. data/spec/grape/validations/validators/values_spec.rb +227 -180
  257. data/spec/grape/validations_spec.rb +502 -72
  258. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  259. data/spec/integration/multi_json/json_spec.rb +2 -2
  260. data/spec/integration/multi_xml/xml_spec.rb +2 -2
  261. data/spec/shared/versioning_examples.rb +34 -29
  262. data/spec/spec_helper.rb +31 -5
  263. data/spec/support/basic_auth_encode_helpers.rb +3 -1
  264. data/spec/support/chunks.rb +14 -0
  265. data/spec/support/content_type_helpers.rb +2 -0
  266. data/spec/support/endpoint_faker.rb +2 -0
  267. data/spec/support/file_streamer.rb +2 -0
  268. data/spec/support/integer_helpers.rb +2 -0
  269. data/spec/support/versioned_helpers.rb +8 -8
  270. metadata +111 -61
  271. data/Appraisals +0 -32
  272. data/Dangerfile +0 -2
  273. data/Gemfile +0 -33
  274. data/Gemfile.lock +0 -231
  275. data/Guardfile +0 -10
  276. data/RELEASING.md +0 -111
  277. data/Rakefile +0 -25
  278. data/benchmark/simple.rb +0 -27
  279. data/benchmark/simple_with_type_coercer.rb +0 -22
  280. data/gemfiles/multi_json.gemfile +0 -35
  281. data/gemfiles/multi_xml.gemfile +0 -35
  282. data/gemfiles/rack_1.5.2.gemfile +0 -35
  283. data/gemfiles/rack_edge.gemfile +0 -35
  284. data/gemfiles/rails_3.gemfile +0 -36
  285. data/gemfiles/rails_4.gemfile +0 -35
  286. data/gemfiles/rails_5.gemfile +0 -35
  287. data/gemfiles/rails_edge.gemfile +0 -35
  288. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  289. data/lib/grape/util/content_types.rb +0 -26
  290. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
  291. data/lib/grape/validations/validators/all_or_none.rb +0 -20
  292. data/lib/grape/validations/validators/allow_blank.rb +0 -16
  293. data/lib/grape/validations/validators/as.rb +0 -15
  294. data/lib/grape/validations/validators/at_least_one_of.rb +0 -20
  295. data/lib/grape/validations/validators/coerce.rb +0 -74
  296. data/lib/grape/validations/validators/default.rb +0 -48
  297. data/lib/grape/validations/validators/exactly_one_of.rb +0 -29
  298. data/lib/grape/validations/validators/except_values.rb +0 -20
  299. data/lib/grape/validations/validators/mutual_exclusion.rb +0 -25
  300. data/lib/grape/validations/validators/presence.rb +0 -10
  301. data/lib/grape/validations/validators/regexp.rb +0 -11
  302. data/lib/grape/validations/validators/values.rb +0 -71
  303. data/pkg/grape-0.17.0.gem +0 -0
  304. data/pkg/grape-0.19.0.gem +0 -0
  305. data/spec/grape/dsl/configuration_spec.rb +0 -14
  306. data/spec/grape/validations/attributes_iterator_spec.rb +0 -4
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Grape
4
+ module Config
5
+ class Configuration
6
+ ATTRIBUTES = %i[
7
+ param_builder
8
+ ].freeze
9
+
10
+ attr_accessor(*ATTRIBUTES)
11
+
12
+ def initialize
13
+ reset
14
+ end
15
+
16
+ def reset
17
+ self.param_builder = Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
18
+ end
19
+ end
20
+
21
+ def self.extended(base)
22
+ def base.configure
23
+ block_given? ? yield(config) : config
24
+ end
25
+
26
+ def base.config
27
+ @configuration ||= Grape::Config::Configuration.new
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Grape.extend Grape::Config
34
+ Grape.config.reset
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'grape/util/registrable'
4
+
5
+ module Grape
6
+ module ContentTypes
7
+ extend Util::Registrable
8
+
9
+ # Content types are listed in order of preference.
10
+ CONTENT_TYPES = {
11
+ xml: 'application/xml',
12
+ serializable_hash: 'application/json',
13
+ json: 'application/json',
14
+ binary: 'application/octet-stream',
15
+ txt: 'text/plain'
16
+ }.freeze
17
+
18
+ class << self
19
+ def content_types_for_settings(settings)
20
+ return if settings.blank?
21
+
22
+ settings.each_with_object({}) { |value, result| result.merge!(value) }
23
+ end
24
+
25
+ def content_types_for(from_settings)
26
+ if from_settings.present?
27
+ from_settings
28
+ else
29
+ Grape::ContentTypes::CONTENT_TYPES.merge(default_elements)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/grape/cookies.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  class Cookies
3
5
  def initialize
@@ -31,9 +33,11 @@ module Grape
31
33
  @cookies.each(&block)
32
34
  end
33
35
 
36
+ # rubocop:disable Layout/SpaceBeforeBrackets
34
37
  def delete(name, **opts)
35
38
  options = opts.merge(value: 'deleted', expires: Time.at(0))
36
39
  self.[]=(name, options)
37
40
  end
41
+ # rubocop:enable Layout/SpaceBeforeBrackets
38
42
  end
39
43
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry-types'
4
+
5
+ module Grape
6
+ module DryTypes
7
+ # Call +Dry.Types()+ to add all registered types to +DryTypes+ which is
8
+ # a container in this case. Check documentation for more information
9
+ # https://dry-rb.org/gems/dry-types/1.2/getting-started/
10
+ include Dry.Types()
11
+ end
12
+ end
data/lib/grape/dsl/api.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'active_support/concern'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Grape
4
4
  module DSL
@@ -1,4 +1,4 @@
1
- require 'active_support/concern'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Grape
4
4
  module DSL
@@ -43,6 +43,26 @@ module Grape
43
43
  def after(&block)
44
44
  namespace_stackable(:afters, block)
45
45
  end
46
+
47
+ # Allows you to specify a something that will always be executed after a call
48
+ # API call. Unlike the `after` block, this code will run even on
49
+ # unsuccesful requests.
50
+ # @example
51
+ # class ExampleAPI < Grape::API
52
+ # before do
53
+ # ApiLogger.start
54
+ # end
55
+ # finally do
56
+ # ApiLogger.close
57
+ # end
58
+ # end
59
+ #
60
+ # This will make sure that the ApiLogger is opened and closed around every
61
+ # request
62
+ # @param ensured_block [Proc] The block to be executed after every api_call
63
+ def finally(&block)
64
+ namespace_stackable(:finallies, block)
65
+ end
46
66
  end
47
67
  end
48
68
  end
@@ -1,4 +1,4 @@
1
- require 'active_support/concern'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Grape
4
4
  module DSL
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module DSL
3
5
  module Desc
@@ -9,6 +11,7 @@ module Grape
9
11
  # @param options [Hash] other properties you can set to describe the
10
12
  # endpoint or namespace. Optional.
11
13
  # @option options :detail [String] additional detail about this endpoint
14
+ # @option options :summary [String] summary for this endpoint
12
15
  # @option options :params [Hash] param types and info. normally, you set
13
16
  # these via the `params` dsl method.
14
17
  # @option options :entity [Grape::Entity] the entity returned upon a
@@ -16,7 +19,16 @@ module Grape
16
19
  # @option options :http_codes [Array[Array]] possible HTTP codes this
17
20
  # endpoint may return, with their meanings, in a 2d array
18
21
  # @option options :named [String] a specific name to help find this route
22
+ # @option options :body_name [String] override the autogenerated body name param
19
23
  # @option options :headers [Hash] HTTP headers this method can accept
24
+ # @option options :hidden [Boolean] hide the endpoint or not
25
+ # @option options :deprecated [Boolean] deprecate the endpoint or not
26
+ # @option options :is_array [Boolean] response entity is array or not
27
+ # @option options :nickname [String] nickname of the endpoint
28
+ # @option options :produces [Array[String]] a list of MIME types the endpoint produce
29
+ # @option options :consumes [Array[String]] a list of MIME types the endpoint consume
30
+ # @option options :security [Array[Hash]] a list of security schemes
31
+ # @option options :tags [Array[String]] a list of tags
20
32
  # @yield a block yielding an instance context with methods mapping to
21
33
  # each of the above, except that :entity is also aliased as #success
22
34
  # and :http_codes is aliased as #failure.
@@ -38,17 +50,25 @@ module Grape
38
50
  # end
39
51
  #
40
52
  def desc(description, options = {}, &config_block)
41
- if block_given?
42
- config_class = desc_container
53
+ if config_block
54
+ endpoint_configuration = if defined?(configuration)
55
+ # When the instance is mounted - the configuration is executed on mount time
56
+ if configuration.respond_to?(:evaluate)
57
+ configuration.evaluate
58
+ # Within `given` or `mounted blocks` the configuration is already evaluated
59
+ elsif configuration.is_a?(Hash)
60
+ configuration
61
+ end
62
+ end
63
+ endpoint_configuration ||= {}
64
+ config_class = desc_container(endpoint_configuration)
43
65
 
44
66
  config_class.configure do
45
67
  description description
46
68
  end
47
69
 
48
70
  config_class.configure(&config_block)
49
- unless options.empty?
50
- warn '[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.'
51
- end
71
+ warn '[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.' unless options.empty?
52
72
  options = config_class.settings
53
73
  else
54
74
  options = options.merge(description: description)
@@ -58,34 +78,32 @@ module Grape
58
78
  route_setting :description, options
59
79
  end
60
80
 
61
- def description_field(field, value = nil)
62
- if value
63
- description = route_setting(:description)
64
- description ||= route_setting(:description, {})
65
- description[field] = value
66
- else
67
- description = route_setting(:description)
68
- description[field] if description
69
- end
70
- end
71
-
72
- def unset_description_field(field)
73
- description = route_setting(:description)
74
- description.delete(field) if description
75
- end
76
-
77
81
  # Returns an object which configures itself via an instance-context DSL.
78
- def desc_container
82
+ def desc_container(endpoint_configuration)
79
83
  Module.new do
80
84
  include Grape::Util::StrictHashConfiguration.module(
85
+ :summary,
81
86
  :description,
82
87
  :detail,
83
88
  :params,
84
89
  :entity,
85
90
  :http_codes,
86
91
  :named,
87
- :headers
92
+ :body_name,
93
+ :headers,
94
+ :hidden,
95
+ :deprecated,
96
+ :is_array,
97
+ :nickname,
98
+ :produces,
99
+ :consumes,
100
+ :security,
101
+ :tags,
102
+ :default
88
103
  )
104
+ config_context.define_singleton_method(:configuration) do
105
+ endpoint_configuration
106
+ end
89
107
 
90
108
  def config_context.success(*args)
91
109
  entity(*args)
@@ -1,8 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module DSL
3
5
  module Headers
4
- # Set an individual header or retrieve
5
- # all headers that have been set.
6
+ # This method has four responsibilities:
7
+ # 1. Set a specifc header value by key
8
+ # 2. Retrieve a specifc header value by key
9
+ # 3. Retrieve all headers that have been set
10
+ # 4. Delete a specifc header key-value pair
6
11
  def header(key = nil, val = nil)
7
12
  if key
8
13
  val ? header[key.to_s] = val : header.delete(key.to_s)
@@ -1,4 +1,4 @@
1
- require 'active_support/concern'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Grape
4
4
  module DSL
@@ -34,8 +34,8 @@ module Grape
34
34
  #
35
35
  def helpers(*new_modules, &block)
36
36
  include_new_modules(new_modules) if new_modules.any?
37
- include_block(block) if block_given?
38
- include_all_in_scope if !block_given? && new_modules.empty?
37
+ include_block(block) if block
38
+ include_all_in_scope if !block && new_modules.empty?
39
39
  end
40
40
 
41
41
  protected
@@ -65,12 +65,13 @@ module Grape
65
65
 
66
66
  def define_boolean_in_mod(mod)
67
67
  return if defined? mod::Boolean
68
- mod.const_set('Boolean', Virtus::Attribute::Boolean)
68
+
69
+ mod.const_set(:Boolean, Grape::API::Boolean)
69
70
  end
70
71
 
71
- def inject_api_helpers_to_mod(mod, &_block)
72
+ def inject_api_helpers_to_mod(mod, &block)
72
73
  mod.extend(BaseHelper) unless mod.is_a?(BaseHelper)
73
- yield if block_given?
74
+ yield if block
74
75
  mod.api_changed(self)
75
76
  end
76
77
  end
@@ -79,6 +80,7 @@ module Grape
79
80
  # to provide some API-specific functionality.
80
81
  module BaseHelper
81
82
  attr_accessor :api
83
+
82
84
  def params(name, &block)
83
85
  @named_params ||= {}
84
86
  @named_params[name] = block
@@ -92,7 +94,8 @@ module Grape
92
94
  protected
93
95
 
94
96
  def process_named_params
95
- return unless @named_params && @named_params.any?
97
+ return unless instance_variable_defined?(:@named_params) && @named_params && @named_params.any?
98
+
96
99
  api.namespace_stackable(:named_params, @named_params)
97
100
  end
98
101
  end
@@ -1,4 +1,5 @@
1
- require 'active_support/concern'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'grape/dsl/headers'
3
4
 
4
5
  module Grape
@@ -26,85 +27,106 @@ module Grape
26
27
  # Methods which should not be available in filters until the before filter
27
28
  # has completed
28
29
  module PostBeforeFilter
29
- def declared(passed_params, options = {}, declared_params = nil)
30
- options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
31
- declared_params ||= optioned_declared_params(options)
30
+ def declared(passed_params, options = {}, declared_params = nil, params_nested_path = [])
31
+ options = options.reverse_merge(include_missing: true, include_parent_namespaces: true, evaluate_given: false)
32
+ declared_params ||= optioned_declared_params(**options)
32
33
 
33
34
  if passed_params.is_a?(Array)
34
- declared_array(passed_params, options, declared_params)
35
+ declared_array(passed_params, options, declared_params, params_nested_path)
35
36
  else
36
- declared_hash(passed_params, options, declared_params)
37
+ declared_hash(passed_params, options, declared_params, params_nested_path)
37
38
  end
38
39
  end
39
40
 
40
41
  private
41
42
 
42
- def declared_array(passed_params, options, declared_params)
43
+ def declared_array(passed_params, options, declared_params, params_nested_path)
43
44
  passed_params.map do |passed_param|
44
- declared(passed_param || {}, options, declared_params)
45
+ declared(passed_param || {}, options, declared_params, params_nested_path)
45
46
  end
46
47
  end
47
48
 
48
- def declared_hash(passed_params, options, declared_params)
49
- declared_params.each_with_object(passed_params.class.new) do |declared_param, memo|
50
- if declared_param.is_a?(Hash)
51
- declared_param.each_pair do |declared_parent_param, declared_children_params|
52
- next unless options[:include_missing] || passed_params.key?(declared_parent_param)
49
+ def declared_hash(passed_params, options, declared_params, params_nested_path)
50
+ declared_params.each_with_object(passed_params.class.new) do |declared_param_attr, memo|
51
+ next if options[:evaluate_given] && !declared_param_attr.scope.attr_meets_dependency?(passed_params)
53
52
 
54
- passed_children_params = passed_params[declared_parent_param] || passed_params.class.new
55
- memo_key = optioned_param_key(declared_parent_param, options)
53
+ declared_hash_attr(passed_params, options, declared_param_attr.key, params_nested_path, memo)
54
+ end
55
+ end
56
56
 
57
- memo[memo_key] = handle_passed_param(declared_parent_param, passed_children_params) do
58
- declared(passed_children_params, options, declared_children_params)
59
- end
60
- end
61
- else
62
- # If it is not a Hash then it does not have children.
63
- # Find its value or set it to nil.
64
- has_alias = route_setting(:aliased_params) && route_setting(:aliased_params).find { |current| current[declared_param] }
65
- param_alias = has_alias[declared_param] if has_alias
57
+ def declared_hash_attr(passed_params, options, declared_param, params_nested_path, memo)
58
+ renamed_params = route_setting(:renamed_params) || {}
59
+ if declared_param.is_a?(Hash)
60
+ declared_param.each_pair do |declared_parent_param, declared_children_params|
61
+ params_nested_path_dup = params_nested_path.dup
62
+ params_nested_path_dup << declared_parent_param.to_s
63
+ next unless options[:include_missing] || passed_params.key?(declared_parent_param)
66
64
 
67
- next unless options[:include_missing] || passed_params.key?(declared_param) || (param_alias && passed_params.key?(param_alias))
65
+ rename_path = params_nested_path + [declared_parent_param.to_s]
66
+ renamed_param_name = renamed_params[rename_path]
68
67
 
69
- if param_alias
70
- memo[optioned_param_key(param_alias, options)] = passed_params[param_alias]
71
- else
72
- memo[optioned_param_key(declared_param, options)] = passed_params[declared_param]
68
+ memo_key = optioned_param_key(renamed_param_name || declared_parent_param, options)
69
+ passed_children_params = passed_params[declared_parent_param] || passed_params.class.new
70
+
71
+ memo[memo_key] = handle_passed_param(params_nested_path_dup, passed_children_params.any?) do
72
+ declared(passed_children_params, options, declared_children_params, params_nested_path_dup)
73
73
  end
74
74
  end
75
+ else
76
+ # If it is not a Hash then it does not have children.
77
+ # Find its value or set it to nil.
78
+ return unless options[:include_missing] || passed_params.key?(declared_param)
79
+
80
+ rename_path = params_nested_path + [declared_param.to_s]
81
+ renamed_param_name = renamed_params[rename_path]
82
+
83
+ memo_key = optioned_param_key(renamed_param_name || declared_param, options)
84
+ passed_param = passed_params[declared_param]
85
+
86
+ params_nested_path_dup = params_nested_path.dup
87
+ params_nested_path_dup << declared_param.to_s
88
+ memo[memo_key] = passed_param || handle_passed_param(params_nested_path_dup) do
89
+ passed_param
90
+ end
75
91
  end
76
92
  end
77
93
 
78
- def handle_passed_param(declared_param, passed_children_params, &_block)
79
- should_be_empty_array?(declared_param, passed_children_params) ? [] : yield
80
- end
94
+ def handle_passed_param(params_nested_path, has_passed_children = false, &_block)
95
+ return yield if has_passed_children
81
96
 
82
- def should_be_empty_array?(declared_param, passed_children_params)
83
- declared_param_is_array?(declared_param) && passed_children_params.empty?
84
- end
97
+ key = params_nested_path[0]
98
+ key += "[#{params_nested_path[1..].join('][')}]" if params_nested_path.size > 1
85
99
 
86
- def declared_param_is_array?(declared_param)
87
- route_options_params[declared_param.to_s] && route_options_params[declared_param.to_s][:type] == 'Array'
88
- end
100
+ route_options_params = options[:route_options][:params] || {}
101
+ type = route_options_params.dig(key, :type)
102
+ has_children = route_options_params.keys.any? { |k| k != key && k.start_with?(key) }
89
103
 
90
- def route_options_params
91
- options[:route_options][:params] || {}
104
+ if type == 'Hash' && !has_children
105
+ {}
106
+ elsif type == 'Array' || (type&.start_with?('[') && !type&.include?(','))
107
+ []
108
+ elsif type == 'Set' || type&.start_with?('#<Set')
109
+ Set.new
110
+ else
111
+ yield
112
+ end
92
113
  end
93
114
 
94
115
  def optioned_param_key(declared_param, options)
95
116
  options[:stringify] ? declared_param.to_s : declared_param.to_sym
96
117
  end
97
118
 
98
- def optioned_declared_params(options)
119
+ def optioned_declared_params(**options)
99
120
  declared_params = if options[:include_parent_namespaces]
100
121
  # Declared params including parent namespaces
101
- route_setting(:saved_declared_params).flatten | Array(route_setting(:declared_params))
122
+ route_setting(:declared_params)
102
123
  else
103
124
  # Declared params at current namespace
104
- route_setting(:saved_declared_params).last & Array(route_setting(:declared_params))
125
+ namespace_stackable(:declared_params).last || []
105
126
  end
106
127
 
107
128
  raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
129
+
108
130
  declared_params
109
131
  end
110
132
  end
@@ -129,13 +151,19 @@ module Grape
129
151
  env[Grape::Env::API_VERSION]
130
152
  end
131
153
 
154
+ def configuration
155
+ options[:for].configuration.evaluate
156
+ end
157
+
132
158
  # End the request and display an error to the
133
159
  # end user with the specified message.
134
160
  #
135
161
  # @param message [String] The message to display.
136
162
  # @param status [Integer] the HTTP Status Code. Defaults to default_error_status, 500 if not set.
137
- def error!(message, status = nil, headers = nil)
163
+ # @param additional_headers [Hash] Addtional headers for the response.
164
+ def error!(message, status = nil, additional_headers = nil)
138
165
  self.status(status || namespace_inheritable(:default_error_status))
166
+ headers = additional_headers.present? ? header.merge(additional_headers) : header
139
167
  throw :error, message: message, status: self.status, headers: headers
140
168
  end
141
169
 
@@ -168,17 +196,19 @@ module Grape
168
196
  def status(status = nil)
169
197
  case status
170
198
  when Symbol
171
- raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.keys.include?(status)
199
+ raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(status)
200
+
172
201
  @status = Rack::Utils.status_code(status)
173
202
  when Integer
174
203
  @status = status
175
204
  when nil
176
- return @status if @status
205
+ return @status if instance_variable_defined?(:@status) && @status
206
+
177
207
  case request.request_method.to_s.upcase
178
208
  when Grape::Http::Headers::POST
179
209
  201
180
210
  when Grape::Http::Headers::DELETE
181
- if @body.present?
211
+ if instance_variable_defined?(:@body) && @body.present?
182
212
  200
183
213
  else
184
214
  204
@@ -229,7 +259,7 @@ module Grape
229
259
  @body = ''
230
260
  status 204
231
261
  else
232
- @body
262
+ instance_variable_defined?(:@body) ? @body : nil
233
263
  end
234
264
  end
235
265
 
@@ -247,23 +277,36 @@ module Grape
247
277
  body false
248
278
  end
249
279
 
250
- # Allows you to define the response as a file-like object.
280
+ # Deprecated method to send files to the client. Use `sendfile` or `stream`
281
+ def file(value = nil)
282
+ if value.is_a?(String)
283
+ warn '[DEPRECATION] Use sendfile or stream to send files.'
284
+ sendfile(value)
285
+ elsif !value.is_a?(NilClass)
286
+ warn '[DEPRECATION] Use stream to use a Stream object.'
287
+ stream(value)
288
+ else
289
+ warn '[DEPRECATION] Use sendfile or stream to send files.'
290
+ sendfile
291
+ end
292
+ end
293
+
294
+ # Allows you to send a file to the client via sendfile.
251
295
  #
252
296
  # @example
253
297
  # get '/file' do
254
- # file FileStreamer.new(...)
298
+ # sendfile FileStreamer.new(...)
255
299
  # end
256
300
  #
257
301
  # GET /file # => "contents of file"
258
- def file(value = nil)
302
+ def sendfile(value = nil)
259
303
  if value.is_a?(String)
260
- file_body = Grape::ServeFile::FileBody.new(value)
261
- @file = Grape::ServeFile::FileResponse.new(file_body)
304
+ file_body = Grape::ServeStream::FileBody.new(value)
305
+ @stream = Grape::ServeStream::StreamResponse.new(file_body)
262
306
  elsif !value.is_a?(NilClass)
263
- warn '[DEPRECATION] Argument as FileStreamer-like object is deprecated. Use path to file instead.'
264
- @file = Grape::ServeFile::FileResponse.new(value)
307
+ raise ArgumentError, 'Argument must be a file path'
265
308
  else
266
- @file
309
+ stream
267
310
  end
268
311
  end
269
312
 
@@ -283,10 +326,21 @@ module Grape
283
326
  # * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/chunked.rb
284
327
  # * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/etag.rb
285
328
  def stream(value = nil)
329
+ return if value.nil? && @stream.nil?
330
+
286
331
  header 'Content-Length', nil
287
332
  header 'Transfer-Encoding', nil
288
333
  header 'Cache-Control', 'no-cache' # Skips ETag generation (reading the response up front)
289
- file(value)
334
+ if value.is_a?(String)
335
+ file_body = Grape::ServeStream::FileBody.new(value)
336
+ @stream = Grape::ServeStream::StreamResponse.new(file_body)
337
+ elsif value.respond_to?(:each)
338
+ @stream = Grape::ServeStream::StreamResponse.new(value)
339
+ elsif !value.is_a?(NilClass)
340
+ raise ArgumentError, 'Stream object must respond to :each.'
341
+ else
342
+ @stream
343
+ end
290
344
  end
291
345
 
292
346
  # Allows you to make use of Grape Entities by setting
@@ -322,11 +376,13 @@ module Grape
322
376
  end
323
377
 
324
378
  representation = { root => representation } if root
379
+
325
380
  if key
326
- representation = (@body || {}).merge(key => representation)
327
- elsif entity_class.present? && @body
381
+ representation = (body || {}).merge(key => representation)
382
+ elsif entity_class.present? && body
328
383
  raise ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
329
- representation = @body.merge(representation)
384
+
385
+ representation = body.merge(representation)
330
386
  end
331
387
 
332
388
  body representation
@@ -356,7 +412,7 @@ module Grape
356
412
  entity_class = options.delete(:with)
357
413
 
358
414
  if entity_class.nil?
359
- # entity class not explicitely defined, auto-detect from relation#klass or first object in the collection
415
+ # entity class not explicitly defined, auto-detect from relation#klass or first object in the collection
360
416
  object_class = if object.respond_to?(:klass)
361
417
  object.klass
362
418
  else
@@ -378,7 +434,7 @@ module Grape
378
434
  def entity_representation_for(entity_class, object, options)
379
435
  embeds = { env: env }
380
436
  embeds[:version] = env[Grape::Env::API_VERSION] if env[Grape::Env::API_VERSION]
381
- entity_class.represent(object, embeds.merge(options))
437
+ entity_class.represent(object, **embeds.merge(options))
382
438
  end
383
439
  end
384
440
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module DSL
3
5
  module Logger