grape 1.0.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (284) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +346 -39
  3. data/LICENSE +1 -1
  4. data/README.md +658 -90
  5. data/UPGRADING.md +472 -17
  6. data/grape.gemspec +17 -6
  7. data/lib/grape/api/helpers.rb +2 -0
  8. data/lib/grape/api/instance.rb +279 -0
  9. data/lib/grape/api.rb +132 -176
  10. data/lib/grape/config.rb +34 -0
  11. data/lib/grape/content_types.rb +34 -0
  12. data/lib/grape/cookies.rb +4 -0
  13. data/lib/grape/dsl/api.rb +2 -0
  14. data/lib/grape/dsl/callbacks.rb +22 -0
  15. data/lib/grape/dsl/configuration.rb +2 -0
  16. data/lib/grape/dsl/desc.rb +44 -12
  17. data/lib/grape/dsl/headers.rb +2 -0
  18. data/lib/grape/dsl/helpers.rb +11 -6
  19. data/lib/grape/dsl/inside_route.rb +116 -36
  20. data/lib/grape/dsl/logger.rb +2 -0
  21. data/lib/grape/dsl/middleware.rb +12 -3
  22. data/lib/grape/dsl/parameters.rb +34 -16
  23. data/lib/grape/dsl/request_response.rb +13 -8
  24. data/lib/grape/dsl/routing.rb +19 -12
  25. data/lib/grape/dsl/settings.rb +22 -4
  26. data/lib/grape/dsl/validations.rb +24 -4
  27. data/lib/grape/eager_load.rb +20 -0
  28. data/lib/grape/endpoint.rb +66 -57
  29. data/lib/grape/error_formatter/base.rb +2 -0
  30. data/lib/grape/error_formatter/json.rb +6 -4
  31. data/lib/grape/error_formatter/txt.rb +10 -3
  32. data/lib/grape/error_formatter/xml.rb +6 -4
  33. data/lib/grape/error_formatter.rb +4 -2
  34. data/lib/grape/exceptions/base.rb +22 -14
  35. data/lib/grape/exceptions/empty_message_body.rb +11 -0
  36. data/lib/grape/exceptions/incompatible_option_values.rb +2 -1
  37. data/lib/grape/exceptions/invalid_accept_header.rb +2 -1
  38. data/lib/grape/exceptions/invalid_formatter.rb +2 -1
  39. data/lib/grape/exceptions/invalid_message_body.rb +2 -1
  40. data/lib/grape/exceptions/invalid_response.rb +11 -0
  41. data/lib/grape/exceptions/invalid_version_header.rb +2 -1
  42. data/lib/grape/exceptions/invalid_versioner_option.rb +2 -1
  43. data/lib/grape/exceptions/invalid_with_option_for_represent.rb +2 -1
  44. data/lib/grape/exceptions/method_not_allowed.rb +2 -1
  45. data/lib/grape/exceptions/missing_group_type.rb +2 -1
  46. data/lib/grape/exceptions/missing_mime_type.rb +2 -1
  47. data/lib/grape/exceptions/missing_option.rb +2 -1
  48. data/lib/grape/exceptions/missing_vendor_option.rb +2 -1
  49. data/lib/grape/exceptions/unknown_options.rb +2 -1
  50. data/lib/grape/exceptions/unknown_parameter.rb +2 -1
  51. data/lib/grape/exceptions/unknown_validator.rb +2 -1
  52. data/lib/grape/exceptions/unsupported_group_type.rb +2 -1
  53. data/lib/grape/exceptions/validation.rb +5 -4
  54. data/lib/grape/exceptions/validation_array_errors.rb +2 -0
  55. data/lib/grape/exceptions/validation_errors.rb +16 -13
  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/json.rb +3 -0
  62. data/lib/grape/formatter/serializable_hash.rb +4 -1
  63. data/lib/grape/formatter/txt.rb +2 -0
  64. data/lib/grape/formatter/xml.rb +3 -0
  65. data/lib/grape/formatter.rb +5 -3
  66. data/lib/grape/http/headers.rb +50 -18
  67. data/lib/grape/locale/en.yml +3 -1
  68. data/lib/grape/middleware/auth/base.rb +7 -7
  69. data/lib/grape/middleware/auth/dsl.rb +2 -0
  70. data/lib/grape/middleware/auth/strategies.rb +2 -0
  71. data/lib/grape/middleware/auth/strategy_info.rb +2 -0
  72. data/lib/grape/middleware/base.rb +12 -7
  73. data/lib/grape/middleware/error.rb +75 -61
  74. data/lib/grape/middleware/filter.rb +2 -0
  75. data/lib/grape/middleware/formatter.rb +17 -13
  76. data/lib/grape/middleware/globals.rb +2 -0
  77. data/lib/grape/middleware/helpers.rb +12 -0
  78. data/lib/grape/middleware/stack.rb +15 -5
  79. data/lib/grape/middleware/versioner/accept_version_header.rb +5 -5
  80. data/lib/grape/middleware/versioner/header.rb +13 -9
  81. data/lib/grape/middleware/versioner/param.rb +4 -1
  82. data/lib/grape/middleware/versioner/parse_media_type_patch.rb +5 -1
  83. data/lib/grape/middleware/versioner/path.rb +5 -1
  84. data/lib/grape/middleware/versioner.rb +2 -0
  85. data/lib/grape/namespace.rb +15 -3
  86. data/lib/grape/parser/json.rb +3 -1
  87. data/lib/grape/parser/xml.rb +3 -1
  88. data/lib/grape/parser.rb +4 -2
  89. data/lib/grape/path.rb +16 -3
  90. data/lib/grape/presenters/presenter.rb +2 -0
  91. data/lib/grape/request.rb +19 -9
  92. data/lib/grape/router/attribute_translator.rb +41 -8
  93. data/lib/grape/router/pattern.rb +22 -18
  94. data/lib/grape/router/route.rb +16 -30
  95. data/lib/grape/router.rb +37 -28
  96. data/lib/grape/{serve_file → serve_stream}/file_body.rb +3 -1
  97. data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +3 -1
  98. data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +10 -8
  99. data/lib/grape/util/base_inheritable.rb +43 -0
  100. data/lib/grape/util/cache.rb +20 -0
  101. data/lib/grape/util/endpoint_configuration.rb +8 -0
  102. data/lib/grape/util/env.rb +19 -17
  103. data/lib/grape/util/inheritable_setting.rb +3 -3
  104. data/lib/grape/util/inheritable_values.rb +7 -25
  105. data/lib/grape/util/json.rb +2 -0
  106. data/lib/grape/util/lazy_block.rb +27 -0
  107. data/lib/grape/util/lazy_object.rb +43 -0
  108. data/lib/grape/util/lazy_value.rb +99 -0
  109. data/lib/grape/util/registrable.rb +2 -0
  110. data/lib/grape/util/reverse_stackable_values.rb +10 -35
  111. data/lib/grape/util/stackable_values.rb +21 -34
  112. data/lib/grape/util/strict_hash_configuration.rb +2 -0
  113. data/lib/grape/util/xml.rb +2 -0
  114. data/lib/grape/validations/attributes_iterator.rb +16 -6
  115. data/lib/grape/validations/multiple_attributes_iterator.rb +13 -0
  116. data/lib/grape/validations/params_scope.rb +128 -66
  117. data/lib/grape/validations/single_attribute_iterator.rb +24 -0
  118. data/lib/grape/validations/types/array_coercer.rb +65 -0
  119. data/lib/grape/validations/types/build_coercer.rb +52 -46
  120. data/lib/grape/validations/types/custom_type_coercer.rb +30 -51
  121. data/lib/grape/validations/types/custom_type_collection_coercer.rb +56 -0
  122. data/lib/grape/validations/types/dry_type_coercer.rb +76 -0
  123. data/lib/grape/validations/types/file.rb +22 -18
  124. data/lib/grape/validations/types/invalid_value.rb +24 -0
  125. data/lib/grape/validations/types/json.rb +47 -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/types.rb +26 -38
  131. data/lib/grape/validations/validator_factory.rb +8 -11
  132. data/lib/grape/validations/validators/all_or_none.rb +8 -12
  133. data/lib/grape/validations/validators/allow_blank.rb +4 -2
  134. data/lib/grape/validations/validators/as.rb +12 -0
  135. data/lib/grape/validations/validators/at_least_one_of.rb +7 -12
  136. data/lib/grape/validations/validators/base.rb +23 -15
  137. data/lib/grape/validations/validators/coerce.rb +47 -28
  138. data/lib/grape/validations/validators/default.rb +7 -6
  139. data/lib/grape/validations/validators/exactly_one_of.rb +10 -22
  140. data/lib/grape/validations/validators/except_values.rb +4 -2
  141. data/lib/grape/validations/validators/multiple_params_base.rb +19 -10
  142. data/lib/grape/validations/validators/mutual_exclusion.rb +8 -17
  143. data/lib/grape/validations/validators/presence.rb +4 -1
  144. data/lib/grape/validations/validators/regexp.rb +5 -2
  145. data/lib/grape/validations/validators/same_as.rb +27 -0
  146. data/lib/grape/validations/validators/values.rb +29 -9
  147. data/lib/grape/validations.rb +2 -0
  148. data/lib/grape/version.rb +3 -1
  149. data/lib/grape.rb +107 -73
  150. data/spec/grape/api/custom_validations_spec.rb +6 -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 +116 -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 +49 -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 +775 -65
  169. data/spec/grape/config_spec.rb +19 -0
  170. data/spec/grape/dsl/callbacks_spec.rb +3 -1
  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 +23 -2
  175. data/spec/grape/dsl/inside_route_spec.rb +185 -34
  176. data/spec/grape/dsl/logger_spec.rb +2 -0
  177. data/spec/grape/dsl/middleware_spec.rb +11 -1
  178. data/spec/grape/dsl/parameters_spec.rb +12 -9
  179. data/spec/grape/dsl/request_response_spec.rb +2 -0
  180. data/spec/grape/dsl/routing_spec.rb +13 -1
  181. data/spec/grape/dsl/settings_spec.rb +4 -2
  182. data/spec/grape/dsl/validations_spec.rb +2 -0
  183. data/spec/grape/endpoint/declared_spec.rb +848 -0
  184. data/spec/grape/endpoint_spec.rb +75 -515
  185. data/spec/grape/entity_spec.rb +19 -11
  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 -1
  190. data/spec/grape/exceptions/invalid_response_spec.rb +13 -0
  191. data/spec/grape/exceptions/invalid_versioner_option_spec.rb +2 -1
  192. data/spec/grape/exceptions/missing_mime_type_spec.rb +2 -0
  193. data/spec/grape/exceptions/missing_option_spec.rb +2 -1
  194. data/spec/grape/exceptions/unknown_options_spec.rb +3 -2
  195. data/spec/grape/exceptions/unknown_validator_spec.rb +2 -1
  196. data/spec/grape/exceptions/validation_errors_spec.rb +17 -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 +6 -4
  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 +4 -3
  210. data/spec/grape/middleware/exception_spec.rb +205 -55
  211. data/spec/grape/middleware/formatter_spec.rb +130 -15
  212. data/spec/grape/middleware/globals_spec.rb +2 -0
  213. data/spec/grape/middleware/stack_spec.rb +15 -1
  214. data/spec/grape/middleware/versioner/accept_version_header_spec.rb +3 -1
  215. data/spec/grape/middleware/versioner/header_spec.rb +10 -2
  216. data/spec/grape/middleware/versioner/param_spec.rb +4 -2
  217. data/spec/grape/middleware/versioner/path_spec.rb +4 -2
  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 +9 -7
  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 +4 -2
  225. data/spec/grape/util/inheritable_values_spec.rb +2 -0
  226. data/spec/grape/util/reverse_stackable_values_spec.rb +15 -13
  227. data/spec/grape/util/stackable_values_spec.rb +16 -14
  228. data/spec/grape/util/strict_hash_configuration_spec.rb +3 -1
  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 +364 -7
  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 +558 -133
  242. data/spec/grape/validations/validators/default_spec.rb +212 -0
  243. data/spec/grape/validations/validators/exactly_one_of_spec.rb +204 -38
  244. data/spec/grape/validations/validators/except_values_spec.rb +6 -3
  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 +73 -19
  250. data/spec/grape/validations_spec.rb +403 -53
  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 +78 -18
  255. data/spec/spec_helper.rb +13 -2
  256. data/spec/support/basic_auth_encode_helpers.rb +3 -1
  257. data/spec/support/chunks.rb +14 -0
  258. data/spec/support/content_type_helpers.rb +3 -1
  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 +176 -130
  265. data/Appraisals +0 -32
  266. data/Dangerfile +0 -1
  267. data/Gemfile +0 -34
  268. data/Gemfile.lock +0 -229
  269. data/Guardfile +0 -10
  270. data/RELEASING.md +0 -111
  271. data/Rakefile +0 -70
  272. data/benchmark/simple.rb +0 -27
  273. data/benchmark/simple_with_type_coercer.rb +0 -22
  274. data/gemfiles/multi_json.gemfile +0 -36
  275. data/gemfiles/multi_xml.gemfile +0 -36
  276. data/gemfiles/rack_1.5.2.gemfile +0 -36
  277. data/gemfiles/rack_edge.gemfile +0 -36
  278. data/gemfiles/rails_3.gemfile +0 -37
  279. data/gemfiles/rails_4.gemfile +0 -36
  280. data/gemfiles/rails_5.gemfile +0 -36
  281. data/gemfiles/rails_edge.gemfile +0 -36
  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
