grape 1.2.5 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (262) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -0
  3. data/LICENSE +1 -1
  4. data/README.md +53 -16
  5. data/UPGRADING.md +231 -23
  6. data/grape.gemspec +10 -1
  7. data/lib/grape.rb +6 -7
  8. data/lib/grape/api.rb +4 -2
  9. data/lib/grape/api/helpers.rb +2 -0
  10. data/lib/grape/api/instance.rb +36 -33
  11. data/lib/grape/config.rb +2 -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 +2 -0
  16. data/lib/grape/dsl/configuration.rb +2 -0
  17. data/lib/grape/dsl/desc.rb +2 -0
  18. data/lib/grape/dsl/headers.rb +2 -0
  19. data/lib/grape/dsl/helpers.rb +4 -2
  20. data/lib/grape/dsl/inside_route.rb +83 -34
  21. data/lib/grape/dsl/logger.rb +2 -0
  22. data/lib/grape/dsl/middleware.rb +2 -0
  23. data/lib/grape/dsl/parameters.rb +8 -6
  24. data/lib/grape/dsl/request_response.rb +4 -2
  25. data/lib/grape/dsl/routing.rb +9 -5
  26. data/lib/grape/dsl/settings.rb +7 -1
  27. data/lib/grape/dsl/validations.rb +20 -1
  28. data/lib/grape/eager_load.rb +3 -1
  29. data/lib/grape/endpoint.rb +21 -13
  30. data/lib/grape/error_formatter.rb +3 -1
  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 +11 -13
  36. data/lib/grape/exceptions/incompatible_option_values.rb +2 -0
  37. data/lib/grape/exceptions/invalid_accept_header.rb +2 -0
  38. data/lib/grape/exceptions/invalid_formatter.rb +2 -0
  39. data/lib/grape/exceptions/invalid_message_body.rb +2 -0
  40. data/lib/grape/exceptions/invalid_response.rb +2 -0
  41. data/lib/grape/exceptions/invalid_version_header.rb +2 -0
  42. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -0
  43. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -0
  44. data/lib/grape/exceptions/method_not_allowed.rb +2 -0
  45. data/lib/grape/exceptions/missing_group_type.rb +2 -0
  46. data/lib/grape/exceptions/missing_mime_type.rb +2 -0
  47. data/lib/grape/exceptions/missing_option.rb +2 -0
  48. data/lib/grape/exceptions/missing_vendor_option.rb +2 -0
  49. data/lib/grape/exceptions/unknown_options.rb +2 -0
  50. data/lib/grape/exceptions/unknown_parameter.rb +2 -0
  51. data/lib/grape/exceptions/unknown_validator.rb +2 -0
  52. data/lib/grape/exceptions/unsupported_group_type.rb +2 -0
  53. data/lib/grape/exceptions/validation.rb +3 -1
  54. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  55. data/lib/grape/exceptions/validation_errors.rb +13 -12
  56. data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +4 -3
  57. data/lib/grape/extensions/deep_mergeable_hash.rb +2 -0
  58. data/lib/grape/extensions/deep_symbolize_hash.rb +2 -0
  59. data/lib/grape/extensions/hash.rb +2 -0
  60. data/lib/grape/extensions/hashie/mash.rb +2 -0
  61. data/lib/grape/formatter.rb +5 -3
  62. data/lib/grape/formatter/json.rb +2 -0
  63. data/lib/grape/formatter/serializable_hash.rb +2 -0
  64. data/lib/grape/formatter/txt.rb +2 -0
  65. data/lib/grape/formatter/xml.rb +2 -0
  66. data/lib/grape/http/headers.rb +50 -18
  67. data/lib/grape/middleware/auth/base.rb +2 -0
  68. data/lib/grape/middleware/auth/dsl.rb +2 -0
  69. data/lib/grape/middleware/auth/strategies.rb +2 -0
  70. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  71. data/lib/grape/middleware/base.rb +7 -7
  72. data/lib/grape/middleware/error.rb +3 -1
  73. data/lib/grape/middleware/filter.rb +2 -0
  74. data/lib/grape/middleware/formatter.rb +8 -6
  75. data/lib/grape/middleware/globals.rb +2 -0
  76. data/lib/grape/middleware/helpers.rb +2 -0
  77. data/lib/grape/middleware/stack.rb +4 -1
  78. data/lib/grape/middleware/versioner.rb +2 -0
  79. data/lib/grape/middleware/versioner/accept_version_header.rb +2 -0
  80. data/lib/grape/middleware/versioner/header.rb +6 -4
  81. data/lib/grape/middleware/versioner/param.rb +3 -1
  82. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +4 -1
  83. data/lib/grape/middleware/versioner/path.rb +3 -1
  84. data/lib/grape/namespace.rb +14 -2
  85. data/lib/grape/parser.rb +3 -1
  86. data/lib/grape/parser/json.rb +2 -0
  87. data/lib/grape/parser/xml.rb +2 -0
  88. data/lib/grape/path.rb +15 -3
  89. data/lib/grape/presenters/presenter.rb +2 -0
  90. data/lib/grape/request.rb +15 -8
  91. data/lib/grape/router.rb +30 -29
  92. data/lib/grape/router/attribute_translator.rb +39 -8
  93. data/lib/grape/router/pattern.rb +20 -16
  94. data/lib/grape/router/route.rb +12 -26
  95. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  96. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  97. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  98. data/lib/grape/util/base_inheritable.rb +15 -6
  99. data/lib/grape/util/cache.rb +20 -0
  100. data/lib/grape/util/endpoint_configuration.rb +2 -0
  101. data/lib/grape/util/env.rb +19 -17
  102. data/lib/grape/util/inheritable_setting.rb +2 -0
  103. data/lib/grape/util/inheritable_values.rb +2 -0
  104. data/lib/grape/util/json.rb +2 -0
  105. data/lib/grape/util/lazy_block.rb +2 -0
  106. data/lib/grape/util/lazy_object.rb +43 -0
  107. data/lib/grape/util/lazy_value.rb +2 -0
  108. data/lib/grape/util/registrable.rb +2 -0
  109. data/lib/grape/util/reverse_stackable_values.rb +4 -0
  110. data/lib/grape/util/stackable_values.rb +10 -20
  111. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  112. data/lib/grape/util/xml.rb +2 -0
  113. data/lib/grape/validations.rb +2 -0
  114. data/lib/grape/validations/attributes_iterator.rb +3 -3
  115. data/lib/grape/validations/multiple_attributes_iterator.rb +2 -0
  116. data/lib/grape/validations/params_scope.rb +27 -14
  117. data/lib/grape/validations/single_attribute_iterator.rb +13 -2
  118. data/lib/grape/validations/types.rb +12 -34
  119. data/lib/grape/validations/types/array_coercer.rb +65 -0
  120. data/lib/grape/validations/types/build_coercer.rb +47 -49
  121. data/lib/grape/validations/types/custom_type_coercer.rb +15 -49
  122. data/lib/grape/validations/types/custom_type_collection_coercer.rb +10 -25
  123. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  124. data/lib/grape/validations/types/file.rb +22 -18
  125. data/lib/grape/validations/types/json.rb +46 -39
  126. data/lib/grape/validations/types/multiple_type_coercer.rb +14 -33
  127. data/lib/grape/validations/types/primitive_coercer.rb +67 -0
  128. data/lib/grape/validations/types/set_coercer.rb +40 -0
  129. data/lib/grape/validations/types/variant_collection_coercer.rb +5 -13
  130. data/lib/grape/validations/validator_factory.rb +2 -0
  131. data/lib/grape/validations/validators/all_or_none.rb +3 -1
  132. data/lib/grape/validations/validators/allow_blank.rb +3 -1
  133. data/lib/grape/validations/validators/as.rb +2 -0
  134. data/lib/grape/validations/validators/at_least_one_of.rb +3 -1
  135. data/lib/grape/validations/validators/base.rb +8 -5
  136. data/lib/grape/validations/validators/coerce.rb +39 -29
  137. data/lib/grape/validations/validators/default.rb +2 -1
  138. data/lib/grape/validations/validators/exactly_one_of.rb +6 -2
  139. data/lib/grape/validations/validators/except_values.rb +3 -1
  140. data/lib/grape/validations/validators/multiple_params_base.rb +2 -0
  141. data/lib/grape/validations/validators/mutual_exclusion.rb +3 -1
  142. data/lib/grape/validations/validators/presence.rb +3 -1
  143. data/lib/grape/validations/validators/regexp.rb +4 -2
  144. data/lib/grape/validations/validators/same_as.rb +6 -3
  145. data/lib/grape/validations/validators/values.rb +17 -5
  146. data/lib/grape/version.rb +3 -1
  147. data/spec/grape/api/custom_validations_spec.rb +5 -3
  148. data/spec/grape/api/deeply_included_options_spec.rb +2 -0
  149. data/spec/grape/api/defines_boolean_in_params_spec.rb +5 -3
  150. data/spec/grape/api/inherited_helpers_spec.rb +2 -0
  151. data/spec/grape/api/instance_spec.rb +104 -0
  152. data/spec/grape/api/invalid_format_spec.rb +2 -0
  153. data/spec/grape/api/namespace_parameters_in_route_spec.rb +2 -0
  154. data/spec/grape/api/nested_helpers_spec.rb +2 -0
  155. data/spec/grape/api/optional_parameters_in_route_spec.rb +2 -0
  156. data/spec/grape/api/parameters_modification_spec.rb +3 -1
  157. data/spec/grape/api/patch_method_helpers_spec.rb +2 -0
  158. data/spec/grape/api/recognize_path_spec.rb +2 -0
  159. data/spec/grape/api/required_parameters_in_route_spec.rb +2 -0
  160. data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +2 -0
  161. data/spec/grape/api/routes_with_requirements_spec.rb +2 -0
  162. data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +2 -0
  163. data/spec/grape/api/shared_helpers_spec.rb +2 -0
  164. data/spec/grape/api_remount_spec.rb +2 -0
  165. data/spec/grape/api_spec.rb +99 -11
  166. data/spec/grape/config_spec.rb +2 -0
  167. data/spec/grape/dsl/callbacks_spec.rb +2 -0
  168. data/spec/grape/dsl/configuration_spec.rb +2 -0
  169. data/spec/grape/dsl/desc_spec.rb +2 -0
  170. data/spec/grape/dsl/headers_spec.rb +2 -0
  171. data/spec/grape/dsl/helpers_spec.rb +4 -2
  172. data/spec/grape/dsl/inside_route_spec.rb +177 -33
  173. data/spec/grape/dsl/logger_spec.rb +2 -0
  174. data/spec/grape/dsl/middleware_spec.rb +2 -0
  175. data/spec/grape/dsl/parameters_spec.rb +2 -0
  176. data/spec/grape/dsl/request_response_spec.rb +2 -0
  177. data/spec/grape/dsl/routing_spec.rb +2 -0
  178. data/spec/grape/dsl/settings_spec.rb +2 -0
  179. data/spec/grape/dsl/validations_spec.rb +2 -0
  180. data/spec/grape/endpoint_spec.rb +21 -6
  181. data/spec/grape/entity_spec.rb +2 -0
  182. data/spec/grape/exceptions/base_spec.rb +3 -1
  183. data/spec/grape/exceptions/body_parse_errors_spec.rb +2 -0
  184. data/spec/grape/exceptions/invalid_accept_header_spec.rb +2 -0
  185. data/spec/grape/exceptions/invalid_formatter_spec.rb +2 -0
  186. data/spec/grape/exceptions/invalid_response_spec.rb +2 -0
  187. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -0
  188. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  189. data/spec/grape/exceptions/missing_option_spec.rb +2 -0
  190. data/spec/grape/exceptions/unknown_options_spec.rb +2 -0
  191. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -0
  192. data/spec/grape/exceptions/validation_errors_spec.rb +4 -2
  193. data/spec/grape/exceptions/validation_spec.rb +3 -1
  194. data/spec/grape/extensions/param_builders/hash_spec.rb +2 -0
  195. data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +2 -0
  196. data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +2 -0
  197. data/spec/grape/integration/global_namespace_function_spec.rb +2 -0
  198. data/spec/grape/integration/rack_sendfile_spec.rb +14 -8
  199. data/spec/grape/integration/rack_spec.rb +3 -1
  200. data/spec/grape/loading_spec.rb +2 -0
  201. data/spec/grape/middleware/auth/base_spec.rb +2 -0
  202. data/spec/grape/middleware/auth/dsl_spec.rb +2 -0
  203. data/spec/grape/middleware/auth/strategies_spec.rb +3 -1
  204. data/spec/grape/middleware/base_spec.rb +2 -0
  205. data/spec/grape/middleware/error_spec.rb +2 -0
  206. data/spec/grape/middleware/exception_spec.rb +3 -1
  207. data/spec/grape/middleware/formatter_spec.rb +19 -12
  208. data/spec/grape/middleware/globals_spec.rb +2 -0
  209. data/spec/grape/middleware/stack_spec.rb +11 -0
  210. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  211. data/spec/grape/middleware/versioner/header_spec.rb +3 -1
  212. data/spec/grape/middleware/versioner/param_spec.rb +3 -1
  213. data/spec/grape/middleware/versioner/path_spec.rb +3 -1
  214. data/spec/grape/middleware/versioner_spec.rb +2 -0
  215. data/spec/grape/named_api_spec.rb +2 -0
  216. data/spec/grape/parser_spec.rb +7 -5
  217. data/spec/grape/path_spec.rb +6 -4
  218. data/spec/grape/presenters/presenter_spec.rb +2 -0
  219. data/spec/grape/request_spec.rb +2 -0
  220. data/spec/grape/util/inheritable_setting_spec.rb +2 -0
  221. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  222. data/spec/grape/util/reverse_stackable_values_spec.rb +2 -0
  223. data/spec/grape/util/stackable_values_spec.rb +3 -1
  224. data/spec/grape/util/strict_hash_configuration_spec.rb +2 -0
  225. data/spec/grape/validations/attributes_iterator_spec.rb +2 -0
  226. data/spec/grape/validations/instance_behaivour_spec.rb +5 -3
  227. data/spec/grape/validations/multiple_attributes_iterator_spec.rb +2 -0
  228. data/spec/grape/validations/params_scope_spec.rb +3 -1
  229. data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -4
  230. data/spec/grape/validations/types/array_coercer_spec.rb +35 -0
  231. data/spec/grape/validations/types/primitive_coercer_spec.rb +135 -0
  232. data/spec/grape/validations/types/set_coercer_spec.rb +34 -0
  233. data/spec/grape/validations/types_spec.rb +9 -36
  234. data/spec/grape/validations/validators/all_or_none_spec.rb +2 -0
  235. data/spec/grape/validations/validators/allow_blank_spec.rb +2 -0
  236. data/spec/grape/validations/validators/at_least_one_of_spec.rb +2 -0
  237. data/spec/grape/validations/validators/coerce_spec.rb +341 -136
  238. data/spec/grape/validations/validators/default_spec.rb +123 -0
  239. data/spec/grape/validations/validators/exactly_one_of_spec.rb +14 -12
  240. data/spec/grape/validations/validators/except_values_spec.rb +3 -1
  241. data/spec/grape/validations/validators/mutual_exclusion_spec.rb +2 -0
  242. data/spec/grape/validations/validators/presence_spec.rb +30 -0
  243. data/spec/grape/validations/validators/regexp_spec.rb +2 -0
  244. data/spec/grape/validations/validators/same_as_spec.rb +2 -0
  245. data/spec/grape/validations/validators/values_spec.rb +30 -5
  246. data/spec/grape/validations_spec.rb +91 -33
  247. data/spec/integration/eager_load/eager_load_spec.rb +15 -0
  248. data/spec/integration/multi_json/json_spec.rb +2 -0
  249. data/spec/integration/multi_xml/xml_spec.rb +2 -0
  250. data/spec/shared/versioning_examples.rb +2 -0
  251. data/spec/spec_helper.rb +18 -0
  252. data/spec/support/basic_auth_encode_helpers.rb +2 -0
  253. data/spec/support/content_type_helpers.rb +2 -0
  254. data/spec/support/eager_load.rb +19 -0
  255. data/spec/support/endpoint_faker.rb +2 -0
  256. data/spec/support/file_streamer.rb +2 -0
  257. data/spec/support/integer_helpers.rb +2 -0
  258. data/spec/support/versioned_helpers.rb +4 -2
  259. metadata +48 -28
  260. data/lib/grape/extensions/deep_hash_with_indifferent_access.rb +0 -18
  261. data/lib/grape/util/content_types.rb +0 -26
  262. data/lib/grape/validations/types/virtus_collection_patch.rb +0 -16
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  $LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
2
4
  require 'grape/version'
