grape 1.1.0 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +278 -44
  3. data/LICENSE +1 -1
  4. data/README.md +514 -69
  5. data/UPGRADING.md +424 -17
  6. data/grape.gemspec +13 -2
  7. data/lib/grape.rb +104 -71
  8. data/lib/grape/api.rb +138 -175
  9. data/lib/grape/api/helpers.rb +2 -0
  10. data/lib/grape/api/instance.rb +283 -0
  11. data/lib/grape/config.rb +34 -0
  12. data/lib/grape/content_types.rb +34 -0
  13. data/lib/grape/cookies.rb +2 -0
  14. data/lib/grape/dsl/api.rb +2 -0
  15. data/lib/grape/dsl/callbacks.rb +22 -0
  16. data/lib/grape/dsl/configuration.rb +2 -0
  17. data/lib/grape/dsl/desc.rb +41 -7
  18. data/lib/grape/dsl/headers.rb +2 -0
  19. data/lib/grape/dsl/helpers.rb +5 -2
  20. data/lib/grape/dsl/inside_route.rb +92 -49
  21. data/lib/grape/dsl/logger.rb +2 -0
  22. data/lib/grape/dsl/middleware.rb +9 -0
  23. data/lib/grape/dsl/parameters.rb +25 -14
  24. data/lib/grape/dsl/request_response.rb +4 -2
  25. data/lib/grape/dsl/routing.rb +17 -10
  26. data/lib/grape/dsl/settings.rb +7 -1
  27. data/lib/grape/dsl/validations.rb +24 -4
  28. data/lib/grape/eager_load.rb +20 -0
  29. data/lib/grape/endpoint.rb +59 -35
  30. data/lib/grape/error_formatter.rb +4 -2
  31. data/lib/grape/error_formatter/base.rb +2 -0
  32. data/lib/grape/error_formatter/json.rb +2 -0
  33. data/lib/grape/error_formatter/txt.rb +2 -0
  34. data/lib/grape/error_formatter/xml.rb +2 -0
  35. data/lib/grape/exceptions/base.rb +20 -14
  36. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  37. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  38. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  39. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  40. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  41. data/lib/grape/exceptions/invalid_response.rb +11 -0
  42. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  43. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  44. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  45. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  46. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  47. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  48. data/lib/grape/exceptions/missing_option.rb +2 -0
  49. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  50. data/lib/grape/exceptions/unknown_options.rb +2 -0
  51. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  52. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  53. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  54. data/lib/grape/exceptions/validation.rb +4 -2
  55. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  56. data/lib/grape/exceptions/validation_errors.rb +16 -13
  57. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  58. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  59. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  60. data/lib/grape/extensions/hash.rb +2 -0
  61. data/lib/grape/extensions/hashie/mash.rb +2 -0
  62. data/lib/grape/formatter.rb +5 -3
  63. data/lib/grape/formatter/json.rb +2 -0
  64. data/lib/grape/formatter/serializable_hash.rb +2 -0
  65. data/lib/grape/formatter/txt.rb +2 -0
  66. data/lib/grape/formatter/xml.rb +2 -0
  67. data/lib/grape/http/headers.rb +50 -18
  68. data/lib/grape/locale/en.yml +3 -1
  69. data/lib/grape/middleware/auth/base.rb +7 -7
  70. data/lib/grape/middleware/auth/dsl.rb +2 -0
  71. data/lib/grape/middleware/auth/strategies.rb +2 -0
  72. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  73. data/lib/grape/middleware/base.rb +10 -7
  74. data/lib/grape/middleware/error.rb +21 -16
  75. data/lib/grape/middleware/filter.rb +2 -0
  76. data/lib/grape/middleware/formatter.rb +8 -6
  77. data/lib/grape/middleware/globals.rb +2 -0
  78. data/lib/grape/middleware/helpers.rb +12 -0
  79. data/lib/grape/middleware/stack.rb +13 -3
  80. data/lib/grape/middleware/versioner.rb +2 -0
  81. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  82. data/lib/grape/middleware/versioner/header.rb +10 -8
  83. data/lib/grape/middleware/versioner/param.rb +3 -1
  84. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
  85. data/lib/grape/middleware/versioner/path.rb +3 -1
  86. data/lib/grape/namespace.rb +14 -2
  87. data/lib/grape/parser.rb +4 -2
  88. data/lib/grape/parser/json.rb +3 -1
  89. data/lib/grape/parser/xml.rb +3 -1
  90. data/lib/grape/path.rb +15 -3
  91. data/lib/grape/presenters/presenter.rb +2 -0
  92. data/lib/grape/request.rb +19 -10
  93. data/lib/grape/router.rb +30 -29
  94. data/lib/grape/router/attribute_translator.rb +41 -8
  95. data/lib/grape/router/pattern.rb +20 -16
  96. data/lib/grape/router/route.rb +14 -28
  97. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  98. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  99. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  100. data/lib/grape/util/base_inheritable.rb +43 -0
  101. data/lib/grape/util/cache.rb +20 -0
  102. data/lib/grape/util/endpoint_configuration.rb +8 -0
  103. data/lib/grape/util/env.rb +19 -17
  104. data/lib/grape/util/inheritable_setting.rb +2 -0
  105. data/lib/grape/util/inheritable_values.rb +7 -25
  106. data/lib/grape/util/json.rb +2 -0
  107. data/lib/grape/util/lazy_block.rb +27 -0
  108. data/lib/grape/util/lazy_object.rb +43 -0
  109. data/lib/grape/util/lazy_value.rb +98 -0
  110. data/lib/grape/util/registrable.rb +2 -0
  111. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  112. data/lib/grape/util/stackable_values.rb +21 -34
  113. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  114. data/lib/grape/util/xml.rb +2 -0
  115. data/lib/grape/validations.rb +2 -0
  116. data/lib/grape/validations/attributes_iterator.rb +16 -6
  117. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  118. data/lib/grape/validations/params_scope.rb +51 -30
  119. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  120. data/lib/grape/validations/types.rb +13 -38
  121. data/lib/grape/validations/types/array_coercer.rb +65 -0
  122. data/lib/grape/validations/types/build_coercer.rb +47 -49
  123. data/lib/grape/validations/types/custom_type_coercer.rb +29 -51
  124. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  125. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  126. data/lib/grape/validations/types/file.rb +22 -18
  127. data/lib/grape/validations/types/invalid_value.rb +24 -0
  128. data/lib/grape/validations/types/json.rb +46 -39
  129. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  130. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  131. data/lib/grape/validations/types/set_coercer.rb +40 -0
  132. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  133. data/lib/grape/validations/validator_factory.rb +8 -11
  134. data/lib/grape/validations/validators/all_or_none.rb +8 -13
  135. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  136. data/lib/grape/validations/validators/as.rb +5 -4
  137. data/lib/grape/validations/validators/at_least_one_of.rb +7 -13
  138. data/lib/grape/validations/validators/base.rb +20 -16
  139. data/lib/grape/validations/validators/coerce.rb +46 -29
  140. data/lib/grape/validations/validators/default.rb +6 -6
  141. data/lib/grape/validations/validators/exactly_one_of.rb +10 -23
  142. data/lib/grape/validations/validators/except_values.rb +4 -2
  143. data/lib/grape/validations/validators/multiple_params_base.rb +17 -10
  144. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -18
  145. data/lib/grape/validations/validators/presence.rb +3 -1
  146. data/lib/grape/validations/validators/regexp.rb +4 -2
  147. data/lib/grape/validations/validators/same_as.rb +26 -0
  148. data/lib/grape/validations/validators/values.rb +18 -6
  149. data/lib/grape/version.rb +3 -1
  150. data/spec/grape/api/custom_validations_spec.rb +5 -3
  151. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  152. data/spec/grape/api/defines_boolean_in_params_spec.rb +39 -0
  153. data/spec/grape/api/inherited_helpers_spec.rb +2 -0
  154. data/spec/grape/api/instance_spec.rb +104 -0
  155. data/spec/grape/api/invalid_format_spec.rb +2 -0
  156. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  157. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  158. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  159. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  160. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  161. data/spec/grape/api/recognize_path_spec.rb +2 -0
  162. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  163. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  164. data/spec/grape/api/routes_with_requirements_spec.rb +61 -0
  165. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
  166. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  167. data/spec/grape/api_remount_spec.rb +473 -0
  168. data/spec/grape/api_spec.rb +565 -12
  169. data/spec/grape/config_spec.rb +19 -0
  170. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  171. data/spec/grape/dsl/configuration_spec.rb +2 -0
  172. data/spec/grape/dsl/desc_spec.rb +42 -16
  173. data/spec/grape/dsl/headers_spec.rb +2 -0
  174. data/spec/grape/dsl/helpers_spec.rb +4 -2
  175. data/spec/grape/dsl/inside_route_spec.rb +184 -33
  176. data/spec/grape/dsl/logger_spec.rb +2 -0
  177. data/spec/grape/dsl/middleware_spec.rb +10 -0
  178. data/spec/grape/dsl/parameters_spec.rb +2 -0
  179. data/spec/grape/dsl/request_response_spec.rb +2 -0
  180. data/spec/grape/dsl/routing_spec.rb +12 -0
  181. data/spec/grape/dsl/settings_spec.rb +2 -0
  182. data/spec/grape/dsl/validations_spec.rb +2 -0
  183. data/spec/grape/endpoint/declared_spec.rb +601 -0
  184. data/spec/grape/endpoint_spec.rb +53 -523
  185. data/spec/grape/entity_spec.rb +9 -1
  186. data/spec/grape/exceptions/base_spec.rb +67 -0
  187. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  188. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  189. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
  190. data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
  191. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  192. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  193. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  194. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  195. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  196. data/spec/grape/exceptions/validation_errors_spec.rb +8 -4
  197. data/spec/grape/exceptions/validation_spec.rb +3 -1
  198. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  199. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  200. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  201. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  202. data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
  203. data/spec/grape/integration/rack_spec.rb +25 -7
  204. data/spec/grape/loading_spec.rb +2 -0
  205. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  206. data/spec/grape/middleware/auth/dsl_spec.rb +5 -3
  207. data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
  208. data/spec/grape/middleware/base_spec.rb +10 -0
  209. data/spec/grape/middleware/error_spec.rb +3 -1
  210. data/spec/grape/middleware/exception_spec.rb +4 -2
  211. data/spec/grape/middleware/formatter_spec.rb +33 -16
  212. data/spec/grape/middleware/globals_spec.rb +2 -0
  213. data/spec/grape/middleware/stack_spec.rb +12 -0
  214. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  215. data/spec/grape/middleware/versioner/header_spec.rb +9 -1
  216. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  217. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  218. data/spec/grape/middleware/versioner_spec.rb +2 -0
  219. data/spec/grape/named_api_spec.rb +21 -0
  220. data/spec/grape/parser_spec.rb +7 -5
  221. data/spec/grape/path_spec.rb +6 -4
  222. data/spec/grape/presenters/presenter_spec.rb +2 -0
  223. data/spec/grape/request_spec.rb +26 -0
  224. data/spec/grape/util/inheritable_setting_spec.rb +2 -0
  225. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  226. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  227. data/spec/grape/util/stackable_values_spec.rb +3 -1
  228. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  229. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  230. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  231. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +41 -0
  232. data/spec/grape/validations/params_scope_spec.rb +213 -9
  233. data/spec/grape/validations/single_attribute_iterator_spec.rb +58 -0
  234. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  235. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  236. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  237. data/spec/grape/validations/types_spec.rb +9 -36
  238. data/spec/grape/validations/validators/all_or_none_spec.rb +140 -30
  239. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  240. data/spec/grape/validations/validators/at_least_one_of_spec.rb +175 -29
  241. data/spec/grape/validations/validators/coerce_spec.rb +476 -135
  242. data/spec/grape/validations/validators/default_spec.rb +172 -0
  243. data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
  244. data/spec/grape/validations/validators/except_values_spec.rb +4 -1
  245. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +186 -27
  246. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  247. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  248. data/spec/grape/validations/validators/same_as_spec.rb +65 -0
  249. data/spec/grape/validations/validators/values_spec.rb +30 -5
  250. data/spec/grape/validations_spec.rb +388 -50
  251. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  252. data/spec/integration/multi_json/json_spec.rb +2 -0
  253. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  254. data/spec/shared/versioning_examples.rb +22 -20
  255. data/spec/spec_helper.rb +12 -1
  256. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  257. data/spec/support/chunks.rb +14 -0
  258. data/spec/support/content_type_helpers.rb +2 -0
  259. data/spec/support/eager_load.rb +19 -0
  260. data/spec/support/endpoint_faker.rb +2 -0
  261. data/spec/support/file_streamer.rb +2 -0
  262. data/spec/support/integer_helpers.rb +2 -0
  263. data/spec/support/versioned_helpers.rb +8 -8
  264. metadata +86 -48
  265. data/Appraisals +0 -32
  266. data/Dangerfile +0 -2
  267. data/Gemfile +0 -33
  268. data/Gemfile.lock +0 -231
  269. data/Guardfile +0 -10
  270. data/RELEASING.md +0 -111
  271. data/Rakefile +0 -25
  272. data/benchmark/simple.rb +0 -27
  273. data/benchmark/simple_with_type_coercer.rb +0 -22
  274. data/gemfiles/multi_json.gemfile +0 -35
  275. data/gemfiles/multi_xml.gemfile +0 -35
  276. data/gemfiles/rack_1.5.2.gemfile +0 -35
  277. data/gemfiles/rack_edge.gemfile +0 -35
  278. data/gemfiles/rails_3.gemfile +0 -36
  279. data/gemfiles/rails_4.gemfile +0 -35
  280. data/gemfiles/rails_5.gemfile +0 -35
  281. data/gemfiles/rails_edge.gemfile +0 -35
  282. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  283. data/lib/grape/util/content_types.rb +0 -26
  284. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
  285. data/pkg/grape-0.17.0.gem +0 -0
  286. data/pkg/grape-0.19.0.gem +0 -0
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module DSL
3
5
  module Headers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