@@ -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,14 +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
-
135
- block_given? ? new_scope(orig_attrs, &block) : push_declared_params(attrs)
136
+ block ? new_scope(orig_attrs, &block) : push_declared_params(attrs, **opts.slice(:as))
136
137
  end
137
138
  end
138
139
 
@@ -145,10 +146,10 @@ module Grape
145
146
 
146
147
  opts = attrs.extract_options!.clone
147
148
  type = opts[:type]
148
- opts = @group.merge(opts) if @group
149
+ opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
149
150
 
150
151
  # check type for optional parameter group
151
- if attrs && block_given?
152
+ if attrs && block
152
153
  raise Grape::Exceptions::MissingGroupTypeError.new if type.nil?
153
154
  raise Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
154
155
  end
@@ -158,7 +159,7 @@ module Grape
158
159
  else
159
160
  validate_attributes(attrs, opts, &block)
160
161
 
161
- block_given? ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs)
162
+ block ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, **opts.slice(:as))
162
163
  end
163
164
  end
164
165
 
@@ -202,7 +203,7 @@ module Grape
202
203
  # @yield a parameter definition DSL
203
204
  def given(*attrs, &block)
204
205
  attrs.each do |attr|
205
- proxy_attr = attr.is_a?(Hash) ? attr.keys[0] : attr
206
+ proxy_attr = first_hash_key_or_param(attr)
206
207
  raise Grape::Exceptions::UnknownParameter.new(proxy_attr) unless declared_param?(proxy_attr)