3
5
 
@@ -11,17 +13,24 @@ Gem::Specification.new do |s|
11
13
  s.summary = 'A simple Ruby framework for building REST-like APIs.'
12
14
  s.description = 'A Ruby framework for rapid API development with great conventions.'
13
15
  s.license = 'MIT'
16
+ s.metadata = {
17
+ 'bug_tracker_uri' => 'https://github.com/ruby-grape/grape/issues',
18
+ 'changelog_uri' => "https://github.com/ruby-grape/grape/blob/v#{s.version}/CHANGELOG.md",
19
+ 'documentation_uri' => "https://www.rubydoc.info/gems/grape/#{s.version}",
20
+ 'source_code_uri' => "https://github.com/ruby-grape/grape/tree/v#{s.version}"
21
+ }
14
22
 
15
23
  s.add_runtime_dependency 'activesupport'
16
24
  s.add_runtime_dependency 'builder'
25
+ s.add_runtime_dependency 'dry-types', '>= 1.1'
17
26
  s.add_runtime_dependency 'mustermann-grape', '~> 1.0.0'
18
27
  s.add_runtime_dependency 'rack', '>= 1.3.0'
19
28
  s.add_runtime_dependency 'rack-accept'
20
- s.add_runtime_dependency 'virtus', '>= 1.0.0'
21
29
 