@@ -65,7 +67,7 @@ module Grape
65
67
 
66
68
  def define_boolean_in_mod(mod)
67
69
  return if defined? mod::Boolean
68
- mod.const_set('Boolean', Virtus::Attribute::Boolean)
70
+ mod.const_set('Boolean', Grape::API::Boolean)
69
71
  end
70
72
 
71
73
  def inject_api_helpers_to_mod(mod, &_block)
@@ -79,6 +81,7 @@ module Grape
79
81
  # to provide some API-specific functionality.
80
82
  module BaseHelper
81
83
  attr_accessor :api
84
+
82
85
  def params(name, &block)
83
86
  @named_params ||= {}
84
87
  @named_params[name] = block
@@ -92,7 +95,7 @@ module Grape
92
95
  protected
93
96
 
94
97
  def process_named_params
95
- return unless @named_params && @named_params.any?
98
+ return unless instance_variable_defined?(:@named_params) && @named_params && @named_params.any?
96
99
  api.namespace_stackable(:named_params, @named_params)
97
100
  end
98
101
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
  require 'grape/dsl/headers'
3
5
 
@@ -26,82 +28,92 @@ module Grape
26
28
  # Methods which should not be available in filters until the before filter
27
29
  # has completed
28
30
  module PostBeforeFilter