207
208
  end
208
209
  new_lateral_scope(dependent_on: attrs, &block)
@@ -212,20 +213,31 @@ module Grape
212
213
  # block yet.
213
214
  # @return [Boolean] whether the parameter has been defined
214
215
  def declared_param?(param)
215
- # @declared_params also includes hashes of options and such, but those
216
- # won't be flattened out.
217
- @declared_params.flatten.include?(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
225
+ end
218
226
  end
219
227
 
220
228
  alias group requires
221
229
 
222
- def map_params(params, element)
230
+ class EmptyOptionalValue; end
231
+
232
+ def map_params(params, element, is_array = false)
223
233
  if params.is_a?(Array)
224
234
  params.map do |el|
225
- map_params(el, element)
235
+ map_params(el, element, true)
226
236
  end
227
237
  elsif params.is_a?(Hash)
228
- params[element] || {}
238
+ params[element] || (@optional && is_array ? EmptyOptionalValue : {})
239
+ elsif params == EmptyOptionalValue
240
+ EmptyOptionalValue
229
241
  else
230
242
  {}
231
243
  end
@@ -235,10 +247,16 @@ module Grape
235
247
  # @return hash of parameters relevant for the current scope
236
248
  # @api private
237
249
  def params(params)
238
- params = @parent.params(params) if @parent
239
- 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
240
252
  params
241
253
  end
254
+
255
+ private
256
+
257
+ def first_hash_key_or_param(parameter)
258
+ parameter.is_a?(Hash) ? parameter.keys.first : parameter
259
+ end
242
260
  end
243
261
  end
244
262
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
@@ -20,10 +22,11 @@ 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
29
+
27
30
  namespace_stackable(:content_types, new_format.to_sym => mime_type)
28
31
  else
29
32
  namespace_inheritable(:format)
@@ -43,7 +46,7 @@ module Grape
43
46
  # Specify a default error formatter.
44
47
  def default_error_formatter(new_formatter_name = nil)
45
48
  if new_formatter_name
46
- new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name, {})
49
+ new_formatter = Grape::ErrorFormatter.formatter_for(new_formatter_name, **{})
47
50
  namespace_inheritable(:default_error_formatter, new_formatter)