22
30
  s.files = %w[CHANGELOG.md CONTRIBUTING.md README.md grape.png UPGRADING.md LICENSE]
23
31
  s.files += %w[grape.gemspec]
24
32
  s.files += Dir['lib/**/*']
25
33
  s.test_files = Dir['spec/**/*']
26
34
  s.require_paths = ['lib']
35
+ s.required_ruby_version = '>= 2.4.0'
27
36
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
  require 'rack'
3
5
  require 'rack/builder'
@@ -18,9 +20,6 @@ require 'active_support/core_ext/hash/conversions'
18
20
  require 'active_support/dependencies/autoload'
19
21
  require 'active_support/notifications'
20
22
  require 'i18n'
21
- require 'thread'
22
-
23
- require 'virtus'
24
23
 
25
24
  I18n.load_path << File.expand_path('../grape/locale/en.yml', __FILE__)
26
25
 
@@ -84,7 +83,6 @@ module Grape
84
83
  eager_autoload do
85
84
  autoload :DeepMergeableHash
86
85
  autoload :DeepSymbolizeHash
87
- autoload :DeepHashWithIndifferentAccess
88
86
  autoload :Hash
89
87
  end
90
88
  module ActiveSupport
@@ -208,18 +206,19 @@ module Grape
208
206
  end