29
- def declared(passed_params, options = {}, declared_params = nil)
31
+ def declared(passed_params, options = {}, declared_params = nil, params_nested_path = [])
30
32
  options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
31
- declared_params ||= optioned_declared_params(options)
33
+ declared_params ||= optioned_declared_params(**options)
32
34
 
33
35
  if passed_params.is_a?(Array)
34
- declared_array(passed_params, options, declared_params)
36
+ declared_array(passed_params, options, declared_params, params_nested_path)
35
37
  else
36
- declared_hash(passed_params, options, declared_params)
38
+ declared_hash(passed_params, options, declared_params, params_nested_path)
37
39
  end
38
40
  end
39
41
 
40
42
  private
41
43
 
42
- def declared_array(passed_params, options, declared_params)
44
+ def declared_array(passed_params, options, declared_params, params_nested_path)
43
45
  passed_params.map do |passed_param|
44
- declared(passed_param || {}, options, declared_params)
46
+ declared(passed_param || {}, options, declared_params, params_nested_path)
45
47
  end
46
48
  end
47
49
 
48
- def declared_hash(passed_params, options, declared_params)
50
+ def declared_hash(passed_params, options, declared_params, params_nested_path)
49
51
  declared_params.each_with_object(passed_params.class.new) do |declared_param, memo|