48
51
  else
49
52
  namespace_inheritable(:default_error_formatter)
@@ -100,14 +103,13 @@ module Grape
100
103
  def rescue_from(*args, &block)
101
104
  if args.last.is_a?(Proc)
102
105
  handler = args.pop
103
- elsif block_given?
106
+ elsif block
104
107
  handler = block
105
108
  end
106
109
 
107
110
  options = args.extract_options!
108
- if block_given? && options.key?(:with)
109
- raise ArgumentError, 'both :with option and block cannot be passed'
110
- end
111
+ raise ArgumentError, 'both :with option and block cannot be passed' if block && options.key?(:with)
112
+
111
113
  handler ||= extract_with(options)
112
114
 
113
115
  if args.include?(:all)
@@ -125,7 +127,7 @@ module Grape
125
127
  :base_only_rescue_handlers
126
128
  end
127
129
 
128
- namespace_reverse_stackable handler_type, Hash[args.map { |arg| [arg, handler] }]
130
+ namespace_reverse_stackable handler_type, args.map { |arg| [arg, handler] }.to_h
129
131
  end
130
132
 
131
133
  namespace_stackable(:rescue_options, options)
@@ -152,7 +154,8 @@ module Grape
152
154
  # @param model_class [Class] The model class that will be represented.
153
155
  # @option options [Class] :with The entity class that will represent the model.
154
156
  def represent(model_class, options)
155
- raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with] && options[:with].is_a?(Class)
157
+ raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with].is_a?(Class)
158
+
156
159
  namespace_stackable(:representations, model_class => options[:with])
157
160
  end
158
161
 