209
207
  end
210
208
 
211
- module ServeFile
209
+ module ServeStream
212
210
  extend ::ActiveSupport::Autoload
213
211
  eager_autoload do
214
- autoload :FileResponse
215
212
  autoload :FileBody
216
213
  autoload :SendfileResponse
214
+ autoload :StreamResponse
217
215
  end
218
216
  end
219
217
  end
220
218
 
221
219
  require 'grape/config'
222
- require 'grape/util/content_types'
220
+ require 'grape/content_types'
221
+
223
222
  require 'grape/util/lazy_value'
224
223
  require 'grape/util/lazy_block'
225
224
  require 'grape/util/endpoint_configuration'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/router'
2
4
  require 'grape/api/instance'
3
5
 
@@ -6,7 +8,7 @@ module Grape
6
8
  # should subclass this class in order to build an API.
7
9
  class API
8
10
  # Class methods that we want to call on the API rather than on the API object
9
- NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration compile!]).freeze
11
+ NON_OVERRIDABLE = (Class.new.methods + %i[call call! configuration compile! inherited]).freeze
10
12
 
11
13
  class << self
12
14
  attr_accessor :base_instance, :instances
@@ -173,7 +175,7 @@ module Grape
173
175
  if argument.respond_to?(:lazy?) && argument.lazy?