50
52
  if declared_param.is_a?(Hash)
51
53
  declared_param.each_pair do |declared_parent_param, declared_children_params|
54
+ params_nested_path_dup = params_nested_path.dup
55
+ params_nested_path_dup << declared_parent_param.to_s
52
56
  next unless options[:include_missing] || passed_params.key?(declared_parent_param)
53
57
 
54
58
  passed_children_params = passed_params[declared_parent_param] || passed_params.class.new
55
59
  memo_key = optioned_param_key(declared_parent_param, options)
56
60
 
57
- memo[memo_key] = handle_passed_param(declared_parent_param, passed_children_params) do
58
- declared(passed_children_params, options, declared_children_params)
61
+ memo[memo_key] = handle_passed_param(params_nested_path_dup, passed_children_params.any?) do
62
+ declared(passed_children_params, options, declared_children_params, params_nested_path_dup)
59
63
  end
60
64
  end
61
65
  else
62
66
  # If it is not a Hash then it does not have children.
63
67
  # 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
68
+ has_renaming = route_setting(:renamed_params) && route_setting(:renamed_params).find { |current| current[declared_param] }
69
+ param_renaming = has_renaming[declared_param] if has_renaming
70
+
71
+ next unless options[:include_missing] || passed_params.key?(declared_param) || (param_renaming && passed_params.key?(param_renaming))
66
72
 