@@ -160,9 +163,11 @@ module Grape
160
163
 
161
164
  def extract_with(options)
162
165
  return unless options.key?(:with)
166
+
163
167
  with_option = options.delete(:with)
164
168
  return with_option if with_option.instance_of?(Proc)
165
169
  return with_option.to_sym if with_option.instance_of?(Symbol) || with_option.instance_of?(String)
170
+
166
171
  raise ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
167
172
  end
168
173
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
@@ -36,7 +38,7 @@ module Grape
36
38
 
37
39
  @versions = versions | requested_versions
38
40
 
39
- if block_given?
41
+ if block
40
42
  within_namespace do
41
43
  namespace_inheritable(:version, requested_versions)
42
44
  namespace_inheritable(:version_options, options)
@@ -49,7 +51,7 @@ module Grape
49
51
  end
50
52
  end
51
53
 
52
- @versions.last unless @versions.nil?
54
+ @versions.last if instance_variable_defined?(:@versions) && @versions
53
55
  end
54
56
 
55
57
  # Define a root URL prefix for your entire API.
@@ -77,9 +79,14 @@ module Grape
77
79
  namespace_inheritable(:do_not_route_options, true)
78
80
  end
79
81
 
80
- def mount(mounts)
82
+ def mount(mounts, *opts)
81
83
  mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
82
84
  mounts.each_pair do |app, path|
85
+ if app.respond_to?(:mount_instance)
86
+ opts_with = opts.any? ? opts.shift[:with] : {}
87
+ mount({ app.mount_instance(configuration: opts_with) => path })
88
+ next
89
+ end
83
90
  in_setting = inheritable_setting
84
91
 
85
92
  if app.respond_to?(:inheritable_setting, true)
@@ -136,16 +143,16 @@ module Grape
136
143
  reset_validations!
137
144
  end
138
145
 
139
- %w(get post put head delete options patch).each do |meth|
140
- define_method meth do |*args, &block|
146
+ Grape::Http::Headers::SUPPORTED_METHODS.each do |supported_method|
147
+ define_method supported_method.downcase do |*args, &block|
141
148
  options = args.extract_options!
142
149
  paths = args.first || ['/']
143
- route(meth.upcase, paths, options, &block)
150
+ route(supported_method, paths, options, &block)
144
151
  end
145
152
  end
146
153
 
147
154
  # Declare a "namespace", which prefixes all subordinate routes with its
148
- # name. Any endpoints within a namespace, or group, resource, segment,
155
+ # name. Any endpoints within a namespace, group, resource or segment,
149
156
  # etc., will share their parent context as well as any configuration
150
157
  # done in the namespace context.
151
158
  #
@@ -157,14 +164,14 @@ module Grape
157
164
  # end
158
165
  # end
159
166
  def namespace(space = nil, options = {}, &block)
160
- if space || block_given?
167
+ @namespace_description = nil unless instance_variable_defined?(:@namespace_description) && @namespace_description
168
+
169
+ if space || block
161
170
  within_namespace do
162
171
  previous_namespace_description = @namespace_description
163
172
  @namespace_description = (@namespace_description || {}).deep_merge(namespace_setting(:description) || {})
164
173
  nest(block) do
165
- if space
166
- namespace_stackable(:namespace, Namespace.new(space, options))
167
- end
174
+ namespace_stackable(:namespace, Namespace.new(space, **options)) if space
168
175
  end
169
176
  @namespace_description = previous_namespace_description
170
177
  end
@@ -193,7 +200,7 @@ module Grape
193
200
  @endpoints = []
194
201
  end
195
202
 
196
- # Thie method allows you to quickly define a parameter route segment
203
+ # This method allows you to quickly define a parameter route segment
197
204
  # in your API.
198
205
  #
199
206
  # @param param [Symbol] The name of the parameter you wish to declare.
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
4
6
  module DSL
5
- # Keeps track of settings (impemented as key-value pairs, grouped by
7
+ # Keeps track of settings (implemented as key-value pairs, grouped by
6
8
  # types), in two contexts: top-level settings which apply globally no
7
9
  # matter where they're defined, and inheritable settings which apply only
8
10
  # in the current scope and scopes nested under it.
@@ -13,7 +15,7 @@ module Grape
13
15
 
14
16
  # Fetch our top-level settings, which apply to all endpoints in the API.
15
17
  def top_level_setting
16
- @top_level_setting ||= Grape::Util::InheritableSetting.new
18
+ @top_level_setting ||= build_top_level_setting
17
19
  end
18
20
 
19
21
  # Fetch our current inheritable settings, which are inherited by
@@ -101,12 +103,14 @@ module Grape
101
103
  def namespace_stackable_with_hash(key)
102
104
  settings = get_or_set :namespace_stackable, key, nil
103
105
  return if settings.blank?
