grape 0.18.0 → 0.19.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.
Potentially problematic release.
This version of grape might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -69
- data/Gemfile +3 -3
- data/Gemfile.lock +37 -41
- data/README.md +49 -7
- data/UPGRADING.md +56 -1
- data/gemfiles/rack_edge.gemfile +34 -0
- data/gemfiles/rails_5.gemfile +1 -1
- data/gemfiles/rails_edge.gemfile +34 -0
- data/lib/grape/api.rb +1 -1
- data/lib/grape/dsl/helpers.rb +1 -0
- data/lib/grape/dsl/inside_route.rb +2 -0
- data/lib/grape/dsl/parameters.rb +24 -12
- data/lib/grape/dsl/request_response.rb +2 -3
- data/lib/grape/dsl/routing.rb +4 -0
- data/lib/grape/endpoint.rb +15 -16
- data/lib/grape/error_formatter/base.rb +1 -0
- data/lib/grape/formatter/serializable_hash.rb +2 -2
- data/lib/grape/http/headers.rb +2 -0
- data/lib/grape/middleware/base.rb +3 -4
- data/lib/grape/middleware/error.rb +1 -1
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/router.rb +37 -21
- data/lib/grape/router/attribute_translator.rb +1 -1
- data/lib/grape/router/pattern.rb +9 -1
- data/lib/grape/router/route.rb +4 -0
- data/lib/grape/validations/params_scope.rb +24 -6
- data/lib/grape/validations/validators/base.rb +1 -2
- data/lib/grape/validations/validators/regexp.rb +2 -1
- data/lib/grape/version.rb +1 -1
- data/pkg/grape-0.17.0.gem +0 -0
- data/spec/grape/api/patch_method_helpers_spec.rb +1 -2
- data/spec/grape/api_spec.rb +87 -21
- data/spec/grape/dsl/desc_spec.rb +2 -4
- data/spec/grape/dsl/inside_route_spec.rb +29 -22
- data/spec/grape/dsl/parameters_spec.rb +15 -1
- data/spec/grape/endpoint_spec.rb +53 -19
- data/spec/grape/middleware/formatter_spec.rb +39 -30
- data/spec/grape/middleware/versioner/param_spec.rb +15 -10
- data/spec/grape/middleware/versioner/path_spec.rb +4 -3
- data/spec/grape/util/inheritable_setting_spec.rb +2 -1
- data/spec/grape/util/strict_hash_configuration_spec.rb +1 -2
- data/spec/grape/validations/params_scope_spec.rb +182 -0
- data/spec/grape/validations/validators/default_spec.rb +1 -3
- data/spec/grape/validations/validators/presence_spec.rb +29 -1
- data/spec/grape/validations/validators/regexp_spec.rb +88 -0
- metadata +5 -3
- data/pkg/grape-0.18.0.gem +0 -0
@@ -15,6 +15,7 @@ module Grape
|
|
15
15
|
# env['api.endpoint'].route does not work when the error occurs within a middleware
|
16
16
|
# the Endpoint does not have a valid env at this moment
|
17
17
|
http_codes = env[Grape::Env::GRAPE_ROUTING_ARGS][:route_info].http_codes || []
|
18
|
+
|
18
19
|
found_code = http_codes.find do |http_code|
|
19
20
|
(http_code[0].to_i == env[Grape::Env::API_ENDPOINT].status) && http_code[2].respond_to?(:represent)
|
20
21
|
end if env[Grape::Env::API_ENDPOINT].request
|
@@ -12,13 +12,13 @@ module Grape
|
|
12
12
|
private
|
13
13
|
|
14
14
|
def serializable?(object)
|
15
|
-
object.respond_to?(:serializable_hash) || object.is_a?(Array) &&
|
15
|
+
object.respond_to?(:serializable_hash) || object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash } || object.is_a?(Hash)
|
16
16
|
end
|
17
17
|
|
18
18
|
def serialize(object)
|
19
19
|
if object.respond_to? :serializable_hash
|
20
20
|
object.serializable_hash
|
21
|
-
elsif object.is_a?(Array) &&
|
21
|
+
elsif object.is_a?(Array) && object.all? { |o| o.respond_to? :serializable_hash }
|
22
22
|
object.map(&:serializable_hash)
|
23
23
|
elsif object.is_a?(Hash)
|
24
24
|
h = {}
|
data/lib/grape/http/headers.rb
CHANGED
@@ -16,6 +16,8 @@ module Grape
|
|
16
16
|
HEAD = 'HEAD'.freeze
|
17
17
|
OPTIONS = 'OPTIONS'.freeze
|
18
18
|
|
19
|
+
SUPPORTED_METHODS = [GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS].freeze
|
20
|
+
|
19
21
|
HTTP_ACCEPT_VERSION = 'HTTP_ACCEPT_VERSION'.freeze
|
20
22
|
X_CASCADE = 'X-Cascade'.freeze
|
21
23
|
HTTP_TRANSFER_ENCODING = 'HTTP_TRANSFER_ENCODING'.freeze
|
@@ -13,6 +13,7 @@ module Grape
|
|
13
13
|
def initialize(app, **options)
|
14
14
|
@app = app
|
15
15
|
@options = default_options.merge(**options)
|
16
|
+
@app_response = nil
|
16
17
|
end
|
17
18
|
|
18
19
|
def default_options
|
@@ -44,14 +45,12 @@ module Grape
|
|
44
45
|
|
45
46
|
# @abstract
|
46
47
|
# Called before the application is called in the middleware lifecycle.
|
47
|
-
def before
|
48
|
-
end
|
48
|
+
def before; end
|
49
49
|
|
50
50
|
# @abstract
|
51
51
|
# Called after the application is called in the middleware lifecycle.
|
52
52
|
# @return [Response, nil] a Rack SPEC response or nil to call the application afterwards.
|
53
|
-
def after
|
54
|
-
end
|
53
|
+
def after; end
|
55
54
|
|
56
55
|
def response
|
57
56
|
return @app_response if @app_response.is_a?(Rack::Response)
|
data/lib/grape/router.rb
CHANGED
@@ -19,6 +19,10 @@ module Grape
|
|
19
19
|
path
|
20
20
|
end
|
21
21
|
|
22
|
+
def self.supported_methods
|
23
|
+
@supported_methods ||= Grape::Http::Headers::SUPPORTED_METHODS + ['*']
|
24
|
+
end
|
25
|
+
|
22
26
|
def initialize
|
23
27
|
@neutral_map = []
|
24
28
|
@map = Hash.new { |hash, key| hash[key] = [] }
|
@@ -28,7 +32,8 @@ module Grape
|
|
28
32
|
def compile!
|
29
33
|
return if compiled
|
30
34
|
@union = Regexp.union(@neutral_map.map(&:regexp))
|
31
|
-
|
35
|
+
self.class.supported_methods.each do |method|
|
36
|
+
routes = map[method]
|
32
37
|
@optimized_map[method] = routes.map.with_index do |route, index|
|
33
38
|
route.index = index
|
34
39
|
route.regexp = /(?<_#{index}>#{route.pattern.to_regexp})/
|
@@ -64,38 +69,54 @@ module Grape
|
|
64
69
|
|
65
70
|
def identity(env)
|
66
71
|
route = nil
|
67
|
-
response = transaction(env) do |input, method
|
72
|
+
response = transaction(env) do |input, method|
|
68
73
|
route = match?(input, method)
|
69
|
-
if route
|
70
|
-
env[Grape::Env::GRAPE_ROUTING_ARGS] = make_routing_args(routing_args, route, input)
|
71
|
-
route.exec(env)
|
72
|
-
end
|
74
|
+
process_route(route, env) if route
|
73
75
|
end
|
74
76
|
[response, route]
|
75
77
|
end
|
76
78
|
|
77
79
|
def rotation(env, exact_route = nil)
|
78
80
|
response = nil
|
79
|
-
input, method
|
80
|
-
|
81
|
+
input, method = *extract_input_and_method(env)
|
82
|
+
map[method].each do |route|
|
81
83
|
next if exact_route == route
|
82
84
|
next unless route.match?(input)
|
83
|
-
|
84
|
-
response = route.exec(env)
|
85
|
+
response = process_route(route, env)
|
85
86
|
break unless cascade?(response)
|
86
87
|
end
|
87
88
|
response
|
88
89
|
end
|
89
90
|
|
90
91
|
def transaction(env)
|
91
|
-
input, method
|
92
|
-
response = yield(input, method
|
92
|
+
input, method = *extract_input_and_method(env)
|
93
|
+
response = yield(input, method)
|
93
94
|
|
94
95
|
return response if response && !(cascade = cascade?(response))
|
95
96
|
neighbor = greedy_match?(input)
|
96
|
-
return unless neighbor
|
97
97
|
|
98
|
-
|
98
|
+
# If neighbor exists and request method is OPTIONS,
|
99
|
+
# return response by using #call_with_allow_headers.
|
100
|
+
return call_with_allow_headers(
|
101
|
+
env,
|
102
|
+
neighbor.allow_header,
|
103
|
+
neighbor.endpoint
|
104
|
+
) if neighbor && method == 'OPTIONS' && !cascade
|
105
|
+
|
106
|
+
route = match?(input, '*')
|
107
|
+
if route
|
108
|
+
response = process_route(route, env)
|
109
|
+
return response if response && !(cascade = cascade?(response))
|
110
|
+
end
|
111
|
+
|
112
|
+
!cascade && neighbor ? call_with_allow_headers(env, neighbor.allow_header, neighbor.endpoint) : nil
|
113
|
+
end
|
114
|
+
|
115
|
+
def process_route(route, env)
|
116
|
+
input, = *extract_input_and_method(env)
|
117
|
+
routing_args = env[Grape::Env::GRAPE_ROUTING_ARGS]
|
118
|
+
env[Grape::Env::GRAPE_ROUTING_ARGS] = make_routing_args(routing_args, route, input)
|
119
|
+
route.exec(env)
|
99
120
|
end
|
100
121
|
|
101
122
|
def make_routing_args(default_args, route, input)
|
@@ -103,11 +124,10 @@ module Grape
|
|
103
124
|
args.merge(route.params(input))
|
104
125
|
end
|
105
126
|
|
106
|
-
def
|
127
|
+
def extract_input_and_method(env)
|
107
128
|
input = string_for(env[Grape::Http::Headers::PATH_INFO])
|
108
129
|
method = env[Grape::Http::Headers::REQUEST_METHOD]
|
109
|
-
|
110
|
-
[input, method, routing_args]
|
130
|
+
[input, method]
|
111
131
|
end
|
112
132
|
|
113
133
|
def with_optimization
|
@@ -141,10 +161,6 @@ module Grape
|
|
141
161
|
response && response[1][Grape::Http::Headers::X_CASCADE] == 'pass'
|
142
162
|
end
|
143
163
|
|
144
|
-
def routes_for(method)
|
145
|
-
map[method] + map['ANY']
|
146
|
-
end
|
147
|
-
|
148
164
|
def string_for(input)
|
149
165
|
self.class.normalize_path(input)
|
150
166
|
end
|
data/lib/grape/router/pattern.rb
CHANGED
@@ -35,7 +35,15 @@ module Grape
|
|
35
35
|
end
|
36
36
|
|
37
37
|
def build_path(pattern, anchor: false, suffix: nil, **_options)
|
38
|
-
|
38
|
+
unless anchor || pattern.end_with?('*path')
|
39
|
+
pattern << '/' unless pattern.end_with?('/')
|
40
|
+
pattern << '*path'
|
41
|
+
end
|
42
|
+
|
43
|
+
pattern = pattern.split('/').tap do |parts|
|
44
|
+
parts[parts.length - 1] = '?' + parts.last
|
45
|
+
end.join('/') if pattern.end_with?('*path')
|
46
|
+
|
39
47
|
pattern + suffix.to_s
|
40
48
|
end
|
41
49
|
|
data/lib/grape/router/route.rb
CHANGED
@@ -16,6 +16,7 @@ module Grape
|
|
16
16
|
# @option opts :optional [Boolean] whether or not this scope needs to have
|
17
17
|
# any parameters set or not
|
18
18
|
# @option opts :type [Class] a type meant to govern this scope (deprecated)
|
19
|
+
# @option opts :type [Hash] group options for this scope
|
19
20
|
# @option opts :dependent_on [Symbol] if present, this scope should only
|
20
21
|
# validate if this param is present in the parent scope
|
21
22
|
# @yield the instance context, open for parameter definitions
|
@@ -25,8 +26,10 @@ module Grape
|
|
25
26
|
@api = opts[:api]
|
26
27
|
@optional = opts[:optional] || false
|
27
28
|
@type = opts[:type]
|
29
|
+
@group = opts[:group] || {}
|
28
30
|
@dependent_on = opts[:dependent_on]
|
29
31
|
@declared_params = []
|
32
|
+
@index = nil
|
30
33
|
|
31
34
|
instance_eval(&block) if block_given?
|
32
35
|
|
@@ -55,11 +58,10 @@ module Grape
|
|
55
58
|
|
56
59
|
# @return [String] the proper attribute name, with nesting considered.
|
57
60
|
def full_name(name)
|
58
|
-
|
59
|
-
when nested?
|
61
|
+
if nested?
|
60
62
|
# Find our containing element's name, and append ours.
|
61
63
|
"#{@parent.full_name(@element)}#{array_index}[#{name}]"
|
62
|
-
|
64
|
+
elsif lateral?
|
63
65
|
# Find the name of the element as if it was at the
|
64
66
|
# same nesting level as our parent.
|
65
67
|
@parent.full_name(name)
|
@@ -168,7 +170,8 @@ module Grape
|
|
168
170
|
parent: self,
|
169
171
|
optional: optional,
|
170
172
|
type: opts[:type],
|
171
|
-
&block
|
173
|
+
&block
|
174
|
+
)
|
172
175
|
end
|
173
176
|
|
174
177
|
# Returns a new parameter scope, not nested under any current-level param
|
@@ -186,7 +189,22 @@ module Grape
|
|
186
189
|
options: @optional,
|
187
190
|
type: Hash,
|
188
191
|
dependent_on: options[:dependent_on],
|
189
|
-
&block
|
192
|
+
&block
|
193
|
+
)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Returns a new parameter scope, subordinate to the current one and nested
|
197
|
+
# under the parameter corresponding to `attrs.first`.
|
198
|
+
# @param attrs [Array] the attributes passed to the `requires` or
|
199
|
+
# `optional` invocation that opened this scope.
|
200
|
+
# @yield parameter scope
|
201
|
+
def new_group_scope(attrs, &block)
|
202
|
+
self.class.new(
|
203
|
+
api: @api,
|
204
|
+
parent: self,
|
205
|
+
group: attrs.first,
|
206
|
+
&block
|
207
|
+
)
|
190
208
|
end
|
191
209
|
|
192
210
|
# Pushes declared params to parent or settings
|
@@ -369,7 +387,7 @@ module Grape
|
|
369
387
|
def extract_message_option(attrs)
|
370
388
|
return nil unless attrs.is_a?(Array)
|
371
389
|
opts = attrs.last.is_a?(Hash) ? attrs.pop : {}
|
372
|
-
|
390
|
+
opts.key?(:message) && !opts[:message].nil? ? opts.delete(:message) : nil
|
373
391
|
end
|
374
392
|
|
375
393
|
def options_key?(type, key, validations)
|
@@ -39,13 +39,12 @@ module Grape
|
|
39
39
|
def validate!(params)
|
40
40
|
attributes = AttributesIterator.new(self, @scope, params)
|
41
41
|
array_errors = []
|
42
|
-
attributes.each do |resource_params, attr_name
|
42
|
+
attributes.each do |resource_params, attr_name|
|
43
43
|
next unless @required || (resource_params.respond_to?(:key?) && resource_params.key?(attr_name))
|
44
44
|
|
45
45
|
begin
|
46
46
|
validate_param!(attr_name, resource_params)
|
47
47
|
rescue Grape::Exceptions::Validation => e
|
48
|
-
raise e unless inside_array
|
49
48
|
# we collect errors inside array because
|
50
49
|
# there may be more than one error per field
|
51
50
|
array_errors << e
|
@@ -2,7 +2,8 @@ module Grape
|
|
2
2
|
module Validations
|
3
3
|
class RegexpValidator < Base
|
4
4
|
def validate_param!(attr_name, params)
|
5
|
-
return unless params.key?
|
5
|
+
return unless params.respond_to?(:key?) && params.key?(attr_name)
|
6
|
+
return if Array.wrap(params[attr_name]).all? { |param| param.nil? || (param.to_s =~ (options_key?(:value) ? @option[:value] : @option)) }
|
6
7
|
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: message(:regexp)
|
7
8
|
end
|
8
9
|
end
|
data/lib/grape/version.rb
CHANGED
Binary file
|
data/spec/grape/api_spec.rb
CHANGED
@@ -508,6 +508,37 @@ describe Grape::API do
|
|
508
508
|
end
|
509
509
|
end
|
510
510
|
|
511
|
+
it 'allows for catch-all in a namespace' do
|
512
|
+
subject.namespace :nested do
|
513
|
+
get do
|
514
|
+
'root'
|
515
|
+
end
|
516
|
+
|
517
|
+
get 'something' do
|
518
|
+
'something'
|
519
|
+
end
|
520
|
+
|
521
|
+
route :any, '*path' do
|
522
|
+
'catch-all'
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
get 'nested'
|
527
|
+
expect(last_response.body).to eql 'root'
|
528
|
+
|
529
|
+
get 'nested/something'
|
530
|
+
expect(last_response.body).to eql 'something'
|
531
|
+
|
532
|
+
get 'nested/missing'
|
533
|
+
expect(last_response.body).to eql 'catch-all'
|
534
|
+
|
535
|
+
post 'nested'
|
536
|
+
expect(last_response.body).to eql 'catch-all'
|
537
|
+
|
538
|
+
post 'nested/something'
|
539
|
+
expect(last_response.body).to eql 'catch-all'
|
540
|
+
end
|
541
|
+
|
511
542
|
verbs = %w(post get head delete put options patch)
|
512
543
|
verbs.each do |verb|
|
513
544
|
it "allows and properly constrain a #{verb.upcase} method" do
|
@@ -516,9 +547,15 @@ describe Grape::API do
|
|
516
547
|
end
|
517
548
|
send(verb, '/example')
|
518
549
|
expect(last_response.body).to eql verb == 'head' ? '' : verb
|
519
|
-
# Call it with
|
520
|
-
|
521
|
-
|
550
|
+
# Call it with all methods other than the properly constrained one.
|
551
|
+
(verbs - [verb]).each do |other_verb|
|
552
|
+
send(other_verb, '/example')
|
553
|
+
expected_rc = if other_verb == 'options' then 204
|
554
|
+
elsif other_verb == 'head' && verb == 'get' then 200
|
555
|
+
else 405
|
556
|
+
end
|
557
|
+
expect(last_response.status).to eql expected_rc
|
558
|
+
end
|
522
559
|
end
|
523
560
|
end
|
524
561
|
|
@@ -549,6 +586,7 @@ describe Grape::API do
|
|
549
586
|
before_validation { raise 'before_validation filter should not run' }
|
550
587
|
after_validation { raise 'after_validation filter should not run' }
|
551
588
|
after { raise 'after filter should not run' }
|
589
|
+
params { requires :only_for_get }
|
552
590
|
get
|
553
591
|
end
|
554
592
|
|
@@ -573,6 +611,26 @@ describe Grape::API do
|
|
573
611
|
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
|
574
612
|
end
|
575
613
|
|
614
|
+
it 'runs all filters and body with a custom OPTIONS method' do
|
615
|
+
subject.namespace :example do
|
616
|
+
before { header 'X-Custom-Header-1', 'foo' }
|
617
|
+
before_validation { header 'X-Custom-Header-2', 'foo' }
|
618
|
+
after_validation { header 'X-Custom-Header-3', 'foo' }
|
619
|
+
after { header 'X-Custom-Header-4', 'foo' }
|
620
|
+
options { 'yup' }
|
621
|
+
get
|
622
|
+
end
|
623
|
+
|
624
|
+
options '/example'
|
625
|
+
expect(last_response.status).to eql 200
|
626
|
+
expect(last_response.body).to eql 'yup'
|
627
|
+
expect(last_response.headers['Allow']).to be_nil
|
628
|
+
expect(last_response.headers['X-Custom-Header-1']).to eql 'foo'
|
629
|
+
expect(last_response.headers['X-Custom-Header-2']).to eql 'foo'
|
630
|
+
expect(last_response.headers['X-Custom-Header-3']).to eql 'foo'
|
631
|
+
expect(last_response.headers['X-Custom-Header-4']).to eql 'foo'
|
632
|
+
end
|
633
|
+
|
576
634
|
context 'when format is xml' do
|
577
635
|
it 'returns a 405 for an unsupported method' do
|
578
636
|
subject.format :xml
|
@@ -594,8 +652,8 @@ XML
|
|
594
652
|
context 'when accessing env' do
|
595
653
|
it 'returns a 405 for an unsupported method' do
|
596
654
|
subject.before do
|
597
|
-
|
598
|
-
|
655
|
+
_customheader1 = headers['X-Custom-Header']
|
656
|
+
_customheader2 = env['HTTP_X_CUSTOM_HEADER']
|
599
657
|
end
|
600
658
|
subject.get 'example' do
|
601
659
|
'example'
|
@@ -630,7 +688,11 @@ XML
|
|
630
688
|
|
631
689
|
describe 'adds an OPTIONS route that' do
|
632
690
|
before do
|
633
|
-
subject.before
|
691
|
+
subject.before { header 'X-Custom-Header', 'foo' }
|
692
|
+
subject.before_validation { header 'X-Custom-Header-2', 'bar' }
|
693
|
+
subject.after_validation { header 'X-Custom-Header-3', 'baz' }
|
694
|
+
subject.after { header 'X-Custom-Header-4', 'bing' }
|
695
|
+
subject.params { requires :only_for_get }
|
634
696
|
subject.get 'example' do
|
635
697
|
'example'
|
636
698
|
end
|
@@ -652,10 +714,22 @@ XML
|
|
652
714
|
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
|
653
715
|
end
|
654
716
|
|
655
|
-
it '
|
717
|
+
it 'calls before hook' do
|
656
718
|
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
|
657
719
|
end
|
658
720
|
|
721
|
+
it 'does not call before_validation hook' do
|
722
|
+
expect(last_response.headers.key?('X-Custom-Header-2')).to be false
|
723
|
+
end
|
724
|
+
|
725
|
+
it 'does not call after_validation hook' do
|
726
|
+
expect(last_response.headers.key?('X-Custom-Header-3')).to be false
|
727
|
+
end
|
728
|
+
|
729
|
+
it 'calls after hook' do
|
730
|
+
expect(last_response.headers['X-Custom-Header-4']).to eq 'bing'
|
731
|
+
end
|
732
|
+
|
659
733
|
it 'has no Content-Type' do
|
660
734
|
expect(last_response.content_type).to be_nil
|
661
735
|
end
|
@@ -712,9 +786,6 @@ XML
|
|
712
786
|
subject.post :example do
|
713
787
|
'example'
|
714
788
|
end
|
715
|
-
subject.route :any, '*path' do
|
716
|
-
error! :not_found, 404
|
717
|
-
end
|
718
789
|
get '/example'
|
719
790
|
end
|
720
791
|
|
@@ -1131,7 +1202,7 @@ XML
|
|
1131
1202
|
def initialize(app, *args)
|
1132
1203
|
@args = args
|
1133
1204
|
@app = app
|
1134
|
-
@block = true
|
1205
|
+
@block = block_given? ? true : nil
|
1135
1206
|
end
|
1136
1207
|
|
1137
1208
|
def call(env)
|
@@ -2555,13 +2626,11 @@ XML
|
|
2555
2626
|
params: {
|
2556
2627
|
'param1' => { required: true },
|
2557
2628
|
'param2' => { required: false }
|
2558
|
-
}
|
2559
|
-
},
|
2629
|
+
} },
|
2560
2630
|
{ description: 'global description',
|
2561
2631
|
params: {
|
2562
2632
|
'param2' => { required: false }
|
2563
|
-
}
|
2564
|
-
}
|
2633
|
+
} }
|
2565
2634
|
]
|
2566
2635
|
end
|
2567
2636
|
it 'merges the parameters of the namespace with the parameters of the method' do
|
@@ -2585,8 +2654,7 @@ XML
|
|
2585
2654
|
params: {
|
2586
2655
|
'ns_param' => { required: true, desc: 'namespace parameter' },
|
2587
2656
|
'method_param' => { required: false, desc: 'method parameter' }
|
2588
|
-
}
|
2589
|
-
}
|
2657
|
+
} }
|
2590
2658
|
]
|
2591
2659
|
end
|
2592
2660
|
it 'merges the parameters of nested namespaces' do
|
@@ -2618,8 +2686,7 @@ XML
|
|
2618
2686
|
'ns1_param' => { required: true, desc: 'ns1 param' },
|
2619
2687
|
'ns2_param' => { required: true, desc: 'ns2 param' },
|
2620
2688
|
'method_param' => { required: false, desc: 'method param' }
|
2621
|
-
}
|
2622
|
-
}
|
2689
|
+
} }
|
2623
2690
|
]
|
2624
2691
|
end
|
2625
2692
|
it 'groups nested params and prevents overwriting of params with same name in different groups' do
|
@@ -2662,8 +2729,7 @@ XML
|
|
2662
2729
|
'root_param' => { required: true, desc: 'root param' },
|
2663
2730
|
'nested' => { required: true, type: 'Array' },
|
2664
2731
|
'nested[nested_param]' => { required: true, desc: 'nested param' }
|
2665
|
-
}
|
2666
|
-
}
|
2732
|
+
} }
|
2667
2733
|
]
|
2668
2734
|
end
|
2669
2735
|
it 'allows to set the type attribute on :group element' do
|