67
- next unless options[:include_missing] || passed_params.key?(declared_param) || (param_alias && passed_params.key?(param_alias))
73
+ memo_key = optioned_param_key(param_renaming || declared_param, options)
74
+ passed_param = passed_params[param_renaming || declared_param]
68
75
 
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]
76
+ params_nested_path_dup = params_nested_path.dup
77
+ params_nested_path_dup << declared_param.to_s
78
+ memo[memo_key] = passed_param || handle_passed_param(params_nested_path_dup) do
79
+ passed_param
73
80
  end
74
81
  end
75
82
  end
76
83
  end
77
84
 
78
- def handle_passed_param(declared_param, passed_children_params, &_block)
79
- should_be_empty_array?(declared_param, passed_children_params) ? [] : yield
80
- end
85
+ def handle_passed_param(params_nested_path, has_passed_children = false, &_block)
86
+ return yield if has_passed_children
81
87
 
82
- def should_be_empty_array?(declared_param, passed_children_params)
83
- declared_param_is_array?(declared_param) && passed_children_params.empty?
84
- end
88
+ key = params_nested_path[0]
89
+ key += '[' + params_nested_path[1..-1].join('][') + ']' if params_nested_path.size > 1
85
90
 
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
91
+ route_options_params = options[:route_options][:params] || {}
92
+ type = route_options_params.dig(key, :type)
93
+ has_children = route_options_params.keys.any? { |k| k != key && k.start_with?(key) }
89
94
 
90
- def route_options_params
91
- options[:route_options][:params] || {}
95
+ if type == 'Hash' && !has_children
96
+ {}
97
+ elsif type == 'Array' || type&.start_with?('[') && !type&.include?(',')
98
+ []
99
+ elsif type == 'Set' || type&.start_with?('#<Set')
100
+ Set.new
101
+ else
102
+ yield
103
+ end
92
104
  end