106
+
104
107
  settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
105
108
  end
106
109
 
107
110
  def namespace_reverse_stackable_with_hash(key)
108
111
  settings = get_or_set :namespace_reverse_stackable, key, nil
109
112
  return if settings.blank?
113
+
110
114
  result = {}
111
115
  settings.each do |setting|
112
116
  setting.each do |field, value|
@@ -152,16 +156,30 @@ module Grape
152
156
 
153
157
  # Execute the block within a context where our inheritable settings are forked
154
158
  # to a new copy (see #namespace_start).
155
- def within_namespace(&_block)
159
+ def within_namespace(&block)
156
160
  namespace_start
157
161
 
158
- result = yield if block_given?
162
+ result = yield if block
159
163
 
160
164
  namespace_end
161
165
  reset_validations!
162
166
 
163
167
  result
164
168
  end
169
+
170
+ private
171
+
172
+ # Builds the current class :inheritable_setting. If available, it inherits from
173
+ # the superclass's :inheritable_setting.
174
+ def build_top_level_setting
175
+ Grape::Util::InheritableSetting.new.tap do |setting|
176
+ # Doesn't try to inherit settings from +Grape::API::Instance+ which also responds to
177
+ # +inheritable_setting+, however, it doesn't contain any user-defined settings.
178
+ # Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
179
+ # in the chain for every endpoint.
180
+ setting.inherit_from superclass.inheritable_setting if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
181
+ end
182
+ end
165
183
  end
166
184
  end
167
185
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/concern'
2
4
 
3
5
  module Grape
@@ -8,7 +10,24 @@ module Grape
8
10
  include Grape::DSL::Configuration
9
11
 
10
12
  module ClassMethods
11
- # Clears all defined parameters and validations.
13
+ # Clears all defined parameters and validations. The main purpose of it is to clean up
14
+ # settings, so next endpoint won't interfere with previous one.
15
+ #
16
+ # params do
17
+ # # params for the endpoint below this block
18
+ # end
19
+ # post '/current' do
20
+ # # whatever
21
+ # end
22
+ #
23
+ # # somewhere between them the reset_validations! method gets called
24
+ #
25
+ # params do
26
+ # # params for the endpoint below this block
27
+ # end
28
+ # post '/next' do
29
+ # # whatever
30
+ # end
12
31
  def reset_validations!
13
32
  unset_namespace_stackable :declared_params
14
33
  unset_namespace_stackable :validations
@@ -27,10 +46,11 @@ module Grape
27
46
  setting = description_field(:params)
28
47
  setting ||= description_field(:params, {})
29
48
  Array(names).each do |name|
30
- setting[name[:full_name].to_s] ||= {}
31
- setting[name[:full_name].to_s].merge!(opts)
49
+ full_name = name[:full_name].to_s
50
+ setting[full_name] ||= {}
51
+ setting[full_name].merge!(opts)
32
52
 
33
- namespace_stackable(:params, name[:full_name].to_s => opts)
53
+ namespace_stackable(:params, full_name => opts)
34
54
  end
35
55
  end
36
56
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ Grape.eager_load!
4
+ Grape::Http.eager_load!
5
+ Grape::Exceptions.eager_load!
6
+ Grape::Extensions.eager_load!
7
+ Grape::Extensions::ActiveSupport.eager_load!
8
+ Grape::Extensions::Hashie.eager_load!
9
+ Grape::Middleware.eager_load!
10
+ Grape::Middleware::Auth.eager_load!
11
+ Grape::Middleware::Versioner.eager_load!
12
+ Grape::Util.eager_load!
13
+ Grape::ErrorFormatter.eager_load!
14
+ Grape::Formatter.eager_load!
15
+ Grape::Parser.eager_load!
16
+ Grape::DSL.eager_load!
17
+ Grape::API.eager_load!
18
+ Grape::Presenters.eager_load!
19
+ Grape::ServeStream.eager_load!
20
+ Rack::Head # AutoLoads the Rack::Head
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  # An Endpoint is the proxy scope in which all routing
3
5
  # blocks are executed. In other words, any methods
@@ -18,7 +20,8 @@ module Grape
18
20
  def before_each(new_setup = false, &block)
19
21
  @before_each ||= []
20
22
  if new_setup == false
21
- return @before_each unless block_given?
23
+ return @before_each unless block
24
+
22
25
  @before_each << block
23
26
  else
24
27
  @before_each = [new_setup]
@@ -44,9 +47,7 @@ module Grape
44
47
  # @return [Proc]
45
48
  # @raise [NameError] an instance method with the same name already exists
46
49
  def generate_api_method(method_name, &block)
47
- if instance_methods.include?(method_name.to_sym) || instance_methods.include?(method_name.to_s)
48
- raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name")
49
- end
50
+ raise NameError.new("method #{method_name.inspect} already exists and cannot be used as an unbound method name") if method_defined?(method_name)
50
51
 