174
176
  argument.evaluate_from(configuration)
175
177
  elsif argument.is_a?(Hash)
176
- argument.map { |key, value| [key, evaluate_arguments(configuration, value).first] }.to_h
178
+ argument.transform_values { |value| evaluate_arguments(configuration, value).first }
177
179
  elsif argument.is_a?(Array)
178
180
  evaluate_arguments(configuration, *argument)
179
181
  else
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  class API
3
5
  module Helpers
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'grape/router'
2
4
 
3
5
  module Grape
@@ -72,7 +74,7 @@ module Grape
72
74
  # (see #cascade?)
73
75
  def cascade(value = nil)
74
76
  if value.nil?
75
- inheritable_setting.namespace_inheritable.keys.include?(:cascade) ? !namespace_inheritable(:cascade).nil? : true
77
+ inheritable_setting.namespace_inheritable.key?(:cascade) ? !namespace_inheritable(:cascade).nil? : true
76
78
  else
77
79
  namespace_inheritable(:cascade, value)
78
80
  end
@@ -119,7 +121,7 @@ module Grape
119
121
  self.configuration = value_for_configuration
120
122
  response
121
123
  end
122
- if base_instance? && lazy
124
+ if base && base_instance? && lazy
123
125
  lazy_block
124
126
  else