93
105
 
94
106
  def optioned_param_key(declared_param, options)
95
107
  options[:stringify] ? declared_param.to_s : declared_param.to_sym
96
108
  end
97
109
 
98
- def optioned_declared_params(options)
110
+ def optioned_declared_params(**options)
99
111
  declared_params = if options[:include_parent_namespaces]
100
112
  # Declared params including parent namespaces
101
- route_setting(:saved_declared_params).flatten | Array(route_setting(:declared_params))
113
+ route_setting(:declared_params)
102
114
  else
103
115
  # Declared params at current namespace
104
- route_setting(:saved_declared_params).last & Array(route_setting(:declared_params))
116
+ namespace_stackable(:declared_params).last || []
105
117
  end
106
118
 
107
119
  raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
@@ -129,13 +141,19 @@ module Grape
129
141
  env[Grape::Env::API_VERSION]
130
142
  end
131
143
 
144
+ def configuration
145
+ options[:for].configuration.evaluate
146
+ end
147
+
132
148
  # End the request and display an error to the
133
149
  # end user with the specified message.
134
150
  #
135
151
  # @param message [String] The message to display.
136
152
  # @param status [Integer] the HTTP Status Code. Defaults to default_error_status, 500 if not set.
137
- def error!(message, status = nil, headers = nil)
153
+ # @param additional_headers [Hash] Addtional headers for the response.
154
+ def error!(message, status = nil, additional_headers = nil)
138
155
  self.status(status || namespace_inheritable(:default_error_status))
156
+ headers = additional_headers.present? ? header.merge(additional_headers) : header
139
157
  throw :error, message: message, status: self.status, headers: headers
140
158
  end
141
159
 
@@ -168,17 +186,17 @@ module Grape
168
186
  def status(status = nil)
169
187
  case status
170
188
  when Symbol
171
- raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.keys.include?(status)
189
+ raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(status)
172
190
  @status = Rack::Utils.status_code(status)
173
191
  when Integer
174
192
  @status = status
175
193
  when nil
176
- return @status if @status
194
+ return @status if instance_variable_defined?(:@status) && @status
177
195
  case request.request_method.to_s.upcase
178
196
  when Grape::Http::Headers::POST
179
197
  201
180
198
  when Grape::Http::Headers::DELETE
181
- if @body.present?
199
+ if instance_variable_defined?(:@body) && @body.present?
182
200
  200
183
201
  else
184
202
  204
@@ -229,7 +247,7 @@ module Grape
229
247
  @body = ''
230
248
  status 204
231
249
  else
232
- @body
250
+ instance_variable_defined?(:@body) ? @body : nil
233
251
  end
234
252
  end
235
253
 
@@ -247,23 +265,36 @@ module Grape
247
265
  body false
248
266
  end
249
267
 
250
- # Allows you to define the response as a file-like object.
268
+ # Deprecated method to send files to the client. Use `sendfile` or `stream`
269
+ def file(value = nil)
270
+ if value.is_a?(String)
271
+ warn '[DEPRECATION] Use sendfile or stream to send files.'
272
+ sendfile(value)
273
+ elsif !value.is_a?(NilClass)
274
+ warn '[DEPRECATION] Use stream to use a Stream object.'
275
+ stream(value)
276
+ else
277
+ warn '[DEPRECATION] Use sendfile or stream to send files.'
278
+ sendfile
279
+ end
280
+ end
281
+
282
+ # Allows you to send a file to the client via sendfile.
251
283
  #
252
284
  # @example
253
285
  # get '/file' do
254
- # file FileStreamer.new(...)
286
+ # sendfile FileStreamer.new(...)
255
287
  # end
256
288
  #
257
289
  # GET /file # => "contents of file"
258
- def file(value = nil)
290
+ def sendfile(value = nil)
259
291
  if value.is_a?(String)