51
52
  define_method(method_name, &block)
52
53
  method = instance_method(method_name)
@@ -78,7 +79,10 @@ module Grape
78
79
 
79
80
  self.inheritable_setting = new_settings.point_in_time_copy
80
81
 
81
- route_setting(:saved_declared_params, namespace_stackable(:declared_params))
82
+ # now +namespace_stackable(:declared_params)+ contains all params defined for
83
+ # this endpoint and its parents, but later it will be cleaned up,
84
+ # see +reset_validations!+ in lib/grape/dsl/validations.rb
85
+ route_setting(:declared_params, namespace_stackable(:declared_params).flatten)
82
86
  route_setting(:saved_validations, namespace_stackable(:validations))
83
87
 
84
88
  namespace_stackable(:representations, []) unless namespace_stackable(:representations)
@@ -97,11 +101,11 @@ module Grape
97
101
  @block = nil
98
102
 
99
103
  @status = nil
100
- @file = nil
104
+ @stream = nil
101
105
  @body = nil
102
106
  @proc = nil
103
107
 
104
- return unless block_given?
108
+ return unless block
105
109
 
106
110
  @source = block
107
111
  @block = self.class.generate_api_method(method_name, &block)
@@ -113,12 +117,9 @@ module Grape
113
117
  inheritable_setting.route[:saved_validations] += namespace_stackable[:validations]
114
118
  parent_declared_params = namespace_stackable[:declared_params]
115
119
 
116
- if parent_declared_params
117
- inheritable_setting.route[:declared_params] ||= []
118
- inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten)
119
- end
120
+ inheritable_setting.route[:declared_params].concat(parent_declared_params.flatten) if parent_declared_params
120
121
 
121
- endpoints && endpoints.each { |e| e.inherit_settings(namespace_stackable) }
122
+ endpoints&.each { |e| e.inherit_settings(namespace_stackable) }
122
123
  end
123
124
 
124
125
  def require_option(options, key)
@@ -138,7 +139,7 @@ module Grape
138
139
  end
139
140
 
140
141
  def reset_routes!
141
- endpoints.each(&:reset_routes!) if endpoints
142
+ endpoints&.each(&:reset_routes!)
142
143
  @namespace = nil
143
144
  @routes = nil
144
145
  end
@@ -150,13 +151,9 @@ module Grape
150
151
  reset_routes!
151
152
  routes.each do |route|
152
153
  methods = [route.request_method]
153
- if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
154
- methods << Grape::Http::Headers::HEAD
155
- end
154
+ methods << Grape::Http::Headers::HEAD if !namespace_inheritable(:do_not_route_head) && route.request_method == Grape::Http::Headers::GET
156
155
  methods.each do |method|
157
- unless route.request_method.to_s.upcase == method
158
- route = Grape::Router::Route.new(method, route.origin, route.attributes.to_h)
159
- end
156
+ route = Grape::Router::Route.new(method, route.origin, **route.attributes.to_h) unless route.request_method == method
160
157
  router.append(route.apply(self))
161
158
  end
162
159
  end
@@ -167,8 +164,8 @@ module Grape
167
164
  route_options = prepare_default_route_attributes
168
165
  map_routes do |method, path|
169
166
  path = prepare_path(path)
170
- params = merge_route_options(route_options.merge(suffix: path.suffix))
171
- route = Router::Route.new(method, path.path, params)
167
+ params = merge_route_options(**route_options.merge(suffix: path.suffix))
168
+ route = Router::Route.new(method, path.path, **params)
172
169
  route.apply(self)
173
170
  end.flatten
174
171
  end
@@ -188,7 +185,7 @@ module Grape
188
185
  requirements: prepare_routes_requirements,
189
186
  prefix: namespace_inheritable(:root_prefix),
190
187
  anchor: options[:route_options].fetch(:anchor, true),
191
- settings: inheritable_setting.route.except(:saved_declared_params, :saved_validations),
188
+ settings: inheritable_setting.route.except(:declared_params, :saved_validations),
192
189
  forward_match: options[:forward_match]
193
190
  }
194
191
  end
@@ -196,11 +193,12 @@ module Grape
196
193
  def prepare_version
197
194
  version = namespace_inheritable(:version) || []
198
195
  return if version.empty?
196
+
199
197
  version.length == 1 ? version.first.to_s : version
200
198
  end
201
199
 
202
200
  def merge_route_options(**default)
203
- options[:route_options].clone.reverse_merge(**default)
201
+ options[:route_options].clone.merge!(**default)
204
202
  end
205
203
 
206
204
  def map_routes
@@ -230,7 +228,7 @@ module Grape
230
228
  # Return the collection of endpoints within this endpoint.