125
127
  lazy_block.evaluate_from(configuration)
@@ -176,7 +178,7 @@ module Grape
176
178
  # errors from reaching upstream. This is effectivelly done by unsetting
177
179
  # X-Cascade. Default :cascade is true.
178
180
  def cascade?
179
- return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.keys.include?(:cascade)
181
+ return self.class.namespace_inheritable(:cascade) if self.class.inheritable_setting.namespace_inheritable.key?(:cascade)
180
182
  return self.class.namespace_inheritable(:version_options)[:cascade] if self.class.namespace_inheritable(:version_options) && self.class.namespace_inheritable(:version_options).key?(:cascade)
181
183
  true
182
184
  end
@@ -190,62 +192,63 @@ module Grape
190
192
  # will return an HTTP 405 response for any HTTP method that the resource
191
193
  # cannot handle.
192
194
  def add_head_not_allowed_methods_and_options_methods
193
- routes_map = {}
194
-
195
- self.class.endpoints.each do |endpoint|
196
- routes = endpoint.routes
197
- routes.each do |route|
198
- # using the :any shorthand produces [nil] for route methods, substitute all manually
199
- route_key = route.pattern.to_regexp
200
- routes_map[route_key] ||= {}
201
- route_settings = routes_map[route_key]
202
- route_settings[:pattern] = route.pattern
203
- route_settings[:requirements] = route.requirements
204
- route_settings[:path] = route.origin
205
- route_settings[:methods] ||= []
206
- route_settings[:methods] << route.request_method
207
- route_settings[:endpoint] = route.app
208
-
209
- # using the :any shorthand produces [nil] for route methods, substitute all manually
210
- route_settings[:methods] = %w[GET PUT POST DELETE PATCH HEAD OPTIONS] if route_settings[:methods].include?('*')
211
- end
212
- end
213
-
195
+ versioned_route_configs = collect_route_config_per_pattern
214
196
  # The paths we collected are prepared (cf. Path#prepare), so they
215
197
  # contain already versioning information when using path versioning.
216
198
  # Disable versioning so adding a route won't prepend versioning
217
199
  # informations again.
218
200
  without_root_prefix do
219
201
  without_versioning do
220
- routes_map.each_value do |config|
221
- methods = config[:methods]
222
- allowed_methods = methods.dup
202
+ versioned_route_configs.each do |config|
203
+ allowed_methods = config[:methods].dup
223
204
 
224
205
  unless self.class.namespace_inheritable(:do_not_route_head)
225
206
  allowed_methods |= [Grape::Http::Headers::HEAD] if allowed_methods.include?(Grape::Http::Headers::GET)
226
207
  end
227
208
 
228
- allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods).join(', ')
209
+ allow_header = (self.class.namespace_inheritable(:do_not_route_options) ? allowed_methods : [Grape::Http::Headers::OPTIONS] | allowed_methods)
229
210
 
230
211
  unless self.class.namespace_inheritable(:do_not_route_options) || allowed_methods.include?(Grape::Http::Headers::OPTIONS)
231
212
  config[:endpoint].options[:options_route_enabled] = true
232
213
  end
233
214
 
234
215
  attributes = config.merge(allowed_methods: allowed_methods, allow_header: allow_header)
235
- generate_not_allowed_method(config[:pattern], attributes)
216
+ generate_not_allowed_method(config[:pattern], **attributes)
236
217
  end
237
218
  end
238
219
  end
239
220
  end
240
221
 