260
- file_body = Grape::ServeFile::FileBody.new(value)
261
- @file = Grape::ServeFile::FileResponse.new(file_body)
292
+ file_body = Grape::ServeStream::FileBody.new(value)
293
+ @stream = Grape::ServeStream::StreamResponse.new(file_body)
262
294
  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)
295
+ raise ArgumentError, 'Argument must be a file path'
265
296
  else
266
- @file
297
+ stream
267
298
  end
268
299
  end
269
300
 
@@ -283,10 +314,21 @@ module Grape
283
314
  # * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/chunked.rb
284
315
  # * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/etag.rb
285
316
  def stream(value = nil)
317
+ return if value.nil? && @stream.nil?
318
+
286
319
  header 'Content-Length', nil
287
320
  header 'Transfer-Encoding', nil
288
321
  header 'Cache-Control', 'no-cache' # Skips ETag generation (reading the response up front)
289
- file(value)
322
+ if value.is_a?(String)
323
+ file_body = Grape::ServeStream::FileBody.new(value)
324
+ @stream = Grape::ServeStream::StreamResponse.new(file_body)
325
+ elsif value.respond_to?(:each)
326
+ @stream = Grape::ServeStream::StreamResponse.new(value)
327
+ elsif !value.is_a?(NilClass)
328
+ raise ArgumentError, 'Stream object must respond to :each.'
329
+ else
330
+ @stream
331
+ end
290
332
  end
291
333
 
292
334
  # Allows you to make use of Grape Entities by setting
@@ -322,11 +364,12 @@ module Grape
322
364
  end
323
365
 
324
366
  representation = { root => representation } if root
367
+
325
368
  if key
326
- representation = (@body || {}).merge(key => representation)
327
- elsif entity_class.present? && @body
369
+ representation = (body || {}).merge(key => representation)
370
+ elsif entity_class.present? && body
328
371
  raise ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
329
- representation = @body.merge(representation)
372
+ representation = body.merge(representation)
330
373
  end
331
374
 
332
375
  body representation
@@ -356,7 +399,7 @@ module Grape
356
399
  entity_class = options.delete(:with)
357
400
 
358
401
  if entity_class.nil?
359
- # entity class not explicitely defined, auto-detect from relation#klass or first object in the collection
402
+ # entity class not explicitly defined, auto-detect from relation#klass or first object in the collection
360
403
  object_class = if object.respond_to?(:klass)
361
404
  object.klass
362
405
  else
@@ -378,7 +421,7 @@ module Grape
378
421
  def entity_representation_for(entity_class, object, options)
379
422
  embeds = { env: env }
380
423
  embeds[:version] = env[Grape::Env::API_VERSION] if env[Grape::Env::API_VERSION]
381
- entity_class.represent(object, embeds.merge(options))
424
+ entity_class.represent(object, **embeds.merge(options))
382
425
  end
383
426
  end
384
427
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module DSL
3
5
  module Logger
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
@@ -21,6 +23,13 @@ module Grape
21
23
  namespace_stackable(:middleware, arr)
22
24
  end
23
25
 
26
+ def insert(*args, &block)
27
+ arr = [:insert, *args]
28
+ arr << block if block_given?
29
+
30
+ namespace_stackable(:middleware, arr)
31
+ end
32
+
24
33
  def insert_before(*args, &block)
25
34
  arr = [:insert_before, *args]
26
35
  arr << block if block_given?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
@@ -70,7 +72,7 @@ module Grape
70
72
 
71
73
  # Require one or more parameters for the current endpoint.
72
74
  #
73
- # @param attrs list of parameter names, or, if :using is
75
+ # @param attrs list of parameters names, or, if :using is
74
76
  # passed as an option, which keys to include (:all or :none) from
75
77
  # the :using hash. The last key can be a hash, which specifies
76
78
  # options for the parameters
@@ -125,13 +127,13 @@ module Grape
125
127
 
126
128
  opts = attrs.extract_options!.clone
127
129
  opts[:presence] = { value: true, message: opts[:message] }
