grape 0.6.1 → 0.7.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 +9 -9
- data/.gitignore +7 -0
- data/.rubocop.yml +5 -0
- data/.travis.yml +3 -3
- data/CHANGELOG.md +42 -3
- data/CONTRIBUTING.md +118 -0
- data/Gemfile +4 -4
- data/README.md +312 -52
- data/Rakefile +6 -1
- data/UPGRADING.md +124 -0
- data/lib/grape.rb +2 -0
- data/lib/grape/api.rb +95 -44
- data/lib/grape/cookies.rb +0 -2
- data/lib/grape/endpoint.rb +63 -39
- data/lib/grape/error_formatter/base.rb +0 -3
- data/lib/grape/error_formatter/json.rb +0 -2
- data/lib/grape/error_formatter/txt.rb +0 -2
- data/lib/grape/error_formatter/xml.rb +0 -2
- data/lib/grape/exceptions/base.rb +0 -2
- data/lib/grape/exceptions/incompatible_option_values.rb +0 -3
- data/lib/grape/exceptions/invalid_formatter.rb +0 -3
- data/lib/grape/exceptions/invalid_versioner_option.rb +0 -4
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +0 -2
- data/lib/grape/exceptions/missing_mime_type.rb +0 -4
- data/lib/grape/exceptions/missing_option.rb +0 -3
- data/lib/grape/exceptions/missing_vendor_option.rb +0 -3
- data/lib/grape/exceptions/unknown_options.rb +0 -4
- data/lib/grape/exceptions/unknown_validator.rb +0 -2
- data/lib/grape/exceptions/validation_errors.rb +6 -5
- data/lib/grape/formatter/base.rb +0 -3
- data/lib/grape/formatter/json.rb +0 -2
- data/lib/grape/formatter/serializable_hash.rb +15 -16
- data/lib/grape/formatter/txt.rb +0 -2
- data/lib/grape/formatter/xml.rb +0 -2
- data/lib/grape/http/request.rb +2 -4
- data/lib/grape/locale/en.yml +1 -1
- data/lib/grape/middleware/auth/oauth2.rb +15 -6
- data/lib/grape/middleware/base.rb +7 -7
- data/lib/grape/middleware/error.rb +11 -6
- data/lib/grape/middleware/formatter.rb +80 -78
- data/lib/grape/middleware/globals.rb +13 -0
- data/lib/grape/middleware/versioner/accept_version_header.rb +0 -2
- data/lib/grape/middleware/versioner/header.rb +5 -3
- data/lib/grape/middleware/versioner/param.rb +2 -4
- data/lib/grape/middleware/versioner/path.rb +3 -4
- data/lib/grape/namespace.rb +0 -1
- data/lib/grape/parser/base.rb +0 -3
- data/lib/grape/parser/json.rb +0 -2
- data/lib/grape/parser/xml.rb +0 -2
- data/lib/grape/path.rb +1 -3
- data/lib/grape/route.rb +0 -3
- data/lib/grape/util/hash_stack.rb +1 -1
- data/lib/grape/validations.rb +72 -22
- data/lib/grape/validations/coerce.rb +5 -4
- data/lib/grape/validations/default.rb +5 -3
- data/lib/grape/validations/presence.rb +1 -1
- data/lib/grape/validations/regexp.rb +0 -2
- data/lib/grape/validations/values.rb +2 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +385 -96
- data/spec/grape/endpoint_spec.rb +162 -15
- data/spec/grape/entity_spec.rb +25 -0
- data/spec/grape/exceptions/validation_errors_spec.rb +19 -0
- data/spec/grape/middleware/auth/oauth2_spec.rb +60 -15
- data/spec/grape/middleware/base_spec.rb +3 -8
- data/spec/grape/middleware/error_spec.rb +2 -2
- data/spec/grape/middleware/exception_spec.rb +4 -4
- data/spec/grape/middleware/formatter_spec.rb +7 -4
- data/spec/grape/middleware/versioner/param_spec.rb +8 -7
- data/spec/grape/path_spec.rb +24 -14
- data/spec/grape/util/hash_stack_spec.rb +8 -8
- data/spec/grape/validations/coerce_spec.rb +75 -33
- data/spec/grape/validations/default_spec.rb +57 -0
- data/spec/grape/validations/presence_spec.rb +13 -11
- data/spec/grape/validations/values_spec.rb +76 -2
- data/spec/grape/validations_spec.rb +443 -20
- data/spec/spec_helper.rb +2 -2
- data/spec/support/content_type_helpers.rb +11 -0
- metadata +9 -38
@@ -3,18 +3,18 @@ require 'grape/middleware/base'
|
|
3
3
|
module Grape
|
4
4
|
module Middleware
|
5
5
|
class Error < Base
|
6
|
-
|
7
6
|
def default_options
|
8
7
|
{
|
9
|
-
default_status:
|
8
|
+
default_status: 500, # default status returned on error
|
10
9
|
default_message: "",
|
11
10
|
format: :txt,
|
12
11
|
formatters: {},
|
13
12
|
error_formatters: {},
|
14
13
|
rescue_all: false, # true to rescue all exceptions
|
14
|
+
rescue_subclasses: true, # rescue subclasses of exceptions listed
|
15
15
|
rescue_options: { backtrace: false }, # true to display backtrace
|
16
16
|
rescue_handlers: {}, # rescue handler blocks
|
17
|
-
|
17
|
+
base_only_rescue_handlers: {} # rescue handler blocks rescuing only the base class
|
18
18
|
}
|
19
19
|
end
|
20
20
|
|
@@ -31,15 +31,21 @@ module Grape
|
|
31
31
|
handler = lambda { |arg| error_response(arg) }
|
32
32
|
else
|
33
33
|
raise unless is_rescuable
|
34
|
-
handler =
|
34
|
+
handler = find_handler(e.class)
|
35
35
|
end
|
36
36
|
|
37
37
|
handler.nil? ? handle_error(e) : exec_handler(e, &handler)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
def find_handler(klass)
|
42
|
+
handler = options[:rescue_handlers].find(-> { [] }) { |error, _| klass <= error }[1]
|
43
|
+
handler = options[:base_only_rescue_handlers][klass] || options[:base_only_rescue_handlers][:all] unless handler
|
44
|
+
handler
|
45
|
+
end
|
46
|
+
|
41
47
|
def rescuable?(klass)
|
42
|
-
options[:rescue_all] || (options[:
|
48
|
+
options[:rescue_all] || (options[:rescue_handlers] || []).any? { |error, handler| klass <= error } || (options[:base_only_rescue_handlers] || []).include?(klass)
|
43
49
|
end
|
44
50
|
|
45
51
|
def exec_handler(e, &handler)
|
@@ -73,7 +79,6 @@ module Grape
|
|
73
79
|
throw :error, status: 406, message: "The requested format '#{format}' is not supported." unless formatter
|
74
80
|
formatter.call(message, backtrace, options, env)
|
75
81
|
end
|
76
|
-
|
77
82
|
end
|
78
83
|
end
|
79
84
|
end
|
@@ -3,7 +3,6 @@ require 'grape/middleware/base'
|
|
3
3
|
module Grape
|
4
4
|
module Middleware
|
5
5
|
class Formatter < Base
|
6
|
-
|
7
6
|
def default_options
|
8
7
|
{
|
9
8
|
default_format: :txt,
|
@@ -42,107 +41,110 @@ module Grape
|
|
42
41
|
|
43
42
|
private
|
44
43
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
(!request.form_data? || !request.media_type) &&
|
49
|
-
(!request.parseable_data?) &&
|
50
|
-
(request.content_length.to_i > 0 || request.env['HTTP_TRANSFER_ENCODING'] == 'chunked')
|
44
|
+
def request
|
45
|
+
@request ||= Rack::Request.new(env)
|
46
|
+
end
|
51
47
|
|
52
|
-
|
48
|
+
# store read input in env['api.request.input']
|
49
|
+
def read_body_input
|
50
|
+
if (request.post? || request.put? || request.patch? || request.delete?) &&
|
51
|
+
(!request.form_data? || !request.media_type) &&
|
52
|
+
(!request.parseable_data?) &&
|
53
|
+
(request.content_length.to_i > 0 || request.env['HTTP_TRANSFER_ENCODING'] == 'chunked')
|
54
|
+
|
55
|
+
if (input = env['rack.input'])
|
56
|
+
input.rewind
|
57
|
+
body = env['api.request.input'] = input.read
|
58
|
+
begin
|
59
|
+
read_rack_input(body) if body && body.length > 0
|
60
|
+
ensure
|
53
61
|
input.rewind
|
54
|
-
body = env['api.request.input'] = input.read
|
55
|
-
begin
|
56
|
-
read_rack_input(body) if body && body.length > 0
|
57
|
-
ensure
|
58
|
-
input.rewind
|
59
|
-
end
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
65
|
+
end
|
63
66
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
79
|
-
env['rack.request.form_input'] = env['rack.input']
|
67
|
+
# store parsed input in env['api.request.body']
|
68
|
+
def read_rack_input(body)
|
69
|
+
fmt = mime_types[request.media_type] if request.media_type
|
70
|
+
fmt ||= options[:default_format]
|
71
|
+
if content_type_for(fmt)
|
72
|
+
parser = Grape::Parser::Base.parser_for fmt, options
|
73
|
+
if parser
|
74
|
+
begin
|
75
|
+
body = (env['api.request.body'] = parser.call(body, env))
|
76
|
+
if body.is_a?(Hash)
|
77
|
+
if env['rack.request.form_hash']
|
78
|
+
env['rack.request.form_hash'] = env['rack.request.form_hash'].merge(body)
|
79
|
+
else
|
80
|
+
env['rack.request.form_hash'] = body
|
80
81
|
end
|
81
|
-
|
82
|
-
throw :error, status: 400, message: e.message
|
82
|
+
env['rack.request.form_input'] = env['rack.input']
|
83
83
|
end
|
84
|
-
|
85
|
-
|
84
|
+
rescue StandardError => e
|
85
|
+
throw :error, status: 400, message: e.message
|
86
86
|
end
|
87
87
|
else
|
88
|
-
|
88
|
+
env['api.request.body'] = body
|
89
89
|
end
|
90
|
+
else
|
91
|
+
throw :error, status: 406, message: "The requested content-type '#{request.media_type}' is not supported."
|
90
92
|
end
|
93
|
+
end
|
91
94
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
95
|
+
def negotiate_content_type
|
96
|
+
fmt = format_from_extension || format_from_params || options[:format] || format_from_header || options[:default_format]
|
97
|
+
if content_type_for(fmt)
|
98
|
+
env['api.format'] = fmt
|
99
|
+
else
|
100
|
+
throw :error, status: 406, message: "The requested format '#{fmt}' is not supported."
|
99
101
|
end
|
102
|
+
end
|
100
103
|
|
101
|
-
|
102
|
-
|
104
|
+
def format_from_extension
|
105
|
+
parts = request.path.split('.')
|
103
106
|
|
104
|
-
|
105
|
-
|
106
|
-
# avoid symbol memory leak on an unknown format
|
107
|
-
return extension.to_sym if content_type_for(extension)
|
108
|
-
end
|
109
|
-
nil
|
110
|
-
end
|
111
|
-
|
112
|
-
def format_from_params
|
113
|
-
fmt = Rack::Utils.parse_nested_query(env['QUERY_STRING'])["format"]
|
107
|
+
if parts.size > 1
|
108
|
+
extension = parts.last
|
114
109
|
# avoid symbol memory leak on an unknown format
|
115
|
-
return
|
116
|
-
fmt
|
110
|
+
return extension.to_sym if content_type_for(extension)
|
117
111
|
end
|
112
|
+
nil
|
113
|
+
end
|
118
114
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
115
|
+
def format_from_params
|
116
|
+
fmt = Rack::Utils.parse_nested_query(env['QUERY_STRING'])["format"]
|
117
|
+
# avoid symbol memory leak on an unknown format
|
118
|
+
return fmt.to_sym if content_type_for(fmt)
|
119
|
+
fmt
|
120
|
+
end
|
125
121
|
|
126
|
-
|
127
|
-
|
128
|
-
return []
|
122
|
+
def format_from_header
|
123
|
+
mime_array.each do |t|
|
124
|
+
return mime_types[t] if mime_types.key?(t)
|
125
|
+
end
|
126
|
+
nil
|
127
|
+
end
|
129
128
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
(?:
|
134
|
-
(?:;[^,]*?)? # optionally multiple formats in a row
|
135
|
-
;\s*q=([\d.]+) # optional "quality" preference (eg q=0.5)
|
136
|
-
)?
|
137
|
-
)x
|
129
|
+
def mime_array
|
130
|
+
accept = headers['accept']
|
131
|
+
return [] unless accept
|
138
132
|
|
139
|
-
|
133
|
+
accept_into_mime_and_quality = %r(
|
134
|
+
(
|
135
|
+
\w+/[\w+.-]+) # eg application/vnd.example.myformat+xml
|
136
|
+
(?:
|
137
|
+
(?:;[^,]*?)? # optionally multiple formats in a row
|
138
|
+
;\s*q=([\d.]+) # optional "quality" preference (eg q=0.5)
|
139
|
+
)?
|
140
|
+
)x
|
140
141
|
|
141
|
-
|
142
|
-
.sort_by { |_, quality_preference| -quality_preference.to_f }
|
143
|
-
.map { |mime, _| mime.sub(vendor_prefix_pattern, '') }
|
144
|
-
end
|
142
|
+
vendor_prefix_pattern = /vnd\.[^+]+\+/
|
145
143
|
|
144
|
+
accept.scan(accept_into_mime_and_quality)
|
145
|
+
.sort_by { |_, quality_preference| -quality_preference.to_f }
|
146
|
+
.map { |mime, _| mime.sub(vendor_prefix_pattern, '') }
|
147
|
+
end
|
146
148
|
end
|
147
149
|
end
|
148
150
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'grape/middleware/base'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Middleware
|
5
|
+
class Globals < Base
|
6
|
+
def before
|
7
|
+
@env['grape.request'] = Grape::Request.new(@env)
|
8
|
+
@env['grape.request.headers'] = request.headers
|
9
|
+
@env['grape.request.params'] = request.params if @env['rack.input']
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -17,7 +17,6 @@ module Grape
|
|
17
17
|
# X-Cascade header to alert Rack::Mount to attempt the next matched
|
18
18
|
# route.
|
19
19
|
class AcceptVersionHeader < Base
|
20
|
-
|
21
20
|
def before
|
22
21
|
potential_version = (env['HTTP_ACCEPT_VERSION'] || '').strip
|
23
22
|
|
@@ -62,7 +61,6 @@ module Grape
|
|
62
61
|
def error_headers
|
63
62
|
cascade? ? { 'X-Cascade' => 'pass' } : {}
|
64
63
|
end
|
65
|
-
|
66
64
|
end
|
67
65
|
end
|
68
66
|
end
|
@@ -22,9 +22,12 @@ module Grape
|
|
22
22
|
# X-Cascade header to alert Rack::Mount to attempt the next matched
|
23
23
|
# route.
|
24
24
|
class Header < Base
|
25
|
-
|
26
25
|
def before
|
27
|
-
|
26
|
+
begin
|
27
|
+
header = Rack::Accept::MediaType.new env['HTTP_ACCEPT']
|
28
|
+
rescue RuntimeError => e
|
29
|
+
throw :error, status: 406, headers: error_headers, message: e.message
|
30
|
+
end
|
28
31
|
|
29
32
|
if strict?
|
30
33
|
# If no Accept header:
|
@@ -123,7 +126,6 @@ module Grape
|
|
123
126
|
_, subtype = Rack::Accept::Header.parse_media_type media_type
|
124
127
|
subtype[/\Avnd\.[a-z0-9*.]+-[a-z0-9*\-.]+/]
|
125
128
|
end
|
126
|
-
|
127
129
|
end
|
128
130
|
end
|
129
131
|
end
|
@@ -27,17 +27,15 @@ module Grape
|
|
27
27
|
|
28
28
|
def before
|
29
29
|
paramkey = options[:parameter]
|
30
|
-
potential_version =
|
31
|
-
|
30
|
+
potential_version = Rack::Utils.parse_nested_query(env['QUERY_STRING'])[paramkey]
|
32
31
|
unless potential_version.nil?
|
33
32
|
if options[:versions] && !options[:versions].find { |v| v.to_s == potential_version }
|
34
33
|
throw :error, status: 404, message: "404 API Version Not Found", headers: { 'X-Cascade' => 'pass' }
|
35
34
|
end
|
36
35
|
env['api.version'] = potential_version
|
37
|
-
env['rack.request.query_hash'].delete(paramkey)
|
36
|
+
env['rack.request.query_hash'].delete(paramkey) if env.key? 'rack.request.query_hash'
|
38
37
|
end
|
39
38
|
end
|
40
|
-
|
41
39
|
end
|
42
40
|
end
|
43
41
|
end
|
@@ -43,10 +43,9 @@ module Grape
|
|
43
43
|
|
44
44
|
private
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
def prefix
|
47
|
+
Rack::Mount::Utils.normalize_path(options[:prefix].to_s) if options[:prefix]
|
48
|
+
end
|
50
49
|
end
|
51
50
|
end
|
52
51
|
end
|
data/lib/grape/namespace.rb
CHANGED
data/lib/grape/parser/base.rb
CHANGED
data/lib/grape/parser/json.rb
CHANGED
data/lib/grape/parser/xml.rb
CHANGED
data/lib/grape/path.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Grape
|
2
2
|
class Path
|
3
|
-
|
4
3
|
def self.prepare(raw_path, namespace, settings)
|
5
4
|
Path.new(raw_path, namespace, settings).path_with_suffix
|
6
5
|
end
|
@@ -22,7 +21,7 @@ module Grape
|
|
22
21
|
end
|
23
22
|
|
24
23
|
def uses_path_versioning?
|
25
|
-
settings[:version] && settings[:version_options][:using] == :path
|
24
|
+
!!(settings[:version] && settings[:version_options][:using] == :path)
|
26
25
|
end
|
27
26
|
|
28
27
|
def has_namespace?
|
@@ -67,6 +66,5 @@ module Grape
|
|
67
66
|
return if settings[key].nil?
|
68
67
|
settings[key].to_s.split("/")
|
69
68
|
end
|
70
|
-
|
71
69
|
end
|
72
70
|
end
|
data/lib/grape/route.rb
CHANGED
data/lib/grape/validations.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
module Grape
|
2
|
-
|
3
2
|
module Validations
|
4
|
-
|
5
3
|
##
|
6
4
|
# All validators must inherit from this class.
|
7
5
|
#
|
@@ -64,7 +62,6 @@ module Grape
|
|
64
62
|
@option = options
|
65
63
|
super
|
66
64
|
end
|
67
|
-
|
68
65
|
end
|
69
66
|
|
70
67
|
# We define Validator::inherited here so SingleOptionValidator
|
@@ -94,6 +91,7 @@ module Grape
|
|
94
91
|
@parent = opts[:parent]
|
95
92
|
@api = opts[:api]
|
96
93
|
@optional = opts[:optional] || false
|
94
|
+
@type = opts[:type]
|
97
95
|
@declared_params = []
|
98
96
|
|
99
97
|
instance_eval(&block)
|
@@ -102,41 +100,68 @@ module Grape
|
|
102
100
|
end
|
103
101
|
|
104
102
|
def should_validate?(parameters)
|
105
|
-
return false if @optional && params(parameters).blank?
|
103
|
+
return false if @optional && params(parameters).respond_to?(:all?) && params(parameters).all?(&:blank?)
|
106
104
|
return true if parent.nil?
|
107
105
|
parent.should_validate?(parameters)
|
108
106
|
end
|
109
107
|
|
110
108
|
def requires(*attrs, &block)
|
111
|
-
|
109
|
+
orig_attrs = attrs.clone
|
112
110
|
|
113
|
-
|
114
|
-
validations.merge!(attrs.pop) if attrs.last.is_a?(Hash)
|
111
|
+
opts = attrs.last.is_a?(Hash) ? attrs.pop : nil
|
115
112
|
|
116
|
-
|
117
|
-
|
113
|
+
if opts && opts[:using]
|
114
|
+
require_required_and_optional_fields(attrs.first, opts)
|
115
|
+
else
|
116
|
+
validate_attributes(attrs, opts, &block)
|
117
|
+
|
118
|
+
block_given? ? new_scope(orig_attrs, &block) :
|
119
|
+
push_declared_params(attrs)
|
120
|
+
end
|
118
121
|
end
|
119
122
|
|
120
123
|
def optional(*attrs, &block)
|
121
|
-
|
124
|
+
orig_attrs = attrs
|
122
125
|
|
123
126
|
validations = {}
|
124
127
|
validations.merge!(attrs.pop) if attrs.last.is_a?(Hash)
|
125
|
-
|
126
|
-
push_declared_params(attrs)
|
128
|
+
validations[:type] ||= Array if block_given?
|
127
129
|
validates(attrs, validations)
|
130
|
+
|
131
|
+
block_given? ? new_scope(orig_attrs, true, &block) :
|
132
|
+
push_declared_params(attrs)
|
128
133
|
end
|
129
134
|
|
130
|
-
def group(
|
131
|
-
requires(
|
135
|
+
def group(*attrs, &block)
|
136
|
+
requires(*attrs, &block)
|
132
137
|
end
|
133
138
|
|
134
139
|
def params(params)
|
135
140
|
params = @parent.params(params) if @parent
|
136
|
-
|
141
|
+
if @element
|
142
|
+
if params.is_a?(Array)
|
143
|
+
params = params.flat_map { |el| el[@element] || {} }
|
144
|
+
elsif params.is_a?(Hash)
|
145
|
+
params = params[@element] || {}
|
146
|
+
else
|
147
|
+
params = {}
|
148
|
+
end
|
149
|
+
end
|
137
150
|
params
|
138
151
|
end
|
139
152
|
|
153
|
+
def use(*names)
|
154
|
+
named_params = @api.settings[:named_params] || {}
|
155
|
+
names.each do |name|
|
156
|
+
params_block = named_params.fetch(name) do
|
157
|
+
raise "Params :#{name} not found!"
|
158
|
+
end
|
159
|
+
instance_eval(¶ms_block)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
alias_method :use_scope, :use
|
163
|
+
alias_method :includes, :use
|
164
|
+
|
140
165
|
def full_name(name)
|
141
166
|
return "#{@parent.full_name(@element)}[#{name}]" if @parent
|
142
167
|
name.to_s
|
@@ -150,9 +175,33 @@ module Grape
|
|
150
175
|
|
151
176
|
private
|
152
177
|
|
178
|
+
def require_required_and_optional_fields(context, opts)
|
179
|
+
if context == :all
|
180
|
+
optional_fields = Array(opts[:except])
|
181
|
+
required_fields = opts[:using].keys - optional_fields
|
182
|
+
else # context == :none
|
183
|
+
required_fields = Array(opts[:except])
|
184
|
+
optional_fields = opts[:using].keys - required_fields
|
185
|
+
end
|
186
|
+
required_fields.each do |field|
|
187
|
+
requires(field, opts[:using][field])
|
188
|
+
end
|
189
|
+
optional_fields.each do |field|
|
190
|
+
optional(field, opts[:using][field])
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def validate_attributes(attrs, opts, &block)
|
195
|
+
validations = { presence: true }
|
196
|
+
validations.merge!(opts) if opts
|
197
|
+
validations[:type] ||= Array if block
|
198
|
+
validates(attrs, validations)
|
199
|
+
end
|
200
|
+
|
153
201
|
def new_scope(attrs, optional = false, &block)
|
154
|
-
|
155
|
-
|
202
|
+
opts = attrs[1] || { type: Array }
|
203
|
+
raise ArgumentError unless opts.keys.to_set.subset? [:type].to_set
|
204
|
+
ParamsScope.new(api: @api, element: attrs.first, parent: self, optional: optional, type: opts[:type], &block)
|
156
205
|
end
|
157
206
|
|
158
207
|
# Pushes declared params to parent or settings
|
@@ -183,16 +232,20 @@ module Grape
|
|
183
232
|
values = validations[:values]
|
184
233
|
doc_attrs[:values] = values if values
|
185
234
|
|
235
|
+
values = (values.is_a?(Proc) ? values.call : values)
|
236
|
+
|
186
237
|
# default value should be present in values array, if both exist
|
187
238
|
if default && values && !values.include?(default)
|
188
239
|
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values)
|
189
240
|
end
|
190
241
|
|
191
242
|
# type should be compatible with values array, if both exist
|
192
|
-
if coerce_type && values && values.any? { |v| !v.
|
243
|
+
if coerce_type && values && values.any? { |v| !v.kind_of?(coerce_type) }
|
193
244
|
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
|
194
245
|
end
|
195
246
|
|
247
|
+
doc_attrs[:documentation] = validations.delete(:documentation) if validations.key?(:documentation)
|
248
|
+
|
196
249
|
full_attrs = attrs.collect { |name| { name: name, full_name: full_name(name) } }
|
197
250
|
@api.document_attribute(full_attrs, doc_attrs)
|
198
251
|
|
@@ -224,7 +277,6 @@ module Grape
|
|
224
277
|
raise Grape::Exceptions::UnknownValidator.new(type)
|
225
278
|
end
|
226
279
|
end
|
227
|
-
|
228
280
|
end
|
229
281
|
|
230
282
|
# This module is mixed into the API Class.
|
@@ -235,7 +287,7 @@ module Grape
|
|
235
287
|
end
|
236
288
|
|
237
289
|
def params(&block)
|
238
|
-
ParamsScope.new(api: self, &block)
|
290
|
+
ParamsScope.new(api: self, type: Hash, &block)
|
239
291
|
end
|
240
292
|
|
241
293
|
def document_attribute(names, opts)
|
@@ -246,9 +298,7 @@ module Grape
|
|
246
298
|
@last_description[:params][name[:full_name].to_s].merge!(opts)
|
247
299
|
end
|
248
300
|
end
|
249
|
-
|
250
301
|
end
|
251
|
-
|
252
302
|
end
|
253
303
|
end
|
254
304
|
|