222
+ def collect_route_config_per_pattern
223
+ all_routes = self.class.endpoints.map(&:routes).flatten
224
+ routes_by_regexp = all_routes.group_by { |route| route.pattern.to_regexp }
225
+
226
+ # Build the configuration based on the first endpoint and the collection of methods supported.
227
+ routes_by_regexp.values.map do |routes|
228
+ last_route = routes.last # Most of the configuration is taken from the last endpoint
229
+ matching_wildchar = routes.any? { |route| route.request_method == '*' }
230
+ {
231
+ options: {},
232
+ pattern: last_route.pattern,
233
+ requirements: last_route.requirements,
234
+ path: last_route.origin,
235
+ endpoint: last_route.app,
236
+ methods: matching_wildchar ? Grape::Http::Headers::SUPPORTED_METHODS : routes.map(&:request_method)
237
+ }
238
+ end
239
+ end
240
+
241
241
  # Generate a route that returns an HTTP 405 response for a user defined
242
242
  # path on methods not specified
243
243
  def generate_not_allowed_method(pattern, allowed_methods: [], **attributes)
244
- not_allowed_methods = %w[GET PUT POST DELETE PATCH HEAD] - allowed_methods
245
- not_allowed_methods << Grape::Http::Headers::OPTIONS if self.class.namespace_inheritable(:do_not_route_options)
246
-
244
+ supported_methods =
245
+ if self.class.namespace_inheritable(:do_not_route_options)
246
+ Grape::Http::Headers::SUPPORTED_METHODS
247
+ else
248
+ Grape::Http::Headers::SUPPORTED_METHODS_WITHOUT_OPTIONS
249
+ end
250
+ not_allowed_methods = supported_methods - allowed_methods
247
251
  return if not_allowed_methods.empty?
248
-
249
252
  @router.associate_routes(pattern, not_allowed_methods: not_allowed_methods, **attributes)
250
253
  end
251
254
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module Config
3
5
  class Configuration
@@ -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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  class Cookies
3
5
  def initialize
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module DSL
3
5
  module Desc
@@ -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)
@@ -92,7 +94,7 @@ 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?
96
98
  api.namespace_stackable(:named_params, @named_params)
97
99
  end
98
100
  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,36 +28,38 @@ 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(passed_children_params, params_nested_path_dup) do
62
+ declared(passed_children_params, options, declared_children_params, params_nested_path_dup)
59
63
  end
60
64
  end
61
65
  else
@@ -75,16 +79,32 @@ module Grape
75
79
  end
76
80
  end
77
81
 
78
- def handle_passed_param(declared_param, passed_children_params, &_block)
79
- should_be_empty_array?(declared_param, passed_children_params) ? [] : yield
82
+ def handle_passed_param(passed_children_params, params_nested_path, &_block)
83
+ if should_be_empty_hash?(passed_children_params, params_nested_path)
84
+ {}
85
+ elsif should_be_empty_array?(passed_children_params, params_nested_path)
86
+ []
87
+ else
88
+ yield
89
+ end
90
+ end
91
+
92
+ def should_be_empty_array?(passed_children_params, params_nested_path)
93
+ passed_children_params.empty? && declared_param_is_array?(params_nested_path)
80
94
  end
81
95
 
82
- def should_be_empty_array?(declared_param, passed_children_params)
83
- declared_param_is_array?(declared_param) && passed_children_params.empty?
96
+ def declared_param_is_array?(params_nested_path)
97
+ key = route_options_params_key(params_nested_path)
98
+ route_options_params[key] && route_options_params[key][:type] == 'Array'
84
99
  end
85
100
 
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'
101
+ def should_be_empty_hash?(passed_children_params, params_nested_path)
102
+ passed_children_params.empty? && declared_param_is_hash?(params_nested_path)
103
+ end
104
+
105
+ def declared_param_is_hash?(params_nested_path)
106
+ key = route_options_params_key(params_nested_path)
107
+ route_options_params[key] && route_options_params[key][:type] == 'Hash'
88
108
  end
89
109
 
90
110
  def route_options_params
@@ -95,13 +115,19 @@ module Grape
95
115
  options[:stringify] ? declared_param.to_s : declared_param.to_sym
96
116
  end
97
117
 
98
- def optioned_declared_params(options)
118
+ def route_options_params_key(params_nested_path)
119
+ key = params_nested_path[0]
120
+ key += '[' + params_nested_path[1..-1].join('][') + ']' if params_nested_path.size > 1
121
+ key
122
+ end
123
+
124
+ def optioned_declared_params(**options)
99
125
  declared_params = if options[:include_parent_namespaces]