128
- opts = @group.merge(opts) if @group
130
+ opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
129
131
 
130
132
  if opts[:using]
131
133
  require_required_and_optional_fields(attrs.first, opts)
132
134
  else
133
135
  validate_attributes(attrs, opts, &block)
134
- block_given? ? new_scope(orig_attrs, &block) : push_declared_params(attrs, opts.slice(:as))
136
+ block_given? ? new_scope(orig_attrs, &block) : push_declared_params(attrs, **opts.slice(:as))
135
137
  end
136
138
  end
137
139
 
@@ -144,7 +146,7 @@ module Grape
144
146
 
145
147
  opts = attrs.extract_options!.clone
146
148
  type = opts[:type]
147
- opts = @group.merge(opts) if @group
149
+ opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
148
150
 
149
151
  # check type for optional parameter group
150
152
  if attrs && block_given?
@@ -157,7 +159,7 @@ module Grape
157
159
  else
158
160
  validate_attributes(attrs, opts, &block)
159
161
 
160
- block_given? ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, opts.slice(:as))
162
+ block_given? ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, **opts.slice(:as))
161
163
  end
162
164
  end
163
165
 
@@ -211,22 +213,31 @@ module Grape
211
213
  # block yet.
212
214
  # @return [Boolean] whether the parameter has been defined
213
215
  def declared_param?(param)
214
- # @declared_params also includes hashes of options and such, but those
215
- # won't be flattened out.
216
- @declared_params.flatten.any? do |declared_param|
217
- first_hash_key_or_param(declared_param) == param
216
+ if lateral?
217
+ # Elements of @declared_params of lateral scope are pushed in @parent. So check them in @parent.
218
+ @parent.declared_param?(param)
219
+ else
220
+ # @declared_params also includes hashes of options and such, but those
221
+ # won't be flattened out.
222
+ @declared_params.flatten.any? do |declared_param|
223
+ first_hash_key_or_param(declared_param) == param
224
+ end
218
225
  end
219
226
  end
220
227
 
221
228
  alias group requires
222
229
 
223
- def map_params(params, element)
230
+ class EmptyOptionalValue; end
231
+
232
+ def map_params(params, element, is_array = false)
224
233
  if params.is_a?(Array)
225
234
  params.map do |el|
226
- map_params(el, element)
235
+ map_params(el, element, true)
227
236
  end
228
237
  elsif params.is_a?(Hash)
229
- params[element] || {}
238
+ params[element] || (@optional && is_array ? EmptyOptionalValue : {})
239
+ elsif params == EmptyOptionalValue
240
+ EmptyOptionalValue
230
241
  else
231
242
  {}
232
243
  end
@@ -236,8 +247,8 @@ module Grape
236
247
  # @return hash of parameters relevant for the current scope
237
248
  # @api private
238
249
  def params(params)
239
- params = @parent.params(params) if @parent
240
- params = map_params(params, @element) if @element
250
+ params = @parent.params(params) if instance_variable_defined?(:@parent) && @parent
251
+ params = map_params(params, @element) if instance_variable_defined?(:@element) && @element
241
252
  params
242
253
  end
243
254
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
@@ -20,7 +22,7 @@ module Grape
20
22
  if new_format
21
23
  namespace_inheritable(:format, new_format.to_sym)
22
24
  # define the default error formatters
23
- namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter.formatter_for(new_format, {}))
25
+ namespace_inheritable(:default_error_formatter, Grape::ErrorFormatter.formatter_for(new_format, **{}))
24
26
  # define a single mime type
25
27
  mime_type = content_types[new_format.to_sym]
26
28
  raise Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type
@@ -43,7 +45,7 @@ module Grape
43
45
  # Specify a default error formatter.
44
46
  def default_error_formatter(new_formatter_name = nil)
45
47
  if new_formatter_name
46
- new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name, {})
48
+ new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name, **{})
47
49
  namespace_inheritable(:default_error_formatter, new_formatter)
48
50
  else
49
51
  namespace_inheritable(:default_error_formatter)