grape 1.7.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +37 -1
- data/CONTRIBUTING.md +1 -1
- data/README.md +30 -25
- data/UPGRADING.md +35 -0
- data/grape.gemspec +3 -6
- data/lib/grape/api.rb +2 -2
- data/lib/grape/content_types.rb +2 -8
- data/lib/grape/dsl/desc.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +11 -11
- data/lib/grape/dsl/request_response.rb +2 -1
- data/lib/grape/dsl/settings.rb +2 -6
- data/lib/grape/endpoint.rb +28 -18
- data/lib/grape/error_formatter/base.rb +1 -1
- data/lib/grape/exceptions/base.rb +2 -2
- data/lib/grape/exceptions/missing_group_type.rb +1 -6
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -6
- data/lib/grape/exceptions/validation_errors.rb +1 -6
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +3 -3
- data/lib/grape/extensions/hash.rb +4 -7
- data/lib/grape/extensions/hashie/mash.rb +3 -3
- data/lib/grape/formatter/serializable_hash.rb +7 -7
- data/lib/grape/http/headers.rb +12 -2
- data/lib/grape/middleware/auth/base.rb +1 -1
- data/lib/grape/middleware/auth/strategies.rb +1 -2
- data/lib/grape/middleware/error.rb +5 -5
- data/lib/grape/middleware/formatter.rb +6 -6
- data/lib/grape/middleware/versioner/header.rb +11 -19
- data/lib/grape/railtie.rb +9 -0
- data/lib/grape/request.rb +8 -2
- data/lib/grape/router/route.rb +1 -3
- data/lib/grape/util/lazy_value.rb +3 -11
- data/lib/grape/util/strict_hash_configuration.rb +3 -4
- data/lib/grape/validations/multiple_attributes_iterator.rb +1 -1
- data/lib/grape/validations/params_scope.rb +8 -2
- data/lib/grape/validations/single_attribute_iterator.rb +3 -1
- data/lib/grape/validations/types/custom_type_coercer.rb +2 -16
- data/lib/grape/validations/validators/base.rb +9 -20
- data/lib/grape/validations/validators/default_validator.rb +2 -20
- data/lib/grape/validations/validators/multiple_params_base.rb +4 -8
- data/lib/grape/validations/validators/values_validator.rb +14 -5
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +26 -5
- metadata +13 -253
- data/lib/grape/config.rb +0 -34
- data/lib/grape/extensions/deep_mergeable_hash.rb +0 -21
- data/lib/grape/extensions/deep_symbolize_hash.rb +0 -32
- data/spec/grape/api/custom_validations_spec.rb +0 -256
- data/spec/grape/api/deeply_included_options_spec.rb +0 -56
- data/spec/grape/api/defines_boolean_in_params_spec.rb +0 -38
- data/spec/grape/api/documentation_spec.rb +0 -59
- data/spec/grape/api/inherited_helpers_spec.rb +0 -114
- data/spec/grape/api/instance_spec.rb +0 -103
- data/spec/grape/api/invalid_format_spec.rb +0 -45
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +0 -38
- data/spec/grape/api/nested_helpers_spec.rb +0 -50
- data/spec/grape/api/optional_parameters_in_route_spec.rb +0 -43
- data/spec/grape/api/parameters_modification_spec.rb +0 -41
- data/spec/grape/api/patch_method_helpers_spec.rb +0 -79
- data/spec/grape/api/recognize_path_spec.rb +0 -21
- data/spec/grape/api/required_parameters_in_route_spec.rb +0 -37
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +0 -26
- data/spec/grape/api/routes_with_requirements_spec.rb +0 -59
- data/spec/grape/api/shared_helpers_exactly_one_of_spec.rb +0 -41
- data/spec/grape/api/shared_helpers_spec.rb +0 -36
- data/spec/grape/api_remount_spec.rb +0 -473
- data/spec/grape/api_spec.rb +0 -4347
- data/spec/grape/config_spec.rb +0 -17
- data/spec/grape/dsl/callbacks_spec.rb +0 -45
- data/spec/grape/dsl/desc_spec.rb +0 -101
- data/spec/grape/dsl/headers_spec.rb +0 -62
- data/spec/grape/dsl/helpers_spec.rb +0 -100
- data/spec/grape/dsl/inside_route_spec.rb +0 -535
- data/spec/grape/dsl/logger_spec.rb +0 -24
- data/spec/grape/dsl/middleware_spec.rb +0 -60
- data/spec/grape/dsl/parameters_spec.rb +0 -180
- data/spec/grape/dsl/request_response_spec.rb +0 -206
- data/spec/grape/dsl/routing_spec.rb +0 -275
- data/spec/grape/dsl/settings_spec.rb +0 -261
- data/spec/grape/dsl/validations_spec.rb +0 -55
- data/spec/grape/endpoint/declared_spec.rb +0 -846
- data/spec/grape/endpoint_spec.rb +0 -1085
- data/spec/grape/entity_spec.rb +0 -336
- data/spec/grape/exceptions/base_spec.rb +0 -81
- data/spec/grape/exceptions/body_parse_errors_spec.rb +0 -145
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +0 -358
- data/spec/grape/exceptions/invalid_formatter_spec.rb +0 -15
- data/spec/grape/exceptions/invalid_response_spec.rb +0 -11
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +0 -15
- data/spec/grape/exceptions/missing_group_type_spec.rb +0 -21
- data/spec/grape/exceptions/missing_mime_type_spec.rb +0 -17
- data/spec/grape/exceptions/missing_option_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_options_spec.rb +0 -15
- data/spec/grape/exceptions/unknown_validator_spec.rb +0 -15
- data/spec/grape/exceptions/unsupported_group_type_spec.rb +0 -23
- data/spec/grape/exceptions/validation_errors_spec.rb +0 -92
- data/spec/grape/exceptions/validation_spec.rb +0 -19
- data/spec/grape/extensions/param_builders/hash_spec.rb +0 -83
- data/spec/grape/extensions/param_builders/hash_with_indifferent_access_spec.rb +0 -105
- data/spec/grape/extensions/param_builders/hashie/mash_spec.rb +0 -79
- data/spec/grape/integration/global_namespace_function_spec.rb +0 -29
- data/spec/grape/integration/rack_sendfile_spec.rb +0 -48
- data/spec/grape/integration/rack_spec.rb +0 -51
- data/spec/grape/loading_spec.rb +0 -44
- data/spec/grape/middleware/auth/base_spec.rb +0 -31
- data/spec/grape/middleware/auth/dsl_spec.rb +0 -60
- data/spec/grape/middleware/auth/strategies_spec.rb +0 -120
- data/spec/grape/middleware/base_spec.rb +0 -221
- data/spec/grape/middleware/error_spec.rb +0 -85
- data/spec/grape/middleware/exception_spec.rb +0 -294
- data/spec/grape/middleware/formatter_spec.rb +0 -461
- data/spec/grape/middleware/globals_spec.rb +0 -30
- data/spec/grape/middleware/stack_spec.rb +0 -155
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +0 -122
- data/spec/grape/middleware/versioner/header_spec.rb +0 -345
- data/spec/grape/middleware/versioner/param_spec.rb +0 -171
- data/spec/grape/middleware/versioner/path_spec.rb +0 -62
- data/spec/grape/middleware/versioner_spec.rb +0 -21
- data/spec/grape/named_api_spec.rb +0 -19
- data/spec/grape/parser_spec.rb +0 -86
- data/spec/grape/path_spec.rb +0 -252
- data/spec/grape/presenters/presenter_spec.rb +0 -71
- data/spec/grape/request_spec.rb +0 -136
- data/spec/grape/util/inheritable_setting_spec.rb +0 -242
- data/spec/grape/util/inheritable_values_spec.rb +0 -79
- data/spec/grape/util/reverse_stackable_values_spec.rb +0 -134
- data/spec/grape/util/stackable_values_spec.rb +0 -128
- data/spec/grape/util/strict_hash_configuration_spec.rb +0 -38
- data/spec/grape/validations/attributes_doc_spec.rb +0 -153
- data/spec/grape/validations/instance_behaivour_spec.rb +0 -43
- data/spec/grape/validations/multiple_attributes_iterator_spec.rb +0 -40
- data/spec/grape/validations/params_scope_spec.rb +0 -1420
- data/spec/grape/validations/single_attribute_iterator_spec.rb +0 -57
- data/spec/grape/validations/types/array_coercer_spec.rb +0 -33
- data/spec/grape/validations/types/primitive_coercer_spec.rb +0 -150
- data/spec/grape/validations/types/set_coercer_spec.rb +0 -32
- data/spec/grape/validations/types_spec.rb +0 -111
- data/spec/grape/validations/validators/all_or_none_spec.rb +0 -162
- data/spec/grape/validations/validators/allow_blank_spec.rb +0 -575
- data/spec/grape/validations/validators/at_least_one_of_spec.rb +0 -205
- data/spec/grape/validations/validators/coerce_spec.rb +0 -1261
- data/spec/grape/validations/validators/default_spec.rb +0 -463
- data/spec/grape/validations/validators/exactly_one_of_spec.rb +0 -233
- data/spec/grape/validations/validators/except_values_spec.rb +0 -192
- data/spec/grape/validations/validators/mutual_exclusion_spec.rb +0 -214
- data/spec/grape/validations/validators/presence_spec.rb +0 -315
- data/spec/grape/validations/validators/regexp_spec.rb +0 -161
- data/spec/grape/validations/validators/same_as_spec.rb +0 -57
- data/spec/grape/validations/validators/values_spec.rb +0 -696
- data/spec/grape/validations/validators/zh-CN.yml +0 -10
- data/spec/grape/validations_spec.rb +0 -2029
- data/spec/integration/eager_load/eager_load_spec.rb +0 -15
- data/spec/integration/multi_json/json_spec.rb +0 -7
- data/spec/integration/multi_xml/xml_spec.rb +0 -7
- data/spec/shared/versioning_examples.rb +0 -215
- data/spec/spec_helper.rb +0 -52
- data/spec/support/basic_auth_encode_helpers.rb +0 -11
- data/spec/support/chunks.rb +0 -14
- data/spec/support/content_type_helpers.rb +0 -15
- data/spec/support/endpoint_faker.rb +0 -25
- data/spec/support/file_streamer.rb +0 -13
- data/spec/support/integer_helpers.rb +0 -13
- data/spec/support/versioned_helpers.rb +0 -55
@@ -15,24 +15,24 @@ module Grape
|
|
15
15
|
private
|
16
16
|
|
17
17
|
def serializable?(object)
|
18
|
-
object.respond_to?(:serializable_hash) ||
|
18
|
+
object.respond_to?(:serializable_hash) || array_serializable?(object) || object.is_a?(Hash)
|
19
19
|
end
|
20
20
|
|
21
21
|
def serialize(object)
|
22
22
|
if object.respond_to? :serializable_hash
|
23
23
|
object.serializable_hash
|
24
|
-
elsif
|
24
|
+
elsif array_serializable?(object)
|
25
25
|
object.map(&:serializable_hash)
|
26
26
|
elsif object.is_a?(Hash)
|
27
|
-
|
28
|
-
object.each_pair do |k, v|
|
29
|
-
h[k] = serialize(v)
|
30
|
-
end
|
31
|
-
h
|
27
|
+
object.transform_values { |v| serialize(v) }
|
32
28
|
else
|
33
29
|
object
|
34
30
|
end
|
35
31
|
end
|
32
|
+
|
33
|
+
def array_serializable?(object)
|
34
|
+
object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash }
|
35
|
+
end
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
data/lib/grape/http/headers.rb
CHANGED
@@ -10,7 +10,18 @@ module Grape
|
|
10
10
|
PATH_INFO = 'PATH_INFO'
|
11
11
|
REQUEST_METHOD = 'REQUEST_METHOD'
|
12
12
|
QUERY_STRING = 'QUERY_STRING'
|
13
|
-
|
13
|
+
|
14
|
+
if Grape.lowercase_headers?
|
15
|
+
ALLOW = 'allow'
|
16
|
+
LOCATION = 'location'
|
17
|
+
TRANSFER_ENCODING = 'transfer-encoding'
|
18
|
+
X_CASCADE = 'x-cascade'
|
19
|
+
else
|
20
|
+
ALLOW = 'Allow'
|
21
|
+
LOCATION = 'Location'
|
22
|
+
TRANSFER_ENCODING = 'Transfer-Encoding'
|
23
|
+
X_CASCADE = 'X-Cascade'
|
24
|
+
end
|
14
25
|
|
15
26
|
GET = 'GET'
|
16
27
|
POST = 'POST'
|
@@ -24,7 +35,6 @@ module Grape
|
|
24
35
|
SUPPORTED_METHODS_WITHOUT_OPTIONS = Grape::Util::LazyObject.new { [GET, POST, PUT, PATCH, DELETE, HEAD].freeze }
|
25
36
|
|
26
37
|
HTTP_ACCEPT_VERSION = 'HTTP_ACCEPT_VERSION'
|
27
|
-
X_CASCADE = 'X-Cascade'
|
28
38
|
HTTP_TRANSFER_ENCODING = 'HTTP_TRANSFER_ENCODING'
|
29
39
|
HTTP_ACCEPT = 'HTTP_ACCEPT'
|
30
40
|
|
@@ -28,7 +28,7 @@ module Grape
|
|
28
28
|
|
29
29
|
strategy_info = Grape::Middleware::Auth::Strategies[options[:type]]
|
30
30
|
|
31
|
-
throw(:error, status: 401, message: 'API Authorization Failed.')
|
31
|
+
throw(:error, status: 401, message: 'API Authorization Failed.') if strategy_info.blank?
|
32
32
|
|
33
33
|
strategy = strategy_info.create(@app, options) do |*args|
|
34
34
|
auth_proc_context.instance_exec(*args, &auth_proc)
|
@@ -12,8 +12,7 @@ module Grape
|
|
12
12
|
|
13
13
|
def auth_strategies
|
14
14
|
@auth_strategies ||= {
|
15
|
-
http_basic: StrategyInfo.new(Rack::Auth::Basic, ->(settings) { [settings[:realm]] })
|
16
|
-
http_digest: StrategyInfo.new(Rack::Auth::Digest::MD5, ->(settings) { [settings[:realm], settings[:opaque]] })
|
15
|
+
http_basic: StrategyInfo.new(Rack::Auth::Basic, ->(settings) { [settings[:realm]] })
|
17
16
|
}
|
18
17
|
end
|
19
18
|
|
@@ -51,7 +51,7 @@ module Grape
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def error!(message, status = options[:default_status], headers = {}, backtrace = [], original_exception = nil)
|
54
|
-
headers = headers.reverse_merge(
|
54
|
+
headers = headers.reverse_merge(Rack::CONTENT_TYPE => content_type)
|
55
55
|
rack_response(format_message(message, backtrace, original_exception), status, headers)
|
56
56
|
end
|
57
57
|
|
@@ -63,15 +63,15 @@ module Grape
|
|
63
63
|
def error_response(error = {})
|
64
64
|
status = error[:status] || options[:default_status]
|
65
65
|
message = error[:message] || options[:default_message]
|
66
|
-
headers = {
|
66
|
+
headers = { Rack::CONTENT_TYPE => content_type }
|
67
67
|
headers.merge!(error[:headers]) if error[:headers].is_a?(Hash)
|
68
68
|
backtrace = error[:backtrace] || error[:original_exception]&.backtrace || []
|
69
69
|
original_exception = error.is_a?(Exception) ? error : error[:original_exception] || nil
|
70
70
|
rack_response(format_message(message, backtrace, original_exception), status, headers)
|
71
71
|
end
|
72
72
|
|
73
|
-
def rack_response(message, status = options[:default_status], headers = {
|
74
|
-
message = ERB::Util.html_escape(message) if headers[
|
73
|
+
def rack_response(message, status = options[:default_status], headers = { Rack::CONTENT_TYPE => content_type })
|
74
|
+
message = ERB::Util.html_escape(message) if headers[Rack::CONTENT_TYPE] == TEXT_HTML
|
75
75
|
Rack::Response.new([message], Rack::Utils.status_code(status), headers)
|
76
76
|
end
|
77
77
|
|
@@ -109,7 +109,7 @@ module Grape
|
|
109
109
|
return :error_response if klass == Grape::Exceptions::InvalidVersionHeader
|
110
110
|
return unless options[:rescue_grape_exceptions] || !options[:rescue_all]
|
111
111
|
|
112
|
-
:error_response
|
112
|
+
options[:grape_exceptions_rescue_handler] || :error_response
|
113
113
|
end
|
114
114
|
|
115
115
|
def rescue_handler_for_any_class(klass)
|
@@ -54,7 +54,7 @@ module Grape
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def fetch_formatter(headers, options)
|
57
|
-
api_format = mime_types[headers[
|
57
|
+
api_format = mime_types[headers[Rack::CONTENT_TYPE]] || env[Grape::Env::API_FORMAT]
|
58
58
|
Grape::Formatter.formatter_for(api_format, **options)
|
59
59
|
end
|
60
60
|
|
@@ -63,10 +63,10 @@ module Grape
|
|
63
63
|
# @param headers [Hash]
|
64
64
|
# @return [Hash]
|
65
65
|
def ensure_content_type(headers)
|
66
|
-
if headers[
|
66
|
+
if headers[Rack::CONTENT_TYPE]
|
67
67
|
headers
|
68
68
|
else
|
69
|
-
headers.merge(
|
69
|
+
headers.merge(Rack::CONTENT_TYPE => content_type_for(env[Grape::Env::API_FORMAT]))
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
@@ -103,7 +103,7 @@ module Grape
|
|
103
103
|
begin
|
104
104
|
body = (env[Grape::Env::API_REQUEST_BODY] = parser.call(body, env))
|
105
105
|
if body.is_a?(Hash)
|
106
|
-
env[Grape::Env::RACK_REQUEST_FORM_HASH] = if env
|
106
|
+
env[Grape::Env::RACK_REQUEST_FORM_HASH] = if env.key?(Grape::Env::RACK_REQUEST_FORM_HASH)
|
107
107
|
env[Grape::Env::RACK_REQUEST_FORM_HASH].merge(body)
|
108
108
|
else
|
109
109
|
body
|
@@ -164,14 +164,14 @@ module Grape
|
|
164
164
|
\w+/[\w+.-]+) # eg application/vnd.example.myformat+xml
|
165
165
|
(?:
|
166
166
|
(?:;[^,]*?)? # optionally multiple formats in a row
|
167
|
-
;\s*q=([\
|
167
|
+
;\s*q=([\w.]+) # optional "quality" preference (eg q=0.5)
|
168
168
|
)?
|
169
169
|
}x
|
170
170
|
|
171
171
|
vendor_prefix_pattern = /vnd\.[^+]+\+/
|
172
172
|
|
173
173
|
accept.scan(accept_into_mime_and_quality)
|
174
|
-
.sort_by { |_, quality_preference| -quality_preference.to_f }
|
174
|
+
.sort_by { |_, quality_preference| -(quality_preference ? quality_preference.to_f : 1.0) }
|
175
175
|
.flat_map { |mime, _| [mime, mime.sub(vendor_prefix_pattern, '')] }
|
176
176
|
end
|
177
177
|
end
|
@@ -57,8 +57,7 @@ module Grape
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def strict_version_vendor_accept_header_presence_check
|
60
|
-
return
|
61
|
-
return if an_accept_header_with_version_and_vendor_is_present?
|
60
|
+
return if versions.blank? || an_accept_header_with_version_and_vendor_is_present?
|
62
61
|
|
63
62
|
fail_with_invalid_accept_header!('API vendor or version not found.')
|
64
63
|
end
|
@@ -101,25 +100,18 @@ module Grape
|
|
101
100
|
end
|
102
101
|
|
103
102
|
def available_media_types
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
]
|
103
|
+
[].tap do |available_media_types|
|
104
|
+
content_types.each_key do |extension|
|
105
|
+
versions.reverse_each do |version|
|
106
|
+
available_media_types << "application/vnd.#{vendor}-#{version}+#{extension}"
|
107
|
+
available_media_types << "application/vnd.#{vendor}-#{version}"
|
108
|
+
end
|
109
|
+
available_media_types << "application/vnd.#{vendor}+#{extension}"
|
112
110
|
end
|
113
|
-
available_media_types << "application/vnd.#{vendor}+#{extension}"
|
114
|
-
end
|
115
|
-
|
116
|
-
available_media_types << "application/vnd.#{vendor}"
|
117
111
|
|
118
|
-
|
119
|
-
available_media_types
|
112
|
+
available_media_types << "application/vnd.#{vendor}"
|
113
|
+
available_media_types.concat(content_types.values.flatten)
|
120
114
|
end
|
121
|
-
|
122
|
-
available_media_types.flatten
|
123
115
|
end
|
124
116
|
|
125
117
|
def headers_contain_wrong_vendor?
|
@@ -130,7 +122,7 @@ module Grape
|
|
130
122
|
|
131
123
|
def headers_contain_wrong_version?
|
132
124
|
header.values.all? do |header_value|
|
133
|
-
version?(header_value) &&
|
125
|
+
version?(header_value) && versions.exclude?(request_version(header_value))
|
134
126
|
end
|
135
127
|
end
|
136
128
|
|
data/lib/grape/request.rb
CHANGED
@@ -46,8 +46,14 @@ module Grape
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
|
50
|
-
|
49
|
+
if Grape.lowercase_headers?
|
50
|
+
def transform_header(header)
|
51
|
+
-header[5..].tr('_', '-').downcase
|
52
|
+
end
|
53
|
+
else
|
54
|
+
def transform_header(header)
|
55
|
+
-header[5..].split('_').map(&:capitalize).join('-')
|
56
|
+
end
|
51
57
|
end
|
52
58
|
end
|
53
59
|
end
|
data/lib/grape/router/route.rb
CHANGED
@@ -84,9 +84,7 @@ module Grape
|
|
84
84
|
path, line = *location.scan(SOURCE_LOCATION_REGEXP).first
|
85
85
|
path = File.realpath(path) if Pathname.new(path).relative?
|
86
86
|
expected ||= name
|
87
|
-
warn
|
88
|
-
#{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.
|
89
|
-
WARNING
|
87
|
+
Grape.deprecator.warn("#{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.")
|
90
88
|
end
|
91
89
|
end
|
92
90
|
end
|
@@ -70,29 +70,21 @@ module Grape
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def evaluate
|
73
|
-
|
74
|
-
@value_hash.each_with_index do |value, index|
|
75
|
-
evaluated[index] = value.evaluate
|
76
|
-
end
|
77
|
-
evaluated
|
73
|
+
@value_hash.map(&:evaluate)
|
78
74
|
end
|
79
75
|
end
|
80
76
|
|
81
77
|
class LazyValueHash < LazyValueEnumerable
|
82
78
|
def initialize(hash)
|
83
79
|
super
|
84
|
-
@value_hash =
|
80
|
+
@value_hash = ActiveSupport::HashWithIndifferentAccess.new
|
85
81
|
hash.each do |key, value|
|
86
82
|
self[key] = value
|
87
83
|
end
|
88
84
|
end
|
89
85
|
|
90
86
|
def evaluate
|
91
|
-
|
92
|
-
@value_hash.each do |key, value|
|
93
|
-
evaluated[key] = value.evaluate
|
94
|
-
end
|
95
|
-
evaluated
|
87
|
+
@value_hash.transform_values(&:evaluate)
|
96
88
|
end
|
97
89
|
end
|
98
90
|
end
|
@@ -66,11 +66,10 @@ module Grape
|
|
66
66
|
end
|
67
67
|
|
68
68
|
define_method :to_hash do
|
69
|
-
merge_hash = {}
|
70
|
-
setting_name.each_key { |k| merge_hash[k] = send("#{k}_context").to_hash }
|
71
|
-
|
72
69
|
@settings.to_hash.merge(
|
73
|
-
merge_hash
|
70
|
+
setting_name.each_key.with_object({}) do |k, merge_hash|
|
71
|
+
merge_hash[k] = send("#{k}_context").to_hash
|
72
|
+
end
|
74
73
|
)
|
75
74
|
end
|
76
75
|
end
|
@@ -10,6 +10,11 @@ module Grape
|
|
10
10
|
|
11
11
|
include Grape::DSL::Parameters
|
12
12
|
|
13
|
+
# There are a number of documentation options on entities that don't have
|
14
|
+
# corresponding validators. Since there is nowhere that enumerates them all,
|
15
|
+
# we maintain a list of them here and skip looking up validators for them.
|
16
|
+
RESERVED_DOCUMENTATION_KEYWORDS = %i[as required param_type is_array format example].freeze
|
17
|
+
|
13
18
|
class Attr
|
14
19
|
attr_accessor :key, :scope
|
15
20
|
|
@@ -359,7 +364,8 @@ module Grape
|
|
359
364
|
coerce_type validations, attrs, doc, opts
|
360
365
|
|
361
366
|
validations.each do |type, options|
|
362
|
-
|
367
|
+
# Don't try to look up validators for documentation params that don't have one.
|
368
|
+
next if RESERVED_DOCUMENTATION_KEYWORDS.include?(type)
|
363
369
|
|
364
370
|
validate(type, options, attrs, doc, opts)
|
365
371
|
end
|
@@ -414,7 +420,7 @@ module Grape
|
|
414
420
|
|
415
421
|
# but not special JSON types, which
|
416
422
|
# already imply coercion method
|
417
|
-
return
|
423
|
+
return if [JSON, Array[JSON]].exclude? validations[:coerce]
|
418
424
|
|
419
425
|
raise ArgumentError, 'coerce_with disallowed for type: JSON'
|
420
426
|
end
|
@@ -141,7 +141,7 @@ module Grape
|
|
141
141
|
lambda do |val|
|
142
142
|
method.call(val).tap do |new_val|
|
143
143
|
new_val.map do |item|
|
144
|
-
item.is_a?(Hash) ?
|
144
|
+
item.is_a?(Hash) ? item.deep_symbolize_keys : item
|
145
145
|
end
|
146
146
|
end
|
147
147
|
end
|
@@ -149,7 +149,7 @@ module Grape
|
|
149
149
|
# Hash objects are processed directly
|
150
150
|
elsif type == Hash
|
151
151
|
lambda do |val|
|
152
|
-
|
152
|
+
method.call(val).deep_symbolize_keys
|
153
153
|
end
|
154
154
|
|
155
155
|
# Simple types are not processed.
|
@@ -158,20 +158,6 @@ module Grape
|
|
158
158
|
method
|
159
159
|
end
|
160
160
|
end
|
161
|
-
|
162
|
-
def symbolize_keys!(hash)
|
163
|
-
hash.each_key do |key|
|
164
|
-
hash[key.to_sym] = hash.delete(key) if key.respond_to?(:to_sym)
|
165
|
-
end
|
166
|
-
hash
|
167
|
-
end
|
168
|
-
|
169
|
-
def symbolize_keys(hash)
|
170
|
-
hash.inject({}) do |new_hash, (key, value)|
|
171
|
-
new_key = key.respond_to?(:to_sym) ? key.to_sym : key
|
172
|
-
new_hash.merge!(new_key => value)
|
173
|
-
end
|
174
|
-
end
|
175
161
|
end
|
176
162
|
end
|
177
163
|
end
|
@@ -46,34 +46,23 @@ module Grape
|
|
46
46
|
# there may be more than one error per field
|
47
47
|
array_errors = []
|
48
48
|
|
49
|
-
attributes.each do |val, attr_name, empty_val
|
50
|
-
next if skip_value
|
49
|
+
attributes.each do |val, attr_name, empty_val|
|
51
50
|
next if !@scope.required? && empty_val
|
52
51
|
next unless @scope.meets_dependency?(val, params)
|
53
52
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
array_errors << e
|
58
|
-
end
|
53
|
+
validate_param!(attr_name, val) if @required || (val.respond_to?(:key?) && val.key?(attr_name))
|
54
|
+
rescue Grape::Exceptions::Validation => e
|
55
|
+
array_errors << e
|
59
56
|
end
|
60
57
|
|
61
58
|
raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
|
62
59
|
end
|
63
60
|
|
64
|
-
def self.convert_to_short_name(klass)
|
65
|
-
ret = klass.name.gsub(/::/, '/')
|
66
|
-
ret.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
67
|
-
ret.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
68
|
-
ret.tr!('-', '_')
|
69
|
-
ret.downcase!
|
70
|
-
File.basename(ret, '_validator')
|
71
|
-
end
|
72
|
-
|
73
61
|
def self.inherited(klass)
|
74
|
-
return
|
62
|
+
return if klass.name.blank?
|
75
63
|
|
76
|
-
|
64
|
+
short_validator_name = klass.name.demodulize.underscore.delete_suffix('_validator')
|
65
|
+
Validations.register_validator(short_validator_name, klass)
|
77
66
|
end
|
78
67
|
|
79
68
|
def message(default_key = nil)
|
@@ -95,8 +84,8 @@ module Grape
|
|
95
84
|
end
|
96
85
|
|
97
86
|
Grape::Validations::Base = Class.new(Grape::Validations::Validators::Base) do
|
98
|
-
def
|
87
|
+
def self.inherited(*)
|
88
|
+
Grape.deprecator.warn 'Grape::Validations::Base is deprecated! Use Grape::Validations::Validators::Base instead.'
|
99
89
|
super
|
100
|
-
warn '[DEPRECATION] `Grape::Validations::Base` is deprecated. Use `Grape::Validations::Validators::Base` instead.'
|
101
90
|
end
|
102
91
|
end
|
@@ -12,10 +12,10 @@ module Grape
|
|
12
12
|
def validate_param!(attr_name, params)
|
13
13
|
params[attr_name] = if @default.is_a? Proc
|
14
14
|
@default.call
|
15
|
-
elsif @default.frozen? ||
|
15
|
+
elsif @default.frozen? || !@default.duplicable?
|
16
16
|
@default
|
17
17
|
else
|
18
|
-
|
18
|
+
@default.dup
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -27,24 +27,6 @@ module Grape
|
|
27
27
|
validate_param!(attr_name, resource_params) if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
|
28
28
|
end
|
29
29
|
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
# return true if we might be able to dup this object
|
34
|
-
def duplicatable?(obj)
|
35
|
-
!obj.nil? &&
|
36
|
-
obj != true &&
|
37
|
-
obj != false &&
|
38
|
-
!obj.is_a?(Symbol) &&
|
39
|
-
!obj.is_a?(Numeric)
|
40
|
-
end
|
41
|
-
|
42
|
-
# make a best effort to dup the object
|
43
|
-
def duplicate(obj)
|
44
|
-
obj.dup
|
45
|
-
rescue TypeError
|
46
|
-
obj
|
47
|
-
end
|
48
30
|
end
|
49
31
|
end
|
50
32
|
end
|
@@ -8,14 +8,10 @@ module Grape
|
|
8
8
|
attributes = MultipleAttributesIterator.new(self, @scope, params)
|
9
9
|
array_errors = []
|
10
10
|
|
11
|
-
attributes.each do |resource_params
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
validate_params!(resource_params)
|
16
|
-
rescue Grape::Exceptions::Validation => e
|
17
|
-
array_errors << e
|
18
|
-
end
|
11
|
+
attributes.each do |resource_params|
|
12
|
+
validate_params!(resource_params)
|
13
|
+
rescue Grape::Exceptions::Validation => e
|
14
|
+
array_errors << e
|
19
15
|
end
|
20
16
|
|
21
17
|
raise Grape::Exceptions::ValidationArrayErrors.new(array_errors) if array_errors.any?
|
@@ -10,13 +10,11 @@ module Grape
|
|
10
10
|
@values = options[:value]
|
11
11
|
@proc = options[:proc]
|
12
12
|
|
13
|
-
warn
|
14
|
-
'Use the except validator instead.' if @excepts
|
13
|
+
Grape.deprecator.warn('The values validator except option is deprecated. Use the except validator instead.') if @excepts
|
15
14
|
|
16
15
|
raise ArgumentError, 'proc must be a Proc' if @proc && !@proc.is_a?(Proc)
|
17
16
|
|
18
|
-
warn
|
19
|
-
'The lambda expression can now be assigned directly to values.' if @proc
|
17
|
+
Grape.deprecator.warn('The values validator proc option is deprecated. The lambda expression can now be assigned directly to values.') if @proc
|
20
18
|
else
|
21
19
|
@excepts = nil
|
22
20
|
@values = nil
|
@@ -45,7 +43,7 @@ module Grape
|
|
45
43
|
unless check_values(param_array, attr_name)
|
46
44
|
|
47
45
|
raise validation_exception(attr_name, message(:values)) \
|
48
|
-
|
46
|
+
if @proc && !validate_proc(@proc, param_array)
|
49
47
|
end
|
50
48
|
|
51
49
|
private
|
@@ -70,6 +68,17 @@ module Grape
|
|
70
68
|
param_array.none? { |param| excepts.include?(param) }
|
71
69
|
end
|
72
70
|
|
71
|
+
def validate_proc(proc, param_array)
|
72
|
+
case proc.arity
|
73
|
+
when 0
|
74
|
+
param_array.all? { |_param| proc.call }
|
75
|
+
when 1
|
76
|
+
param_array.all? { |param| proc.call(param) }
|
77
|
+
else
|
78
|
+
raise ArgumentError, 'proc arity must be 0 or 1'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
73
82
|
def except_message
|
74
83
|
options = instance_variable_get(:@option)
|
75
84
|
options_key?(:except_message) ? options[:except_message] : message(:except_values)
|
data/lib/grape/version.rb
CHANGED
data/lib/grape.rb
CHANGED
@@ -5,33 +5,49 @@ require 'rack'
|
|
5
5
|
require 'rack/builder'
|
6
6
|
require 'rack/accept'
|
7
7
|
require 'rack/auth/basic'
|
8
|
-
require 'rack/auth/digest/md5'
|
9
8
|
require 'set'
|
10
9
|
require 'bigdecimal'
|
11
10
|
require 'date'
|
12
11
|
require 'active_support'
|
13
12
|
require 'active_support/concern'
|
13
|
+
require 'active_support/configurable'
|
14
14
|
require 'active_support/version'
|
15
15
|
require 'active_support/isolated_execution_state' if ActiveSupport::VERSION::MAJOR > 6
|
16
16
|
require 'active_support/core_ext/array/conversions'
|
17
17
|
require 'active_support/core_ext/array/extract_options'
|
18
18
|
require 'active_support/core_ext/array/wrap'
|
19
|
+
require 'active_support/core_ext/enumerable'
|
19
20
|
require 'active_support/core_ext/hash/conversions'
|
20
21
|
require 'active_support/core_ext/hash/deep_merge'
|
21
22
|
require 'active_support/core_ext/hash/except'
|
22
23
|
require 'active_support/core_ext/hash/indifferent_access'
|
24
|
+
require 'active_support/core_ext/hash/keys'
|
23
25
|
require 'active_support/core_ext/hash/reverse_merge'
|
24
26
|
require 'active_support/core_ext/hash/slice'
|
25
27
|
require 'active_support/core_ext/object/blank'
|
28
|
+
require 'active_support/core_ext/object/deep_dup'
|
29
|
+
require 'active_support/core_ext/object/duplicable'
|
30
|
+
require 'active_support/core_ext/string/exclude'
|
26
31
|
require 'active_support/dependencies/autoload'
|
32
|
+
require 'active_support/deprecation'
|
33
|
+
require 'active_support/inflector'
|
27
34
|
require 'active_support/notifications'
|
28
35
|
require 'i18n'
|
29
36
|
|
30
37
|
I18n.load_path << File.expand_path('grape/locale/en.yml', __dir__)
|
31
38
|
|
32
39
|
module Grape
|
40
|
+
include ActiveSupport::Configurable
|
33
41
|
extend ::ActiveSupport::Autoload
|
34
42
|
|
43
|
+
def self.deprecator
|
44
|
+
@deprecator ||= ActiveSupport::Deprecation.new('2.0', 'Grape')
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.lowercase_headers?
|
48
|
+
Rack::CONTENT_TYPE == 'content-type'
|
49
|
+
end
|
50
|
+
|
35
51
|
eager_autoload do
|
36
52
|
autoload :API
|
37
53
|
autoload :Endpoint
|
@@ -92,8 +108,6 @@ module Grape
|
|
92
108
|
module Extensions
|
93
109
|
extend ::ActiveSupport::Autoload
|
94
110
|
eager_autoload do
|
95
|
-
autoload :DeepMergeableHash
|
96
|
-
autoload :DeepSymbolizeHash
|
97
111
|
autoload :Hash
|
98
112
|
end
|
99
113
|
module ActiveSupport
|
@@ -285,13 +299,20 @@ module Grape
|
|
285
299
|
autoload :InvalidValue
|
286
300
|
end
|
287
301
|
end
|
302
|
+
|
303
|
+
configure do |config|
|
304
|
+
config.param_builder = Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
|
305
|
+
config.compile_methods!
|
306
|
+
end
|
288
307
|
end
|
289
308
|
|
290
|
-
require 'grape/config'
|
291
309
|
require 'grape/content_types'
|
292
310
|
|
293
311
|
require 'grape/util/lazy_value'
|
294
312
|
require 'grape/util/lazy_block'
|
295
313
|
require 'grape/util/endpoint_configuration'
|
296
|
-
|
297
314
|
require 'grape/version'
|
315
|
+
|
316
|
+
# https://api.rubyonrails.org/classes/ActiveSupport/Deprecation.html
|
317
|
+
# adding Grape.deprecator to Rails App if any
|
318
|
+
require 'grape/railtie' if defined?(Rails::Railtie) && ActiveSupport.gem_version >= Gem::Version.new('7.1')
|