100
126
  # Declared params including parent namespaces
101
- route_setting(:saved_declared_params).flatten | Array(route_setting(:declared_params))
127
+ route_setting(:declared_params)
102
128
  else
103
129
  # Declared params at current namespace
104
- route_setting(:saved_declared_params).last & Array(route_setting(:declared_params))
130
+ namespace_stackable(:declared_params).last || []
105
131
  end
106
132
 
107
133
  raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
@@ -174,17 +200,17 @@ module Grape
174
200
  def status(status = nil)
175
201
  case status
176
202
  when Symbol
177
- raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.keys.include?(status)
203
+ raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(status)
178
204
  @status = Rack::Utils.status_code(status)
179
205
  when Integer
180
206
  @status = status
181
207
  when nil
182
- return @status if @status
208
+ return @status if instance_variable_defined?(:@status) && @status
183
209
  case request.request_method.to_s.upcase
184
210
  when Grape::Http::Headers::POST
185
211
  201
186
212
  when Grape::Http::Headers::DELETE
187
- if @body.present?
213
+ if instance_variable_defined?(:@body) && @body.present?
188
214
  200
189
215
  else
190
216
  204
@@ -235,7 +261,7 @@ module Grape
235
261
  @body = ''
236
262
  status 204
237
263
  else
238
- @body
264
+ instance_variable_defined?(:@body) ? @body : nil
239
265
  end
240
266
  end
241
267
 
@@ -253,23 +279,36 @@ module Grape
253
279
  body false
254
280
  end
255
281
 
256
- # Allows you to define the response as a file-like object.
282
+ # Deprecated method to send files to the client. Use `sendfile` or `stream`
283
+ def file(value = nil)
284
+ if value.is_a?(String)
285
+ warn '[DEPRECATION] Use sendfile or stream to send files.'
286
+ sendfile(value)
287
+ elsif !value.is_a?(NilClass)
288
+ warn '[DEPRECATION] Use stream to use a Stream object.'
289
+ stream(value)
290
+ else
291
+ warn '[DEPRECATION] Use sendfile or stream to send files.'
292
+ sendfile
293
+ end
294
+ end
295
+
296
+ # Allows you to send a file to the client via sendfile.
257
297
  #
258
298
  # @example
259
299
  # get '/file' do
260
- # file FileStreamer.new(...)
300
+ # sendfile FileStreamer.new(...)
261
301
  # end
262
302
  #
263
303
  # GET /file # => "contents of file"
264
- def file(value = nil)
304
+ def sendfile(value = nil)
265
305
  if value.is_a?(String)
266
- file_body = Grape::ServeFile::FileBody.new(value)
267
- @file = Grape::ServeFile::FileResponse.new(file_body)
306
+ file_body = Grape::ServeStream::FileBody.new(value)
307
+ @stream = Grape::ServeStream::StreamResponse.new(file_body)
268
308
  elsif !value.is_a?(NilClass)
269
- warn '[DEPRECATION] Argument as FileStreamer-like object is deprecated. Use path to file instead.'
270
- @file = Grape::ServeFile::FileResponse.new(value)
309
+ raise ArgumentError, 'Argument must be a file path'
271
310
  else
272
- @file
311
+ stream
273
312
  end
274
313
  end
275
314
 
@@ -292,7 +331,16 @@ module Grape
292
331
  header 'Content-Length', nil
293
332
  header 'Transfer-Encoding', nil
294
333
  header 'Cache-Control', 'no-cache' # Skips ETag generation (reading the response up front)
295
- 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
+ instance_variable_defined?(:@stream) ? @stream : nil
343
+ end
296
344
  end
297
345
 
298
346
  # Allows you to make use of Grape Entities by setting
@@ -328,11 +376,12 @@ module Grape
328
376
  end
329
377
 
330
378
  representation = { root => representation } if root
379
+
331
380
  if key
332
- representation = (@body || {}).merge(key => representation)
333
- elsif entity_class.present? && @body
381
+ representation = (body || {}).merge(key => representation)
382
+ elsif entity_class.present? && body
334
383
  raise ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
335
- representation = @body.merge(representation)
384
+ representation = body.merge(representation)
336
385
  end
337
386
 
338
387
  body representation