grape 0.12.0 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Appraisals +9 -4
- data/CHANGELOG.md +265 -215
- data/CONTRIBUTING.md +4 -4
- data/Gemfile +0 -1
- data/Gemfile.lock +166 -0
- data/README.md +426 -161
- data/RELEASING.md +14 -6
- data/Rakefile +30 -33
- data/UPGRADING.md +54 -23
- data/benchmark/simple.rb +27 -0
- data/gemfiles/rack_1.5.2.gemfile +13 -0
- data/gemfiles/rails_3.gemfile +2 -2
- data/gemfiles/rails_4.gemfile +1 -2
- data/grape.gemspec +6 -7
- data/lib/grape/api.rb +24 -4
- data/lib/grape/dsl/callbacks.rb +20 -0
- data/lib/grape/dsl/configuration.rb +59 -2
- data/lib/grape/dsl/helpers.rb +8 -3
- data/lib/grape/dsl/inside_route.rb +100 -45
- data/lib/grape/dsl/parameters.rb +96 -7
- data/lib/grape/dsl/request_response.rb +1 -1
- data/lib/grape/dsl/routing.rb +17 -4
- data/lib/grape/dsl/settings.rb +36 -1
- data/lib/grape/dsl/validations.rb +7 -5
- data/lib/grape/endpoint.rb +102 -57
- data/lib/grape/error_formatter/base.rb +6 -6
- data/lib/grape/exceptions/base.rb +5 -5
- data/lib/grape/exceptions/invalid_version_header.rb +10 -0
- data/lib/grape/exceptions/unknown_parameter.rb +10 -0
- data/lib/grape/exceptions/validation_errors.rb +4 -3
- data/lib/grape/formatter/serializable_hash.rb +3 -2
- data/lib/grape/http/headers.rb +0 -1
- data/lib/grape/locale/en.yml +5 -1
- data/lib/grape/middleware/auth/base.rb +2 -2
- data/lib/grape/middleware/auth/dsl.rb +1 -1
- data/lib/grape/middleware/auth/strategies.rb +1 -1
- data/lib/grape/middleware/base.rb +8 -4
- data/lib/grape/middleware/error.rb +3 -2
- data/lib/grape/middleware/filter.rb +1 -1
- data/lib/grape/middleware/formatter.rb +64 -45
- data/lib/grape/middleware/globals.rb +3 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +5 -7
- data/lib/grape/middleware/versioner/header.rb +113 -50
- data/lib/grape/middleware/versioner/param.rb +5 -8
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +20 -0
- data/lib/grape/middleware/versioner/path.rb +3 -6
- data/lib/grape/namespace.rb +13 -2
- data/lib/grape/path.rb +4 -3
- data/lib/grape/request.rb +40 -0
- data/lib/grape/route.rb +5 -0
- data/lib/grape/util/content_types.rb +9 -9
- data/lib/grape/util/env.rb +22 -0
- data/lib/grape/util/file_response.rb +21 -0
- data/lib/grape/util/inheritable_setting.rb +23 -2
- data/lib/grape/util/inheritable_values.rb +1 -1
- data/lib/grape/util/stackable_values.rb +5 -2
- data/lib/grape/util/strict_hash_configuration.rb +2 -1
- data/lib/grape/validations/attributes_iterator.rb +8 -3
- data/lib/grape/validations/params_scope.rb +164 -22
- data/lib/grape/validations/types/build_coercer.rb +53 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +183 -0
- data/lib/grape/validations/types/file.rb +28 -0
- data/lib/grape/validations/types/json.rb +65 -0
- data/lib/grape/validations/types/multiple_type_coercer.rb +76 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +59 -0
- data/lib/grape/validations/types/virtus_collection_patch.rb +16 -0
- data/lib/grape/validations/types.rb +144 -0
- data/lib/grape/validations/validators/all_or_none.rb +1 -1
- data/lib/grape/validations/validators/allow_blank.rb +3 -3
- data/lib/grape/validations/validators/base.rb +7 -0
- data/lib/grape/validations/validators/coerce.rb +32 -34
- data/lib/grape/validations/validators/presence.rb +2 -3
- data/lib/grape/validations/validators/regexp.rb +2 -4
- data/lib/grape/validations/validators/values.rb +3 -3
- data/lib/grape/validations.rb +5 -0
- data/lib/grape/version.rb +2 -1
- data/lib/grape.rb +15 -12
- data/pkg/grape-0.13.0.gem +0 -0
- data/spec/grape/api/custom_validations_spec.rb +5 -4
- data/spec/grape/api/deeply_included_options_spec.rb +7 -7
- data/spec/grape/api/nested_helpers_spec.rb +4 -2
- data/spec/grape/api/shared_helpers_spec.rb +8 -8
- data/spec/grape/api_spec.rb +151 -54
- data/spec/grape/dsl/configuration_spec.rb +13 -0
- data/spec/grape/dsl/helpers_spec.rb +16 -2
- data/spec/grape/dsl/inside_route_spec.rb +40 -4
- data/spec/grape/dsl/parameters_spec.rb +0 -6
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/dsl/validations_spec.rb +18 -0
- data/spec/grape/endpoint_spec.rb +130 -6
- data/spec/grape/entity_spec.rb +10 -8
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +1 -15
- data/spec/grape/exceptions/validation_errors_spec.rb +28 -0
- data/spec/grape/integration/rack_spec.rb +3 -2
- data/spec/grape/middleware/base_spec.rb +40 -16
- data/spec/grape/middleware/error_spec.rb +16 -15
- data/spec/grape/middleware/exception_spec.rb +45 -43
- data/spec/grape/middleware/formatter_spec.rb +34 -5
- data/spec/grape/middleware/versioner/header_spec.rb +79 -47
- data/spec/grape/path_spec.rb +10 -10
- data/spec/grape/presenters/presenter_spec.rb +2 -2
- data/spec/grape/request_spec.rb +100 -0
- data/spec/grape/util/inheritable_values_spec.rb +14 -0
- data/spec/grape/util/stackable_values_spec.rb +10 -0
- data/spec/grape/validations/params_scope_spec.rb +86 -0
- data/spec/grape/validations/types_spec.rb +95 -0
- data/spec/grape/validations/validators/coerce_spec.rb +364 -10
- data/spec/grape/validations/validators/values_spec.rb +27 -15
- data/spec/grape/validations_spec.rb +53 -24
- data/spec/shared/versioning_examples.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/support/versioned_helpers.rb +2 -2
- metadata +55 -14
- data/.gitignore +0 -46
- data/.rspec +0 -2
- data/.rubocop.yml +0 -7
- data/.rubocop_todo.yml +0 -84
- data/.travis.yml +0 -20
- data/.yardopts +0 -2
- data/lib/backports/active_support/deep_dup.rb +0 -49
- data/lib/backports/active_support/duplicable.rb +0 -88
- data/lib/grape/http/request.rb +0 -27
@@ -10,6 +10,9 @@ module Grape
|
|
10
10
|
|
11
11
|
include Grape::DSL::Settings
|
12
12
|
|
13
|
+
# Set or retrive the configured logger. If none was configured, this
|
14
|
+
# method will create a new one, logging to stdout.
|
15
|
+
# @param logger [Object] the new logger to use
|
13
16
|
def logger(logger = nil)
|
14
17
|
if logger
|
15
18
|
global_setting(:logger, logger)
|
@@ -19,6 +22,39 @@ module Grape
|
|
19
22
|
end
|
20
23
|
|
21
24
|
# Add a description to the next namespace or function.
|
25
|
+
# @param description [String] descriptive string for this endpoint
|
26
|
+
# or namespace
|
27
|
+
# @param options [Hash] other properties you can set to describe the
|
28
|
+
# endpoint or namespace. Optional.
|
29
|
+
# @option options :detail [String] additional detail about this endpoint
|
30
|
+
# @option options :params [Hash] param types and info. normally, you set
|
31
|
+
# these via the `params` dsl method.
|
32
|
+
# @option options :entity [Grape::Entity] the entity returned upon a
|
33
|
+
# successful call to this action
|
34
|
+
# @option options :http_codes [Array[Array]] possible HTTP codes this
|
35
|
+
# endpoint may return, with their meanings, in a 2d array
|
36
|
+
# @option options :named [String] a specific name to help find this route
|
37
|
+
# @option options :headers [Hash] HTTP headers this method can accept
|
38
|
+
# @yield a block yielding an instance context with methods mapping to
|
39
|
+
# each of the above, except that :entity is also aliased as #success
|
40
|
+
# and :http_codes is aliased as #failure.
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
#
|
44
|
+
# desc 'create a user'
|
45
|
+
# post '/users' do
|
46
|
+
# # ...
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# desc 'find a user' do
|
50
|
+
# detail 'locates the user from the given user ID'
|
51
|
+
# failure [ [404, 'Couldn\'t find the given user' ] ]
|
52
|
+
# success User::Entity
|
53
|
+
# end
|
54
|
+
# get '/user/:id' do
|
55
|
+
# # ...
|
56
|
+
# end
|
57
|
+
#
|
22
58
|
def desc(description, options = {}, &config_block)
|
23
59
|
if block_given?
|
24
60
|
config_class = Grape::DSL::Configuration.desc_container
|
@@ -28,6 +64,9 @@ module Grape
|
|
28
64
|
end
|
29
65
|
|
30
66
|
config_class.configure(&config_block)
|
67
|
+
unless options.empty?
|
68
|
+
warn '[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.'
|
69
|
+
end
|
31
70
|
options = config_class.settings
|
32
71
|
else
|
33
72
|
options = options.merge(description: description)
|
@@ -36,15 +75,33 @@ module Grape
|
|
36
75
|
namespace_setting :description, options
|
37
76
|
route_setting :description, options
|
38
77
|
end
|
78
|
+
|
79
|
+
def description_field(field, value = nil)
|
80
|
+
if value
|
81
|
+
description = route_setting(:description)
|
82
|
+
description ||= route_setting(:description, {})
|
83
|
+
description[field] = value
|
84
|
+
else
|
85
|
+
description = route_setting(:description)
|
86
|
+
description[field] if description
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def unset_description_field(field)
|
91
|
+
description = route_setting(:description)
|
92
|
+
description.delete(field) if description
|
93
|
+
end
|
39
94
|
end
|
40
95
|
|
41
96
|
module_function
|
42
97
|
|
98
|
+
# Merge multiple layers of settings into one hash.
|
43
99
|
def stacked_hash_to_hash(settings)
|
44
|
-
return
|
45
|
-
settings.each_with_object(
|
100
|
+
return if settings.blank?
|
101
|
+
settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
|
46
102
|
end
|
47
103
|
|
104
|
+
# Returns an object which configures itself via an instance-context DSL.
|
48
105
|
def desc_container
|
49
106
|
Module.new do
|
50
107
|
include Grape::Util::StrictHashConfiguration.module(
|
data/lib/grape/dsl/helpers.rb
CHANGED
@@ -29,6 +29,7 @@ module Grape
|
|
29
29
|
def helpers(new_mod = nil, &block)
|
30
30
|
if block_given? || new_mod
|
31
31
|
mod = new_mod || Module.new
|
32
|
+
define_boolean_in_mod(mod)
|
32
33
|
if new_mod
|
33
34
|
inject_api_helpers_to_mod(new_mod) if new_mod.is_a?(BaseHelper)
|
34
35
|
end
|
@@ -51,6 +52,11 @@ module Grape
|
|
51
52
|
|
52
53
|
protected
|
53
54
|
|
55
|
+
def define_boolean_in_mod(mod)
|
56
|
+
return if defined? mod::Boolean
|
57
|
+
mod.const_set('Boolean', Virtus::Attribute::Boolean)
|
58
|
+
end
|
59
|
+
|
54
60
|
def inject_api_helpers_to_mod(mod, &_block)
|
55
61
|
mod.extend(BaseHelper)
|
56
62
|
yield if block_given?
|
@@ -75,9 +81,8 @@ module Grape
|
|
75
81
|
protected
|
76
82
|
|
77
83
|
def process_named_params
|
78
|
-
|
79
|
-
|
80
|
-
end
|
84
|
+
return unless @named_params && @named_params.any?
|
85
|
+
api.namespace_stackable(:named_params, @named_params)
|
81
86
|
end
|
82
87
|
end
|
83
88
|
end
|
@@ -6,57 +6,74 @@ module Grape
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
include Grape::DSL::Settings
|
8
8
|
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
# namespaces.
|
13
|
-
#
|
14
|
-
# @param params [Hash] The initial hash to filter. Usually this will just be `params`
|
15
|
-
# @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces`
|
16
|
-
# options. `:include_parent_namespaces` defaults to true, hence must be set to false if
|
17
|
-
# you want only to return params declared against the current/target endpoint.
|
18
|
-
def declared(params, options = {}, declared_params = nil)
|
19
|
-
options[:include_missing] = true unless options.key?(:include_missing)
|
20
|
-
options[:include_parent_namespaces] = true unless options.key?(:include_parent_namespaces)
|
21
|
-
|
22
|
-
if declared_params.nil?
|
23
|
-
declared_params = (!options[:include_parent_namespaces] ? route_setting(:declared_params) :
|
24
|
-
(route_setting(:saved_declared_params) || [])).flatten(1) || []
|
25
|
-
end
|
9
|
+
# Denotes a situation where a DSL method has been invoked in a
|
10
|
+
# filter which it should not yet be available in
|
11
|
+
class MethodNotYetAvailable < StandardError; end
|
26
12
|
|
27
|
-
|
28
|
-
|
29
|
-
|
13
|
+
# @param type [Symbol] The type of filter for which evaluation has been
|
14
|
+
# completed
|
15
|
+
# @return [Module] A module containing method overrides suitable for the
|
16
|
+
# position in the filter evaluation sequence denoted by +type+. This
|
17
|
+
# defaults to an empty module if no overrides are defined for the given
|
18
|
+
# filter +type+.
|
19
|
+
def self.post_filter_methods(type)
|
20
|
+
@post_filter_modules ||= { before: PostBeforeFilter }
|
21
|
+
@post_filter_modules[type]
|
22
|
+
end
|
30
23
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
declared_params.inject(Hashie::Mash.new) do |hash, key|
|
37
|
-
key = { key => nil } unless key.is_a? Hash
|
24
|
+
# Methods which should not be available in filters until the before filter
|
25
|
+
# has completed
|
26
|
+
module PostBeforeFilter
|
27
|
+
def declared(params, options = {}, declared_params = nil)
|
28
|
+
options = options.reverse_merge(include_missing: true, include_parent_namespaces: true)
|
38
29
|
|
39
|
-
|
40
|
-
output_key = options[:stringify] ? parent.to_s : parent.to_sym
|
30
|
+
declared_params ||= (!options[:include_parent_namespaces] ? route_setting(:declared_params) : (route_setting(:saved_declared_params) || [])).flatten(1) || []
|
41
31
|
|
42
|
-
|
32
|
+
fail ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
|
43
33
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
else
|
48
|
-
params[parent]
|
49
|
-
end
|
34
|
+
if params.is_a? Array
|
35
|
+
params.map do |param|
|
36
|
+
declared(param || {}, options, declared_params)
|
50
37
|
end
|
38
|
+
else
|
39
|
+
declared_params.each_with_object(Hashie::Mash.new) do |key, hash|
|
40
|
+
key = { key => nil } unless key.is_a? Hash
|
41
|
+
|
42
|
+
key.each_pair do |parent, children|
|
43
|
+
output_key = options[:stringify] ? parent.to_s : parent.to_sym
|
44
|
+
|
45
|
+
next unless options[:include_missing] || params.key?(parent)
|
51
46
|
|
52
|
-
|
47
|
+
hash[output_key] = if children
|
48
|
+
children_params = params[parent] || (children.is_a?(Array) ? [] : {})
|
49
|
+
declared(children_params, options, Array(children))
|
50
|
+
else
|
51
|
+
params[parent]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
53
55
|
end
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
59
|
+
# A filtering method that will return a hash
|
60
|
+
# consisting only of keys that have been declared by a
|
61
|
+
# `params` statement against the current/target endpoint or parent
|
62
|
+
# namespaces.
|
63
|
+
#
|
64
|
+
# @see +PostBeforeFilter#declared+
|
65
|
+
#
|
66
|
+
# @param params [Hash] The initial hash to filter. Usually this will just be `params`
|
67
|
+
# @param options [Hash] Can pass `:include_missing`, `:stringify` and `:include_parent_namespaces`
|
68
|
+
# options. `:include_parent_namespaces` defaults to true, hence must be set to false if
|
69
|
+
# you want only to return params declared against the current/target endpoint.
|
70
|
+
def declared(*)
|
71
|
+
fail MethodNotYetAvailable, '#declared is not available prior to parameter validation.'
|
72
|
+
end
|
73
|
+
|
57
74
|
# The API version as specified in the URL.
|
58
75
|
def version
|
59
|
-
env[
|
76
|
+
env[Grape::Env::API_VERSION]
|
60
77
|
end
|
61
78
|
|
62
79
|
# End the request and display an error to the
|
@@ -74,19 +91,25 @@ module Grape
|
|
74
91
|
# @param url [String] The url to be redirect.
|
75
92
|
# @param options [Hash] The options used when redirect.
|
76
93
|
# :permanent, default false.
|
94
|
+
# :body, default a short message including the URL.
|
77
95
|
def redirect(url, options = {})
|
78
|
-
|
79
|
-
|
96
|
+
permanent = options.fetch(:permanent, false)
|
97
|
+
body_message = options.fetch(:body, nil)
|
98
|
+
if permanent
|
80
99
|
status 301
|
100
|
+
body_message ||= "This resource has been moved permanently to #{url}."
|
81
101
|
else
|
82
102
|
if env[Grape::Http::Headers::HTTP_VERSION] == 'HTTP/1.1' && request.request_method.to_s.upcase != Grape::Http::Headers::GET
|
83
103
|
status 303
|
104
|
+
body_message ||= "An alternate resource is located at #{url}."
|
84
105
|
else
|
85
106
|
status 302
|
107
|
+
body_message ||= "This resource has been moved temporarily to #{url}."
|
86
108
|
end
|
87
109
|
end
|
88
110
|
header 'Location', url
|
89
|
-
|
111
|
+
content_type 'text/plain'
|
112
|
+
body body_message
|
90
113
|
end
|
91
114
|
|
92
115
|
# Set or retrieve the HTTP status code.
|
@@ -177,12 +200,34 @@ module Grape
|
|
177
200
|
# GET /file # => "contents of file"
|
178
201
|
def file(value = nil)
|
179
202
|
if value
|
180
|
-
@file = value
|
203
|
+
@file = Grape::Util::FileResponse.new(value)
|
181
204
|
else
|
182
205
|
@file
|
183
206
|
end
|
184
207
|
end
|
185
208
|
|
209
|
+
# Allows you to define the response as a streamable object.
|
210
|
+
#
|
211
|
+
# If Content-Length and Transfer-Encoding are blank (among other conditions),
|
212
|
+
# Rack assumes this response can be streamed in chunks.
|
213
|
+
#
|
214
|
+
# @example
|
215
|
+
# get '/stream' do
|
216
|
+
# stream FileStreamer.new(...)
|
217
|
+
# end
|
218
|
+
#
|
219
|
+
# GET /stream # => "chunked contents of file"
|
220
|
+
#
|
221
|
+
# See:
|
222
|
+
# * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/chunked.rb
|
223
|
+
# * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/etag.rb
|
224
|
+
def stream(value = nil)
|
225
|
+
header 'Content-Length', nil
|
226
|
+
header 'Transfer-Encoding', nil
|
227
|
+
header 'Cache-Control', 'no-cache' # Skips ETag generation (reading the response up front)
|
228
|
+
file(value)
|
229
|
+
end
|
230
|
+
|
186
231
|
# Allows you to make use of Grape Entities by setting
|
187
232
|
# the response body to the serializable hash of the
|
188
233
|
# entity provided in the `:with` option. This has the
|
@@ -219,7 +264,7 @@ module Grape
|
|
219
264
|
if key
|
220
265
|
representation = (@body || {}).merge(key => representation)
|
221
266
|
elsif entity_class.present? && @body
|
222
|
-
fail ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(
|
267
|
+
fail ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
|
223
268
|
representation = @body.merge(representation)
|
224
269
|
end
|
225
270
|
|
@@ -235,9 +280,17 @@ module Grape
|
|
235
280
|
# route.route_description
|
236
281
|
# end
|
237
282
|
def route
|
238
|
-
env[
|
283
|
+
env[Grape::Env::RACK_ROUTING_ARGS][:route_info]
|
239
284
|
end
|
240
285
|
|
286
|
+
# Attempt to locate the Entity class for a given object, if not given
|
287
|
+
# explicitly. This is done by looking for the presence of Klass::Entity,
|
288
|
+
# where Klass is the class of the `object` parameter, or one of its
|
289
|
+
# ancestors.
|
290
|
+
# @param object [Object] the object to locate the Entity class for
|
291
|
+
# @param options [Hash]
|
292
|
+
# @option options :with [Class] the explicit entity class to use
|
293
|
+
# @return [Class] the located Entity class, or nil if none is found
|
241
294
|
def entity_class_for_obj(object, options)
|
242
295
|
entity_class = options.delete(:with)
|
243
296
|
|
@@ -259,9 +312,11 @@ module Grape
|
|
259
312
|
entity_class
|
260
313
|
end
|
261
314
|
|
315
|
+
# @return the representation of the given object as done through
|
316
|
+
# the given entity_class.
|
262
317
|
def entity_representation_for(entity_class, object, options)
|
263
318
|
embeds = { env: env }
|
264
|
-
embeds[:version] = env[
|
319
|
+
embeds[:version] = env[Grape::Env::API_VERSION] if env[Grape::Env::API_VERSION]
|
265
320
|
entity_class.represent(object, embeds.merge(options))
|
266
321
|
end
|
267
322
|
end
|
data/lib/grape/dsl/parameters.rb
CHANGED
@@ -2,6 +2,9 @@ require 'active_support/concern'
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module DSL
|
5
|
+
# Defines DSL methods, meant to be applied to a ParamsScope, which define
|
6
|
+
# and describe the parameters accepted by an endpoint, or all endpoints
|
7
|
+
# within a namespace.
|
5
8
|
module Parameters
|
6
9
|
extend ActiveSupport::Concern
|
7
10
|
|
@@ -28,7 +31,7 @@ module Grape
|
|
28
31
|
# end
|
29
32
|
def use(*names)
|
30
33
|
named_params = Grape::DSL::Configuration.stacked_hash_to_hash(@api.namespace_stackable(:named_params)) || {}
|
31
|
-
options = names.
|
34
|
+
options = names.extract_options!
|
32
35
|
names.each do |name|
|
33
36
|
params_block = named_params.fetch(name) do
|
34
37
|
fail "Params :#{name} not found!"
|
@@ -39,10 +42,62 @@ module Grape
|
|
39
42
|
alias_method :use_scope, :use
|
40
43
|
alias_method :includes, :use
|
41
44
|
|
45
|
+
# Require one or more parameters for the current endpoint.
|
46
|
+
#
|
47
|
+
# @param attrs list of parameter names, or, if :using is
|
48
|
+
# passed as an option, which keys to include (:all or :none) from
|
49
|
+
# the :using hash. The last key can be a hash, which specifies
|
50
|
+
# options for the parameters
|
51
|
+
# @option attrs :type [Class] the type to coerce this parameter to before
|
52
|
+
# passing it to the endpoint. See {Grape::Validations::Types} for a list of
|
53
|
+
# types that are supported automatically. Custom classes may be used
|
54
|
+
# where they define a class-level `::parse` method, or in conjunction
|
55
|
+
# with the `:coerce_with` parameter. `JSON` may be supplied to denote
|
56
|
+
# `JSON`-formatted objects or arrays of objects. `Array[JSON]` accepts
|
57
|
+
# the same values as `JSON` but will wrap single objects in an `Array`.
|
58
|
+
# @option attrs :types [Array<Class>] may be supplied in place of +:type+
|
59
|
+
# to declare an attribute that has multiple allowed types. See
|
60
|
+
# {Validations::Types::MultipleTypeCoercer} for more details on coercion
|
61
|
+
# and validation rules for variant-type parameters.
|
62
|
+
# @option attrs :desc [String] description to document this parameter
|
63
|
+
# @option attrs :default [Object] default value, if parameter is optional
|
64
|
+
# @option attrs :values [Array] permissable values for this field. If any
|
65
|
+
# other value is given, it will be handled as a validation error
|
66
|
+
# @option attrs :using [Hash[Symbol => Hash]] a hash defining keys and
|
67
|
+
# options, like that returned by {Grape::Entity#documentation}. The value
|
68
|
+
# of each key is an options hash accepting the same parameters
|
69
|
+
# @option attrs :except [Array[Symbol]] a list of keys to exclude from
|
70
|
+
# the :using Hash. The meaning of this depends on if :all or :none was
|
71
|
+
# passed; :all + :except will make the :except fields optional, whereas
|
72
|
+
# :none + :except will make the :except fields required
|
73
|
+
# @option attrs :coerce_with [#parse, #call] method to be used when coercing
|
74
|
+
# the parameter to the type named by `attrs[:type]`. Any class or object
|
75
|
+
# that defines `::parse` or `::call` may be used.
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
#
|
79
|
+
# params do
|
80
|
+
# # Basic usage: require a parameter of a certain type
|
81
|
+
# requires :user_id, type: Integer
|
82
|
+
#
|
83
|
+
# # You don't need to specify type; String is default
|
84
|
+
# requires :foo
|
85
|
+
#
|
86
|
+
# # Multiple params can be specified at once if they share
|
87
|
+
# # the same options.
|
88
|
+
# requires :x, :y, :z, type: Date
|
89
|
+
#
|
90
|
+
# # Nested parameters can be handled as hashes. You must
|
91
|
+
# # pass in a block, within which you can use any of the
|
92
|
+
# # parameters DSL methods.
|
93
|
+
# requires :user, type: Hash do
|
94
|
+
# requires :name, type: String
|
95
|
+
# end
|
96
|
+
# end
|
42
97
|
def requires(*attrs, &block)
|
43
98
|
orig_attrs = attrs.clone
|
44
99
|
|
45
|
-
opts = attrs.
|
100
|
+
opts = attrs.extract_options!.clone
|
46
101
|
opts[:presence] = true
|
47
102
|
|
48
103
|
if opts[:using]
|
@@ -50,15 +105,18 @@ module Grape
|
|
50
105
|
else
|
51
106
|
validate_attributes(attrs, opts, &block)
|
52
107
|
|
53
|
-
block_given? ? new_scope(orig_attrs, &block) :
|
54
|
-
push_declared_params(attrs)
|
108
|
+
block_given? ? new_scope(orig_attrs, &block) : push_declared_params(attrs)
|
55
109
|
end
|
56
110
|
end
|
57
111
|
|
112
|
+
# Allow, but don't require, one or more parameters for the current
|
113
|
+
# endpoint.
|
114
|
+
# @param (see #requires)
|
115
|
+
# @option (see #requires)
|
58
116
|
def optional(*attrs, &block)
|
59
117
|
orig_attrs = attrs.clone
|
60
118
|
|
61
|
-
opts = attrs.
|
119
|
+
opts = attrs.extract_options!.clone
|
62
120
|
type = opts[:type]
|
63
121
|
|
64
122
|
# check type for optional parameter group
|
@@ -72,29 +130,60 @@ module Grape
|
|
72
130
|
else
|
73
131
|
validate_attributes(attrs, opts, &block)
|
74
132
|
|
75
|
-
block_given? ? new_scope(orig_attrs, true, &block) :
|
76
|
-
push_declared_params(attrs)
|
133
|
+
block_given? ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs)
|
77
134
|
end
|
78
135
|
end
|
79
136
|
|
137
|
+
# Disallow the given parameters to be present in the same request.
|
138
|
+
# @param attrs [*Symbol] parameters to validate
|
80
139
|
def mutually_exclusive(*attrs)
|
81
140
|
validates(attrs, mutual_exclusion: true)
|
82
141
|
end
|
83
142
|
|
143
|
+
# Require exactly one of the given parameters to be present.
|
144
|
+
# @param (see #mutually_exclusive)
|
84
145
|
def exactly_one_of(*attrs)
|
85
146
|
validates(attrs, exactly_one_of: true)
|
86
147
|
end
|
87
148
|
|
149
|
+
# Require at least one of the given parameters to be present.
|
150
|
+
# @param (see #mutually_exclusive)
|
88
151
|
def at_least_one_of(*attrs)
|
89
152
|
validates(attrs, at_least_one_of: true)
|
90
153
|
end
|
91
154
|
|
155
|
+
# Require that either all given params are present, or none are.
|
156
|
+
# @param (see #mutually_exclusive)
|
92
157
|
def all_or_none_of(*attrs)
|
93
158
|
validates(attrs, all_or_none_of: true)
|
94
159
|
end
|
95
160
|
|
161
|
+
# Define a block of validations which should be applied if and only if
|
162
|
+
# the given parameter is present. The parameters are not nested.
|
163
|
+
# @param attr [Symbol] the parameter which, if present, triggers the
|
164
|
+
# validations
|
165
|
+
# @raise Grape::Exceptions::UnknownParameter if `attr` has not been
|
166
|
+
# defined in this scope yet
|
167
|
+
# @yield a parameter definition DSL
|
168
|
+
def given(attr, &block)
|
169
|
+
fail Grape::Exceptions::UnknownParameter.new(attr) unless declared_param?(attr)
|
170
|
+
new_lateral_scope(dependent_on: attr, &block)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Test for whether a certain parameter has been defined in this params
|
174
|
+
# block yet.
|
175
|
+
# @return [Boolean] whether the parameter has been defined
|
176
|
+
def declared_param?(param)
|
177
|
+
# @declared_params also includes hashes of options and such, but those
|
178
|
+
# won't be flattened out.
|
179
|
+
@declared_params.flatten.include?(param)
|
180
|
+
end
|
181
|
+
|
96
182
|
alias_method :group, :requires
|
97
183
|
|
184
|
+
# @param params [Hash] initial hash of parameters
|
185
|
+
# @return hash of parameters relevant for the current scope
|
186
|
+
# @api private
|
98
187
|
def params(params)
|
99
188
|
params = @parent.params(params) if @parent
|
100
189
|
if @element
|
data/lib/grape/dsl/routing.rb
CHANGED
@@ -28,9 +28,8 @@ module Grape
|
|
28
28
|
#
|
29
29
|
def version(*args, &block)
|
30
30
|
if args.any?
|
31
|
-
options = args.
|
32
|
-
options
|
33
|
-
options = { using: :path }.merge(options)
|
31
|
+
options = args.extract_options!
|
32
|
+
options = options.reverse_merge(using: :path)
|
34
33
|
|
35
34
|
fail Grape::Exceptions::MissingVendorOption.new if options[:using] == :header && !options.key?(:vendor)
|
36
35
|
|
@@ -76,7 +75,7 @@ module Grape
|
|
76
75
|
|
77
76
|
if app.respond_to?(:inheritable_setting, true)
|
78
77
|
mount_path = Rack::Mount::Utils.normalize_path(path)
|
79
|
-
app.top_level_setting.namespace_stackable[:mount_path] =
|
78
|
+
app.top_level_setting.namespace_stackable[:mount_path] = mount_path
|
80
79
|
|
81
80
|
app.inherit_settings(inheritable_setting)
|
82
81
|
|
@@ -135,6 +134,18 @@ module Grape
|
|
135
134
|
end
|
136
135
|
end
|
137
136
|
|
137
|
+
# Declare a "namespace", which prefixes all subordinate routes with its
|
138
|
+
# name. Any endpoints within a namespace, or group, resource, segment,
|
139
|
+
# etc., will share their parent context as well as any configuration
|
140
|
+
# done in the namespace context.
|
141
|
+
#
|
142
|
+
# @example
|
143
|
+
#
|
144
|
+
# namespace :foo do
|
145
|
+
# get 'bar' do
|
146
|
+
# # defines the endpoint: GET /foo/bar
|
147
|
+
# end
|
148
|
+
# end
|
138
149
|
def namespace(space = nil, options = {}, &block)
|
139
150
|
if space || block_given?
|
140
151
|
within_namespace do
|
@@ -162,6 +173,7 @@ module Grape
|
|
162
173
|
@routes ||= prepare_routes
|
163
174
|
end
|
164
175
|
|
176
|
+
# Remove all defined routes.
|
165
177
|
def reset_routes!
|
166
178
|
@routes = nil
|
167
179
|
end
|
@@ -177,6 +189,7 @@ module Grape
|
|
177
189
|
namespace(":#{param}", options, &block)
|
178
190
|
end
|
179
191
|
|
192
|
+
# @return array of defined versions
|
180
193
|
def versions
|
181
194
|
@versions ||= []
|
182
195
|
end
|