grape 1.3.3 → 1.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +111 -2
- data/CONTRIBUTING.md +2 -1
- data/README.md +135 -23
- data/UPGRADING.md +237 -46
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +34 -42
- data/lib/grape/api.rb +21 -16
- data/lib/grape/cookies.rb +2 -0
- data/lib/grape/dsl/callbacks.rb +1 -1
- data/lib/grape/dsl/desc.rb +3 -5
- data/lib/grape/dsl/headers.rb +5 -2
- data/lib/grape/dsl/helpers.rb +8 -5
- data/lib/grape/dsl/inside_route.rb +72 -53
- data/lib/grape/dsl/middleware.rb +4 -4
- data/lib/grape/dsl/parameters.rb +11 -7
- data/lib/grape/dsl/request_response.rb +9 -6
- data/lib/grape/dsl/routing.rb +8 -9
- data/lib/grape/dsl/settings.rb +5 -5
- data/lib/grape/dsl/validations.rb +18 -1
- data/lib/grape/eager_load.rb +1 -1
- data/lib/grape/endpoint.rb +29 -42
- data/lib/grape/error_formatter/json.rb +2 -6
- data/lib/grape/error_formatter/xml.rb +2 -6
- data/lib/grape/exceptions/empty_message_body.rb +11 -0
- data/lib/grape/exceptions/validation.rb +2 -3
- data/lib/grape/exceptions/validation_errors.rb +1 -1
- data/lib/grape/formatter/json.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -1
- data/lib/grape/formatter/xml.rb +1 -0
- data/lib/grape/locale/en.yml +1 -1
- data/lib/grape/middleware/auth/base.rb +3 -3
- data/lib/grape/middleware/auth/dsl.rb +7 -1
- data/lib/grape/middleware/base.rb +6 -3
- data/lib/grape/middleware/error.rb +11 -13
- data/lib/grape/middleware/formatter.rb +7 -7
- data/lib/grape/middleware/stack.rb +10 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +3 -5
- data/lib/grape/middleware/versioner/header.rb +6 -4
- data/lib/grape/middleware/versioner/param.rb +1 -0
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +2 -1
- data/lib/grape/middleware/versioner/path.rb +2 -0
- data/lib/grape/parser/json.rb +1 -1
- data/lib/grape/parser/xml.rb +1 -1
- data/lib/grape/path.rb +1 -0
- data/lib/grape/request.rb +4 -1
- data/lib/grape/router/attribute_translator.rb +3 -3
- data/lib/grape/router/pattern.rb +1 -1
- data/lib/grape/router/route.rb +2 -2
- data/lib/grape/router.rb +31 -30
- data/lib/grape/{serve_file → serve_stream}/file_body.rb +1 -1
- data/lib/grape/{serve_file → serve_stream}/sendfile_response.rb +1 -1
- data/lib/grape/{serve_file/file_response.rb → serve_stream/stream_response.rb} +8 -8
- data/lib/grape/util/base_inheritable.rb +2 -2
- data/lib/grape/util/inheritable_setting.rb +1 -3
- data/lib/grape/util/lazy_value.rb +4 -2
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/attributes_iterator.rb +8 -0
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +97 -62
- data/lib/grape/validations/single_attribute_iterator.rb +1 -1
- data/lib/grape/validations/types/custom_type_coercer.rb +16 -3
- data/lib/grape/validations/types/dry_type_coercer.rb +1 -1
- data/lib/grape/validations/types/invalid_value.rb +24 -0
- data/lib/grape/validations/types/json.rb +2 -1
- data/lib/grape/validations/types/primitive_coercer.rb +4 -5
- data/lib/grape/validations/types.rb +1 -4
- data/lib/grape/validations/validator_factory.rb +1 -1
- data/lib/grape/validations/validators/all_or_none.rb +8 -5
- data/lib/grape/validations/validators/allow_blank.rb +9 -7
- data/lib/grape/validations/validators/as.rb +6 -8
- data/lib/grape/validations/validators/at_least_one_of.rb +7 -4
- data/lib/grape/validations/validators/base.rb +74 -69
- data/lib/grape/validations/validators/coerce.rb +63 -76
- data/lib/grape/validations/validators/default.rb +36 -34
- data/lib/grape/validations/validators/exactly_one_of.rb +9 -6
- data/lib/grape/validations/validators/except_values.rb +13 -11
- data/lib/grape/validations/validators/multiple_params_base.rb +24 -19
- data/lib/grape/validations/validators/mutual_exclusion.rb +8 -5
- data/lib/grape/validations/validators/presence.rb +7 -4
- data/lib/grape/validations/validators/regexp.rb +8 -5
- data/lib/grape/validations/validators/same_as.rb +18 -15
- data/lib/grape/validations/validators/values.rb +61 -56
- data/lib/grape/validations.rb +6 -0
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +7 -3
- data/spec/grape/api/custom_validations_spec.rb +77 -45
- data/spec/grape/api/deeply_included_options_spec.rb +3 -3
- data/spec/grape/api/defines_boolean_in_params_spec.rb +2 -1
- data/spec/grape/api/invalid_format_spec.rb +2 -0
- data/spec/grape/api/recognize_path_spec.rb +1 -1
- data/spec/grape/api/routes_with_requirements_spec.rb +8 -8
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +9 -15
- data/spec/grape/api_remount_spec.rb +25 -19
- data/spec/grape/api_spec.rb +576 -211
- data/spec/grape/dsl/callbacks_spec.rb +2 -1
- data/spec/grape/dsl/headers_spec.rb +39 -9
- data/spec/grape/dsl/helpers_spec.rb +3 -2
- data/spec/grape/dsl/inside_route_spec.rb +185 -34
- data/spec/grape/dsl/logger_spec.rb +16 -18
- data/spec/grape/dsl/middleware_spec.rb +2 -1
- data/spec/grape/dsl/parameters_spec.rb +2 -0
- data/spec/grape/dsl/request_response_spec.rb +1 -0
- data/spec/grape/dsl/routing_spec.rb +10 -7
- data/spec/grape/endpoint/declared_spec.rb +848 -0
- data/spec/grape/endpoint_spec.rb +77 -589
- data/spec/grape/entity_spec.rb +29 -23
- data/spec/grape/exceptions/body_parse_errors_spec.rb +3 -0
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +61 -22
- data/spec/grape/exceptions/validation_errors_spec.rb +13 -10
- data/spec/grape/exceptions/validation_spec.rb +5 -3
- data/spec/grape/extensions/param_builders/hash_spec.rb +7 -7
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +8 -8
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +8 -8
- data/spec/grape/integration/rack_sendfile_spec.rb +13 -9
- data/spec/grape/loading_spec.rb +8 -8
- data/spec/grape/middleware/auth/dsl_spec.rb +15 -6
- data/spec/grape/middleware/auth/strategies_spec.rb +61 -21
- data/spec/grape/middleware/base_spec.rb +24 -15
- data/spec/grape/middleware/error_spec.rb +3 -3
- data/spec/grape/middleware/exception_spec.rb +111 -161
- data/spec/grape/middleware/formatter_spec.rb +28 -7
- data/spec/grape/middleware/globals_spec.rb +7 -4
- data/spec/grape/middleware/stack_spec.rb +15 -12
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +2 -1
- data/spec/grape/middleware/versioner/header_spec.rb +14 -13
- data/spec/grape/middleware/versioner/param_spec.rb +7 -1
- data/spec/grape/middleware/versioner/path_spec.rb +5 -1
- data/spec/grape/middleware/versioner_spec.rb +1 -1
- data/spec/grape/parser_spec.rb +4 -0
- data/spec/grape/path_spec.rb +52 -52
- data/spec/grape/presenters/presenter_spec.rb +7 -6
- data/spec/grape/request_spec.rb +6 -4
- data/spec/grape/util/inheritable_setting_spec.rb +7 -7
- data/spec/grape/util/inheritable_values_spec.rb +3 -2
- data/spec/grape/util/reverse_stackable_values_spec.rb +3 -1
- data/spec/grape/util/stackable_values_spec.rb +7 -5
- data/spec/grape/validations/instance_behaivour_spec.rb +9 -10
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +14 -3
- data/spec/grape/validations/params_scope_spec.rb +72 -10
- data/spec/grape/validations/single_attribute_iterator_spec.rb +18 -6
- data/spec/grape/validations/types/primitive_coercer_spec.rb +63 -7
- data/spec/grape/validations/types_spec.rb +8 -8
- data/spec/grape/validations/validators/all_or_none_spec.rb +50 -56
- data/spec/grape/validations/validators/allow_blank_spec.rb +136 -140
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +50 -56
- data/spec/grape/validations/validators/coerce_spec.rb +248 -33
- data/spec/grape/validations/validators/default_spec.rb +121 -78
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +71 -77
- data/spec/grape/validations/validators/except_values_spec.rb +4 -3
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +71 -77
- data/spec/grape/validations/validators/presence_spec.rb +16 -1
- data/spec/grape/validations/validators/regexp_spec.rb +25 -31
- data/spec/grape/validations/validators/same_as_spec.rb +14 -20
- data/spec/grape/validations/validators/values_spec.rb +183 -178
- data/spec/grape/validations_spec.rb +342 -29
- data/spec/integration/eager_load/eager_load_spec.rb +15 -0
- data/spec/integration/multi_json/json_spec.rb +1 -1
- data/spec/integration/multi_xml/xml_spec.rb +1 -1
- data/spec/shared/versioning_examples.rb +32 -29
- data/spec/spec_helper.rb +12 -12
- data/spec/support/basic_auth_encode_helpers.rb +1 -1
- data/spec/support/chunks.rb +14 -0
- data/spec/support/versioned_helpers.rb +4 -6
- metadata +110 -102
data/lib/grape/dsl/desc.rb
CHANGED
@@ -50,7 +50,7 @@ module Grape
|
|
50
50
|
# end
|
51
51
|
#
|
52
52
|
def desc(description, options = {}, &config_block)
|
53
|
-
if
|
53
|
+
if config_block
|
54
54
|
endpoint_configuration = if defined?(configuration)
|
55
55
|
# When the instance is mounted - the configuration is executed on mount time
|
56
56
|
if configuration.respond_to?(:evaluate)
|
@@ -68,9 +68,7 @@ module Grape
|
|
68
68
|
end
|
69
69
|
|
70
70
|
config_class.configure(&config_block)
|
71
|
-
unless options.empty?
|
72
|
-
warn '[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.'
|
73
|
-
end
|
71
|
+
warn '[DEPRECATION] Passing a options hash and a block to `desc` is deprecated. Move all hash options to block.' unless options.empty?
|
74
72
|
options = config_class.settings
|
75
73
|
else
|
76
74
|
options = options.merge(description: description)
|
@@ -92,7 +90,7 @@ module Grape
|
|
92
90
|
|
93
91
|
def unset_description_field(field)
|
94
92
|
description = route_setting(:description)
|
95
|
-
description
|
93
|
+
description&.delete(field)
|
96
94
|
end
|
97
95
|
|
98
96
|
# Returns an object which configures itself via an instance-context DSL.
|
data/lib/grape/dsl/headers.rb
CHANGED
@@ -3,8 +3,11 @@
|
|
3
3
|
module Grape
|
4
4
|
module DSL
|
5
5
|
module Headers
|
6
|
-
#
|
7
|
-
#
|
6
|
+
# This method has four responsibilities:
|
7
|
+
# 1. Set a specifc header value by key
|
8
|
+
# 2. Retrieve a specifc header value by key
|
9
|
+
# 3. Retrieve all headers that have been set
|
10
|
+
# 4. Delete a specifc header key-value pair
|
8
11
|
def header(key = nil, val = nil)
|
9
12
|
if key
|
10
13
|
val ? header[key.to_s] = val : header.delete(key.to_s)
|
data/lib/grape/dsl/helpers.rb
CHANGED
@@ -36,8 +36,8 @@ module Grape
|
|
36
36
|
#
|
37
37
|
def helpers(*new_modules, &block)
|
38
38
|
include_new_modules(new_modules) if new_modules.any?
|
39
|
-
include_block(block) if
|
40
|
-
include_all_in_scope if !
|
39
|
+
include_block(block) if block
|
40
|
+
include_all_in_scope if !block && new_modules.empty?
|
41
41
|
end
|
42
42
|
|
43
43
|
protected
|
@@ -67,12 +67,13 @@ module Grape
|
|
67
67
|
|
68
68
|
def define_boolean_in_mod(mod)
|
69
69
|
return if defined? mod::Boolean
|
70
|
-
|
70
|
+
|
71
|
+
mod.const_set(:Boolean, Grape::API::Boolean)
|
71
72
|
end
|
72
73
|
|
73
|
-
def inject_api_helpers_to_mod(mod, &
|
74
|
+
def inject_api_helpers_to_mod(mod, &block)
|
74
75
|
mod.extend(BaseHelper) unless mod.is_a?(BaseHelper)
|
75
|
-
yield if
|
76
|
+
yield if block
|
76
77
|
mod.api_changed(self)
|
77
78
|
end
|
78
79
|
end
|
@@ -81,6 +82,7 @@ module Grape
|
|
81
82
|
# to provide some API-specific functionality.
|
82
83
|
module BaseHelper
|
83
84
|
attr_accessor :api
|
85
|
+
|
84
86
|
def params(name, &block)
|
85
87
|
@named_params ||= {}
|
86
88
|
@named_params[name] = block
|
@@ -95,6 +97,7 @@ module Grape
|
|
95
97
|
|
96
98
|
def process_named_params
|
97
99
|
return unless instance_variable_defined?(:@named_params) && @named_params && @named_params.any?
|
100
|
+
|
98
101
|
api.namespace_stackable(:named_params, @named_params)
|
99
102
|
end
|
100
103
|
end
|
@@ -48,6 +48,8 @@ module Grape
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def declared_hash(passed_params, options, declared_params, params_nested_path)
|
51
|
+
renamed_params = route_setting(:renamed_params) || {}
|
52
|
+
|
51
53
|
declared_params.each_with_object(passed_params.class.new) do |declared_param, memo|
|
52
54
|
if declared_param.is_a?(Hash)
|
53
55
|
declared_param.each_pair do |declared_parent_param, declared_children_params|
|
@@ -55,82 +57,72 @@ module Grape
|
|
55
57
|
params_nested_path_dup << declared_parent_param.to_s
|
56
58
|
next unless options[:include_missing] || passed_params.key?(declared_parent_param)
|
57
59
|
|
60
|
+
rename_path = params_nested_path + [declared_parent_param.to_s]
|
61
|
+
renamed_param_name = renamed_params[rename_path]
|
62
|
+
|
63
|
+
memo_key = optioned_param_key(renamed_param_name || declared_parent_param, options)
|
58
64
|
passed_children_params = passed_params[declared_parent_param] || passed_params.class.new
|
59
|
-
memo_key = optioned_param_key(declared_parent_param, options)
|
60
65
|
|
61
|
-
memo[memo_key] = handle_passed_param(
|
66
|
+
memo[memo_key] = handle_passed_param(params_nested_path_dup, passed_children_params.any?) do
|
62
67
|
declared(passed_children_params, options, declared_children_params, params_nested_path_dup)
|
63
68
|
end
|
64
69
|
end
|
65
70
|
else
|
66
71
|
# If it is not a Hash then it does not have children.
|
67
72
|
# Find its value or set it to nil.
|
68
|
-
|
69
|
-
|
73
|
+
next unless options[:include_missing] || passed_params.key?(declared_param)
|
74
|
+
|
75
|
+
rename_path = params_nested_path + [declared_param.to_s]
|
76
|
+
renamed_param_name = renamed_params[rename_path]
|
70
77
|
|
71
|
-
|
78
|
+
memo_key = optioned_param_key(renamed_param_name || declared_param, options)
|
79
|
+
passed_param = passed_params[declared_param]
|
72
80
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
81
|
+
params_nested_path_dup = params_nested_path.dup
|
82
|
+
params_nested_path_dup << declared_param.to_s
|
83
|
+
memo[memo_key] = passed_param || handle_passed_param(params_nested_path_dup) do
|
84
|
+
passed_param
|
77
85
|
end
|
78
86
|
end
|
79
87
|
end
|
80
88
|
end
|
81
89
|
|
82
|
-
def handle_passed_param(
|
83
|
-
if
|
90
|
+
def handle_passed_param(params_nested_path, has_passed_children = false, &_block)
|
91
|
+
return yield if has_passed_children
|
92
|
+
|
93
|
+
key = params_nested_path[0]
|
94
|
+
key += "[#{params_nested_path[1..-1].join('][')}]" if params_nested_path.size > 1
|
95
|
+
|
96
|
+
route_options_params = options[:route_options][:params] || {}
|
97
|
+
type = route_options_params.dig(key, :type)
|
98
|
+
has_children = route_options_params.keys.any? { |k| k != key && k.start_with?(key) }
|
99
|
+
|
100
|
+
if type == 'Hash' && !has_children
|
84
101
|
{}
|
85
|
-
elsif
|
102
|
+
elsif type == 'Array' || (type&.start_with?('[') && !type&.include?(','))
|
86
103
|
[]
|
104
|
+
elsif type == 'Set' || type&.start_with?('#<Set')
|
105
|
+
Set.new
|
87
106
|
else
|
88
107
|
yield
|
89
108
|
end
|
90
109
|
end
|
91
110
|
|
92
|
-
def should_be_empty_array?(passed_children_params, params_nested_path)
|
93
|
-
passed_children_params.empty? && declared_param_is_array?(params_nested_path)
|
94
|
-
end
|
95
|
-
|
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'
|
99
|
-
end
|
100
|
-
|
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'
|
108
|
-
end
|
109
|
-
|
110
|
-
def route_options_params
|
111
|
-
options[:route_options][:params] || {}
|
112
|
-
end
|
113
|
-
|
114
111
|
def optioned_param_key(declared_param, options)
|
115
112
|
options[:stringify] ? declared_param.to_s : declared_param.to_sym
|
116
113
|
end
|
117
114
|
|
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
115
|
def optioned_declared_params(**options)
|
125
116
|
declared_params = if options[:include_parent_namespaces]
|
126
117
|
# Declared params including parent namespaces
|
127
|
-
route_setting(:
|
118
|
+
route_setting(:declared_params)
|
128
119
|
else
|
129
120
|
# Declared params at current namespace
|
130
|
-
|
121
|
+
namespace_stackable(:declared_params).last || []
|
131
122
|
end
|
132
123
|
|
133
124
|
raise ArgumentError, 'Tried to filter for declared parameters but none exist.' unless declared_params
|
125
|
+
|
134
126
|
declared_params
|
135
127
|
end
|
136
128
|
end
|
@@ -201,11 +193,13 @@ module Grape
|
|
201
193
|
case status
|
202
194
|
when Symbol
|
203
195
|
raise ArgumentError, "Status code :#{status} is invalid." unless Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(status)
|
196
|
+
|
204
197
|
@status = Rack::Utils.status_code(status)
|
205
198
|
when Integer
|
206
199
|
@status = status
|
207
200
|
when nil
|
208
201
|
return @status if instance_variable_defined?(:@status) && @status
|
202
|
+
|
209
203
|
case request.request_method.to_s.upcase
|
210
204
|
when Grape::Http::Headers::POST
|
211
205
|
201
|
@@ -279,23 +273,36 @@ module Grape
|
|
279
273
|
body false
|
280
274
|
end
|
281
275
|
|
282
|
-
#
|
276
|
+
# Deprecated method to send files to the client. Use `sendfile` or `stream`
|
277
|
+
def file(value = nil)
|
278
|
+
if value.is_a?(String)
|
279
|
+
warn '[DEPRECATION] Use sendfile or stream to send files.'
|
280
|
+
sendfile(value)
|
281
|
+
elsif !value.is_a?(NilClass)
|
282
|
+
warn '[DEPRECATION] Use stream to use a Stream object.'
|
283
|
+
stream(value)
|
284
|
+
else
|
285
|
+
warn '[DEPRECATION] Use sendfile or stream to send files.'
|
286
|
+
sendfile
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# Allows you to send a file to the client via sendfile.
|
283
291
|
#
|
284
292
|
# @example
|
285
293
|
# get '/file' do
|
286
|
-
#
|
294
|
+
# sendfile FileStreamer.new(...)
|
287
295
|
# end
|
288
296
|
#
|
289
297
|
# GET /file # => "contents of file"
|
290
|
-
def
|
298
|
+
def sendfile(value = nil)
|
291
299
|
if value.is_a?(String)
|
292
|
-
file_body = Grape::
|
293
|
-
@
|
300
|
+
file_body = Grape::ServeStream::FileBody.new(value)
|
301
|
+
@stream = Grape::ServeStream::StreamResponse.new(file_body)
|
294
302
|
elsif !value.is_a?(NilClass)
|
295
|
-
|
296
|
-
@file = Grape::ServeFile::FileResponse.new(value)
|
303
|
+
raise ArgumentError, 'Argument must be a file path'
|
297
304
|
else
|
298
|
-
|
305
|
+
stream
|
299
306
|
end
|
300
307
|
end
|
301
308
|
|
@@ -315,10 +322,21 @@ module Grape
|
|
315
322
|
# * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/chunked.rb
|
316
323
|
# * https://github.com/rack/rack/blob/99293fa13d86cd48021630fcc4bd5acc9de5bdc3/lib/rack/etag.rb
|
317
324
|
def stream(value = nil)
|
325
|
+
return if value.nil? && @stream.nil?
|
326
|
+
|
318
327
|
header 'Content-Length', nil
|
319
328
|
header 'Transfer-Encoding', nil
|
320
329
|
header 'Cache-Control', 'no-cache' # Skips ETag generation (reading the response up front)
|
321
|
-
|
330
|
+
if value.is_a?(String)
|
331
|
+
file_body = Grape::ServeStream::FileBody.new(value)
|
332
|
+
@stream = Grape::ServeStream::StreamResponse.new(file_body)
|
333
|
+
elsif value.respond_to?(:each)
|
334
|
+
@stream = Grape::ServeStream::StreamResponse.new(value)
|
335
|
+
elsif !value.is_a?(NilClass)
|
336
|
+
raise ArgumentError, 'Stream object must respond to :each.'
|
337
|
+
else
|
338
|
+
@stream
|
339
|
+
end
|
322
340
|
end
|
323
341
|
|
324
342
|
# Allows you to make use of Grape Entities by setting
|
@@ -359,6 +377,7 @@ module Grape
|
|
359
377
|
representation = (body || {}).merge(key => representation)
|
360
378
|
elsif entity_class.present? && body
|
361
379
|
raise ArgumentError, "Representation of type #{representation.class} cannot be merged." unless representation.respond_to?(:merge)
|
380
|
+
|
362
381
|
representation = body.merge(representation)
|
363
382
|
end
|
364
383
|
|
@@ -389,7 +408,7 @@ module Grape
|
|
389
408
|
entity_class = options.delete(:with)
|
390
409
|
|
391
410
|
if entity_class.nil?
|
392
|
-
# entity class not
|
411
|
+
# entity class not explicitly defined, auto-detect from relation#klass or first object in the collection
|
393
412
|
object_class = if object.respond_to?(:klass)
|
394
413
|
object.klass
|
395
414
|
else
|
@@ -411,7 +430,7 @@ module Grape
|
|
411
430
|
def entity_representation_for(entity_class, object, options)
|
412
431
|
embeds = { env: env }
|
413
432
|
embeds[:version] = env[Grape::Env::API_VERSION] if env[Grape::Env::API_VERSION]
|
414
|
-
entity_class.represent(object, embeds.merge(options))
|
433
|
+
entity_class.represent(object, **embeds.merge(options))
|
415
434
|
end
|
416
435
|
end
|
417
436
|
end
|
data/lib/grape/dsl/middleware.rb
CHANGED
@@ -18,28 +18,28 @@ module Grape
|
|
18
18
|
# to inject.
|
19
19
|
def use(middleware_class, *args, &block)
|
20
20
|
arr = [:use, middleware_class, *args]
|
21
|
-
arr << block if
|
21
|
+
arr << block if block
|
22
22
|
|
23
23
|
namespace_stackable(:middleware, arr)
|
24
24
|
end
|
25
25
|
|
26
26
|
def insert(*args, &block)
|
27
27
|
arr = [:insert, *args]
|
28
|
-
arr << block if
|
28
|
+
arr << block if block
|
29
29
|
|
30
30
|
namespace_stackable(:middleware, arr)
|
31
31
|
end
|
32
32
|
|
33
33
|
def insert_before(*args, &block)
|
34
34
|
arr = [:insert_before, *args]
|
35
|
-
arr << block if
|
35
|
+
arr << block if block
|
36
36
|
|
37
37
|
namespace_stackable(:middleware, arr)
|
38
38
|
end
|
39
39
|
|
40
40
|
def insert_after(*args, &block)
|
41
41
|
arr = [:insert_after, *args]
|
42
|
-
arr << block if
|
42
|
+
arr << block if block
|
43
43
|
|
44
44
|
namespace_stackable(:middleware, arr)
|
45
45
|
end
|
data/lib/grape/dsl/parameters.rb
CHANGED
@@ -72,7 +72,7 @@ module Grape
|
|
72
72
|
|
73
73
|
# Require one or more parameters for the current endpoint.
|
74
74
|
#
|
75
|
-
# @param attrs list of
|
75
|
+
# @param attrs list of parameters names, or, if :using is
|
76
76
|
# passed as an option, which keys to include (:all or :none) from
|
77
77
|
# the :using hash. The last key can be a hash, which specifies
|
78
78
|
# options for the parameters
|
@@ -133,7 +133,7 @@ module Grape
|
|
133
133
|
require_required_and_optional_fields(attrs.first, opts)
|
134
134
|
else
|
135
135
|
validate_attributes(attrs, opts, &block)
|
136
|
-
|
136
|
+
block ? new_scope(orig_attrs, &block) : push_declared_params(attrs, **opts.slice(:as))
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
@@ -149,7 +149,7 @@ module Grape
|
|
149
149
|
opts = @group.merge(opts) if instance_variable_defined?(:@group) && @group
|
150
150
|
|
151
151
|
# check type for optional parameter group
|
152
|
-
if attrs &&
|
152
|
+
if attrs && block
|
153
153
|
raise Grape::Exceptions::MissingGroupTypeError.new if type.nil?
|
154
154
|
raise Grape::Exceptions::UnsupportedGroupTypeError.new unless Grape::Validations::Types.group?(type)
|
155
155
|
end
|
@@ -159,7 +159,7 @@ module Grape
|
|
159
159
|
else
|
160
160
|
validate_attributes(attrs, opts, &block)
|
161
161
|
|
162
|
-
|
162
|
+
block ? new_scope(orig_attrs, true, &block) : push_declared_params(attrs, **opts.slice(:as))
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
@@ -227,13 +227,17 @@ module Grape
|
|
227
227
|
|
228
228
|
alias group requires
|
229
229
|
|
230
|
-
|
230
|
+
class EmptyOptionalValue; end
|
231
|
+
|
232
|
+
def map_params(params, element, is_array = false)
|
231
233
|
if params.is_a?(Array)
|
232
234
|
params.map do |el|
|
233
|
-
map_params(el, element)
|
235
|
+
map_params(el, element, true)
|
234
236
|
end
|
235
237
|
elsif params.is_a?(Hash)
|
236
|
-
params[element] || {}
|
238
|
+
params[element] || (@optional && is_array ? EmptyOptionalValue : {})
|
239
|
+
elsif params == EmptyOptionalValue
|
240
|
+
EmptyOptionalValue
|
237
241
|
else
|
238
242
|
{}
|
239
243
|
end
|
@@ -26,6 +26,7 @@ module Grape
|
|
26
26
|
# define a single mime type
|
27
27
|
mime_type = content_types[new_format.to_sym]
|
28
28
|
raise Grape::Exceptions::MissingMimeType.new(new_format) unless mime_type
|
29
|
+
|
29
30
|
namespace_stackable(:content_types, new_format.to_sym => mime_type)
|
30
31
|
else
|
31
32
|
namespace_inheritable(:format)
|
@@ -102,14 +103,13 @@ module Grape
|
|
102
103
|
def rescue_from(*args, &block)
|
103
104
|
if args.last.is_a?(Proc)
|
104
105
|
handler = args.pop
|
105
|
-
elsif
|
106
|
+
elsif block
|
106
107
|
handler = block
|
107
108
|
end
|
108
109
|
|
109
110
|
options = args.extract_options!
|
110
|
-
if
|
111
|
-
|
112
|
-
end
|
111
|
+
raise ArgumentError, 'both :with option and block cannot be passed' if block && options.key?(:with)
|
112
|
+
|
113
113
|
handler ||= extract_with(options)
|
114
114
|
|
115
115
|
if args.include?(:all)
|
@@ -127,7 +127,7 @@ module Grape
|
|
127
127
|
:base_only_rescue_handlers
|
128
128
|
end
|
129
129
|
|
130
|
-
namespace_reverse_stackable handler_type,
|
130
|
+
namespace_reverse_stackable handler_type, args.map { |arg| [arg, handler] }.to_h
|
131
131
|
end
|
132
132
|
|
133
133
|
namespace_stackable(:rescue_options, options)
|
@@ -154,7 +154,8 @@ module Grape
|
|
154
154
|
# @param model_class [Class] The model class that will be represented.
|
155
155
|
# @option options [Class] :with The entity class that will represent the model.
|
156
156
|
def represent(model_class, options)
|
157
|
-
raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with]
|
157
|
+
raise Grape::Exceptions::InvalidWithOptionForRepresent.new unless options[:with].is_a?(Class)
|
158
|
+
|
158
159
|
namespace_stackable(:representations, model_class => options[:with])
|
159
160
|
end
|
160
161
|
|
@@ -162,9 +163,11 @@ module Grape
|
|
162
163
|
|
163
164
|
def extract_with(options)
|
164
165
|
return unless options.key?(:with)
|
166
|
+
|
165
167
|
with_option = options.delete(:with)
|
166
168
|
return with_option if with_option.instance_of?(Proc)
|
167
169
|
return with_option.to_sym if with_option.instance_of?(Symbol) || with_option.instance_of?(String)
|
170
|
+
|
168
171
|
raise ArgumentError, "with: #{with_option.class}, expected Symbol, String or Proc"
|
169
172
|
end
|
170
173
|
end
|
data/lib/grape/dsl/routing.rb
CHANGED
@@ -38,7 +38,7 @@ module Grape
|
|
38
38
|
|
39
39
|
@versions = versions | requested_versions
|
40
40
|
|
41
|
-
if
|
41
|
+
if block
|
42
42
|
within_namespace do
|
43
43
|
namespace_inheritable(:version, requested_versions)
|
44
44
|
namespace_inheritable(:version_options, options)
|
@@ -79,11 +79,12 @@ module Grape
|
|
79
79
|
namespace_inheritable(:do_not_route_options, true)
|
80
80
|
end
|
81
81
|
|
82
|
-
def mount(mounts, opts
|
82
|
+
def mount(mounts, *opts)
|
83
83
|
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
|
84
84
|
mounts.each_pair do |app, path|
|
85
85
|
if app.respond_to?(:mount_instance)
|
86
|
-
|
86
|
+
opts_with = opts.any? ? opts.shift[:with] : {}
|
87
|
+
mount({ app.mount_instance(configuration: opts_with) => path })
|
87
88
|
next
|
88
89
|
end
|
89
90
|
in_setting = inheritable_setting
|
@@ -151,7 +152,7 @@ module Grape
|
|
151
152
|
end
|
152
153
|
|
153
154
|
# Declare a "namespace", which prefixes all subordinate routes with its
|
154
|
-
# name. Any endpoints within a namespace,
|
155
|
+
# name. Any endpoints within a namespace, group, resource or segment,
|
155
156
|
# etc., will share their parent context as well as any configuration
|
156
157
|
# done in the namespace context.
|
157
158
|
#
|
@@ -165,14 +166,12 @@ module Grape
|
|
165
166
|
def namespace(space = nil, options = {}, &block)
|
166
167
|
@namespace_description = nil unless instance_variable_defined?(:@namespace_description) && @namespace_description
|
167
168
|
|
168
|
-
if space ||
|
169
|
+
if space || block
|
169
170
|
within_namespace do
|
170
171
|
previous_namespace_description = @namespace_description
|
171
172
|
@namespace_description = (@namespace_description || {}).deep_merge(namespace_setting(:description) || {})
|
172
173
|
nest(block) do
|
173
|
-
if space
|
174
|
-
namespace_stackable(:namespace, Namespace.new(space, **options))
|
175
|
-
end
|
174
|
+
namespace_stackable(:namespace, Namespace.new(space, **options)) if space
|
176
175
|
end
|
177
176
|
@namespace_description = previous_namespace_description
|
178
177
|
end
|
@@ -201,7 +200,7 @@ module Grape
|
|
201
200
|
@endpoints = []
|
202
201
|
end
|
203
202
|
|
204
|
-
#
|
203
|
+
# This method allows you to quickly define a parameter route segment
|
205
204
|
# in your API.
|
206
205
|
#
|
207
206
|
# @param param [Symbol] The name of the parameter you wish to declare.
|
data/lib/grape/dsl/settings.rb
CHANGED
@@ -103,12 +103,14 @@ module Grape
|
|
103
103
|
def namespace_stackable_with_hash(key)
|
104
104
|
settings = get_or_set :namespace_stackable, key, nil
|
105
105
|
return if settings.blank?
|
106
|
+
|
106
107
|
settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
|
107
108
|
end
|
108
109
|
|
109
110
|
def namespace_reverse_stackable_with_hash(key)
|
110
111
|
settings = get_or_set :namespace_reverse_stackable, key, nil
|
111
112
|
return if settings.blank?
|
113
|
+
|
112
114
|
result = {}
|
113
115
|
settings.each do |setting|
|
114
116
|
setting.each do |field, value|
|
@@ -154,10 +156,10 @@ module Grape
|
|
154
156
|
|
155
157
|
# Execute the block within a context where our inheritable settings are forked
|
156
158
|
# to a new copy (see #namespace_start).
|
157
|
-
def within_namespace(&
|
159
|
+
def within_namespace(&block)
|
158
160
|
namespace_start
|
159
161
|
|
160
|
-
result = yield if
|
162
|
+
result = yield if block
|
161
163
|
|
162
164
|
namespace_end
|
163
165
|
reset_validations!
|
@@ -175,9 +177,7 @@ module Grape
|
|
175
177
|
# +inheritable_setting+, however, it doesn't contain any user-defined settings.
|
176
178
|
# Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
|
177
179
|
# in the chain for every endpoint.
|
178
|
-
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
|
179
|
-
setting.inherit_from superclass.inheritable_setting
|
180
|
-
end
|
180
|
+
setting.inherit_from superclass.inheritable_setting if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
|
181
181
|
end
|
182
182
|
end
|
183
183
|
end
|
@@ -10,7 +10,24 @@ module Grape
|
|
10
10
|
include Grape::DSL::Configuration
|
11
11
|
|
12
12
|
module ClassMethods
|
13
|
-
# 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
|
14
31
|
def reset_validations!
|
15
32
|
unset_namespace_stackable :declared_params
|
16
33
|
unset_namespace_stackable :validations
|
data/lib/grape/eager_load.rb
CHANGED