231
229
  # This is the case when an Grape::API mounts another Grape::API.
232
230
  def endpoints
233
- options[:app].endpoints if options[:app] && options[:app].respond_to?(:endpoints)
231
+ options[:app].endpoints if options[:app].respond_to?(:endpoints)
234
232
  end
235
233
 
236
234
  def equals?(e)
@@ -245,32 +243,37 @@ module Grape
245
243
  @request = Grape::Request.new(env, build_params_with: namespace_inheritable(:build_params_with))
246
244
  @params = @request.params
247
245
  @headers = @request.headers
246
+ begin
247
+ cookies.read(@request)
248
+ self.class.run_before_each(self)
249
+ run_filters befores, :before
250
+
251
+ if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
252
+ raise Grape::Exceptions::MethodNotAllowed.new(header.merge('Allow' => allowed_methods)) unless options?
253
+
254
+ header 'Allow', allowed_methods
255
+ response_object = ''
256
+ status 204
257
+ else
258
+ run_filters before_validations, :before_validation
259
+ run_validators validations, request
260
+ run_filters after_validations, :after_validation
261
+ response_object = execute
262
+ end
248
263
 
249
- cookies.read(@request)
250
- self.class.run_before_each(self)
251
- run_filters befores, :before
252
-
253
- if (allowed_methods = env[Grape::Env::GRAPE_ALLOWED_METHODS])
254
- raise Grape::Exceptions::MethodNotAllowed, header.merge('Allow' => allowed_methods) unless options?
255
- header 'Allow', allowed_methods
256
- response_object = ''
257
- status 204
258
- else
259
- run_filters before_validations, :before_validation
260
- run_validators validations, request
261
- run_filters after_validations, :after_validation
262
- response_object = @block ? @block.call(self) : nil
263
- end
264
+ run_filters afters, :after
265
+ cookies.write(header)
264
266
 
265
- run_filters afters, :after
266
- cookies.write(header)
267
+ # status verifies body presence when DELETE
268
+ @body ||= response_object
267
269
 
268
- # status verifies body presence when DELETE
269
- @body ||= response_object
270
+ # The body commonly is an Array of Strings, the application instance itself, or a Stream-like object
271
+ response_object = stream || [body]
270
272
 
271
- # The Body commonly is an Array of Strings, the application instance itself, or a File-like object
272
- response_object = file || [body]
273
- [status, header, response_object]
273
+ [status, header, response_object]
274
+ ensure
275
+ run_filters finallies, :finally
276
+ end
274
277
  end
275
278
  end
276
279
 
@@ -321,6 +324,10 @@ module Grape
321
324
 
322
325
  private :build_stack, :build_helpers
323
326
 
327
+ def execute
328
+ @block ? @block.call(self) : nil
329
+ end
330
+
324
331
  def helpers
325
332
  lazy_initialize! && @helpers
326
333
  end
@@ -341,23 +348,21 @@ module Grape
341
348
  def run_validators(validator_factories, request)
342
349
  validation_errors = []
343
350
 
344
- validators = validator_factories.map(&:create_validator)
351
+ validators = validator_factories.map { |options| Grape::Validations::ValidatorFactory.create_validator(**options) }
345
352
 
346
353
  ActiveSupport::Notifications.instrument('endpoint_run_validators.grape', endpoint: self, validators: validators, request: request) do
347
354
  validators.each do |validator|
348
- begin
349
- validator.validate(request)
350
- rescue Grape::Exceptions::Validation => e
351
- validation_errors << e
352
- break if validator.fail_fast?
353
- rescue Grape::Exceptions::ValidationArrayErrors => e
354
- validation_errors += e.errors
355
- break if validator.fail_fast?
356
- end
355
+ validator.validate(request)
356
+ rescue Grape::Exceptions::Validation => e
357
+ validation_errors << e
358
+ break if validator.fail_fast?
359
+ rescue Grape::Exceptions::ValidationArrayErrors => e
360
+ validation_errors.concat e.errors
361
+ break if validator.fail_fast?
357
362
  end
358
363
  end
359
364
 
360
- validation_errors.any? && raise(Grape::Exceptions::ValidationErrors, errors: validation_errors, headers: header)
365
+ validation_errors.any? && raise(Grape::Exceptions::ValidationErrors.new(errors: validation_errors, headers: header))
361
366
  end
362
367
 
363
368
  def run_filters(filters, type = :other)
@@ -384,6 +389,10 @@ module Grape
384
389
  namespace_stackable(:afters) || []
385
390
  end
386
391
 
392
+ def finallies
393
+ namespace_stackable(:finallies) || []
394
+ end
395
+
387
396
  def validations
388
397
  route_setting(:saved_validations) || []
389
398
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Grape
2
4
  module ErrorFormatter
3
5
  module Base