grape 0.2.6 → 0.3.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.
- data/{CHANGELOG.markdown → CHANGELOG.md} +21 -1
- data/Gemfile +1 -0
- data/{README.markdown → README.md} +178 -125
- data/grape.gemspec +1 -1
- data/lib/grape.rb +25 -3
- data/lib/grape/api.rb +43 -20
- data/lib/grape/endpoint.rb +32 -13
- data/lib/grape/exceptions/base.rb +50 -1
- data/lib/grape/exceptions/invalid_formatter.rb +13 -0
- data/lib/grape/exceptions/invalid_versioner_option.rb +14 -0
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +15 -0
- data/lib/grape/exceptions/missing_mime_type.rb +14 -0
- data/lib/grape/exceptions/missing_option.rb +13 -0
- data/lib/grape/exceptions/missing_vendor_option.rb +13 -0
- data/lib/grape/exceptions/unknown_options.rb +14 -0
- data/lib/grape/exceptions/unknown_validator.rb +12 -0
- data/lib/grape/exceptions/{validation_error.rb → validation.rb} +3 -1
- data/lib/grape/formatter/xml.rb +2 -1
- data/lib/grape/locale/en.yml +20 -0
- data/lib/grape/middleware/base.rb +0 -5
- data/lib/grape/middleware/error.rb +1 -2
- data/lib/grape/middleware/formatter.rb +9 -5
- data/lib/grape/middleware/versioner.rb +1 -1
- data/lib/grape/middleware/versioner/header.rb +16 -6
- data/lib/grape/middleware/versioner/param.rb +1 -1
- data/lib/grape/middleware/versioner/path.rb +1 -1
- data/lib/grape/util/content_types.rb +0 -2
- data/lib/grape/validations.rb +7 -14
- data/lib/grape/validations/coerce.rb +2 -1
- data/lib/grape/validations/presence.rb +2 -1
- data/lib/grape/validations/regexp.rb +2 -1
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +150 -5
- data/spec/grape/endpoint_spec.rb +51 -157
- data/spec/grape/entity_spec.rb +142 -520
- data/spec/grape/exceptions/invalid_formatter_spec.rb +18 -0
- data/spec/grape/exceptions/invalid_versioner_option_spec.rb +18 -0
- data/spec/grape/exceptions/missing_mime_type_spec.rb +24 -0
- data/spec/grape/exceptions/missing_option_spec.rb +18 -0
- data/spec/grape/exceptions/unknown_options_spec.rb +18 -0
- data/spec/grape/exceptions/unknown_validator_spec.rb +18 -0
- data/spec/grape/middleware/formatter_spec.rb +40 -34
- data/spec/grape/middleware/versioner/header_spec.rb +78 -20
- data/spec/grape/middleware/versioner/path_spec.rb +12 -8
- data/spec/grape/validations/coerce_spec.rb +1 -0
- data/spec/grape/validations/presence_spec.rb +8 -8
- data/spec/grape/validations_spec.rb +26 -3
- data/spec/spec_helper.rb +3 -6
- metadata +44 -9
- data/lib/grape/entity.rb +0 -386
@@ -2,11 +2,13 @@ require 'grape/exceptions/base'
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Exceptions
|
5
|
-
class
|
5
|
+
class Validation < Grape::Exceptions::Base
|
6
6
|
attr_accessor :param
|
7
7
|
|
8
8
|
def initialize(args = {})
|
9
9
|
@param = args[:param].to_s if args.has_key? :param
|
10
|
+
attribute = translate_attribute(@param)
|
11
|
+
args[:message] = translate_message(args[:message_key], :attribute => attribute)
|
10
12
|
super
|
11
13
|
end
|
12
14
|
end
|
data/lib/grape/formatter/xml.rb
CHANGED
data/lib/grape/locale/en.yml
CHANGED
@@ -5,3 +5,23 @@ en:
|
|
5
5
|
coerce: 'invalid parameter: %{attribute}'
|
6
6
|
presence: 'missing parameter: %{attribute}'
|
7
7
|
regexp: 'invalid parameter: %{attribute}'
|
8
|
+
missing_vendor_option:
|
9
|
+
problem: 'missing :vendor option.'
|
10
|
+
summary: 'when version using header, you must specify :verdor option. '
|
11
|
+
resolution: "eg: version 'v1', :using => :header, :vendor => 'twitter'"
|
12
|
+
missing_mime_type:
|
13
|
+
problem: 'missing mime type for %{new_format}'
|
14
|
+
resolution:
|
15
|
+
"you can choose exist mime type from Grape::ContentTypes::CONTENT_TYPES
|
16
|
+
or add your own with content_type :%{new_format}, 'application/%{new_format}'
|
17
|
+
"
|
18
|
+
invalid_with_option_for_represent:
|
19
|
+
problem: 'You must specify an entity class in the :with option.'
|
20
|
+
resolution: 'eg: represent User, :with => Entity::User'
|
21
|
+
missing_option: 'You must specify :%{option} options.'
|
22
|
+
invalid_formatter: 'cannot convert %{klass} to %{to_format}'
|
23
|
+
invalid_versioner_option:
|
24
|
+
problem: 'Unknown :using for versioner: %{strategy}'
|
25
|
+
resolution: 'available strategy for :using is :path, :header, :param'
|
26
|
+
unknown_validator: 'unknown validator: %{validator_type}'
|
27
|
+
unknown_options: 'unknown options: %{options}'
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'grape/middleware/base'
|
2
|
-
require 'multi_json'
|
3
2
|
|
4
3
|
module Grape
|
5
4
|
module Middleware
|
@@ -62,7 +61,7 @@ module Grape
|
|
62
61
|
def format_message(message, backtrace)
|
63
62
|
format = env['api.format'] || options[:format]
|
64
63
|
formatter = Grape::ErrorFormatter::Base.formatter_for(format, options)
|
65
|
-
throw :error, :status => 406, :message => "The requested format #{format} is not supported." unless formatter
|
64
|
+
throw :error, :status => 406, :message => "The requested format '#{format}' is not supported." unless formatter
|
66
65
|
formatter.call(message, backtrace, options, env)
|
67
66
|
end
|
68
67
|
|
@@ -24,8 +24,12 @@ module Grape
|
|
24
24
|
def after
|
25
25
|
status, headers, bodies = *@app_response
|
26
26
|
formatter = Grape::Formatter::Base.formatter_for env['api.format'], options
|
27
|
-
|
28
|
-
|
27
|
+
begin
|
28
|
+
bodymap = bodies.collect do |body|
|
29
|
+
formatter.call body, env
|
30
|
+
end
|
31
|
+
rescue Exception => e
|
32
|
+
throw :error, :status => 500, :message => e.message
|
29
33
|
end
|
30
34
|
headers['Content-Type'] = content_type_for(env['api.format']) unless headers['Content-Type']
|
31
35
|
Rack::Response.new(bodymap, status, headers).to_a
|
@@ -34,7 +38,7 @@ module Grape
|
|
34
38
|
private
|
35
39
|
|
36
40
|
def read_body_input
|
37
|
-
if (request.post? || request.put?) && (! request.form_data?) && (! request.parseable_data?) && (request.content_length.to_i > 0)
|
41
|
+
if (request.post? || request.put? || request.patch?) && (! request.form_data?) && (! request.parseable_data?) && (request.content_length.to_i > 0)
|
38
42
|
if env['rack.input'] && (body = env['rack.input'].read).length > 0
|
39
43
|
begin
|
40
44
|
fmt = mime_types[request.media_type] if request.media_type
|
@@ -50,7 +54,7 @@ module Grape
|
|
50
54
|
end
|
51
55
|
end
|
52
56
|
else
|
53
|
-
throw :error, :status => 406, :message =>
|
57
|
+
throw :error, :status => 406, :message => "The requested content-type '#{request.media_type}' is not supported."
|
54
58
|
end
|
55
59
|
ensure
|
56
60
|
env['rack.input'].rewind
|
@@ -64,7 +68,7 @@ module Grape
|
|
64
68
|
if content_type_for(fmt)
|
65
69
|
env['api.format'] = fmt
|
66
70
|
else
|
67
|
-
throw :error, :status => 406, :message =>
|
71
|
+
throw :error, :status => 406, :message => "The requested format '#{fmt}' is not supported."
|
68
72
|
end
|
69
73
|
end
|
70
74
|
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'grape/middleware/base'
|
2
|
-
require 'rack/accept'
|
3
2
|
|
4
3
|
module Grape
|
5
4
|
module Middleware
|
@@ -30,7 +29,7 @@ module Grape
|
|
30
29
|
if strict?
|
31
30
|
# If no Accept header:
|
32
31
|
if header.qvalues.empty?
|
33
|
-
throw :error, :status => 406, :headers =>
|
32
|
+
throw :error, :status => 406, :headers => error_headers, :message => 'Accept header must be set.'
|
34
33
|
end
|
35
34
|
# Remove any acceptable content types with ranges.
|
36
35
|
header.qvalues.reject! do |media_type,_|
|
@@ -38,7 +37,7 @@ module Grape
|
|
38
37
|
end
|
39
38
|
# If all Accept headers included a range:
|
40
39
|
if header.qvalues.empty?
|
41
|
-
throw :error, :status => 406, :headers =>
|
40
|
+
throw :error, :status => 406, :headers => error_headers, :message => 'Accept header must not contain ranges ("*").'
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
@@ -56,10 +55,10 @@ module Grape
|
|
56
55
|
end
|
57
56
|
# If none of the available content types are acceptable:
|
58
57
|
elsif strict?
|
59
|
-
throw :error, :status => 406, :headers =>
|
58
|
+
throw :error, :status => 406, :headers => error_headers, :message => '406 Not Acceptable'
|
60
59
|
# If all acceptable content types specify a vendor or version that doesn't exist:
|
61
|
-
elsif header.values.all?{|media_type| has_vendor?(media_type) || has_version?(media_type)}
|
62
|
-
throw :error, :status => 406, :headers =>
|
60
|
+
elsif header.values.all?{ |media_type| has_vendor?(media_type) || has_version?(media_type)}
|
61
|
+
throw :error, :status => 406, :headers => error_headers, :message => 'API vendor or version not found.'
|
63
62
|
end
|
64
63
|
end
|
65
64
|
|
@@ -96,6 +95,17 @@ module Grape
|
|
96
95
|
options[:version_options] && options[:version_options][:strict]
|
97
96
|
end
|
98
97
|
|
98
|
+
# By default those errors contain an `X-Cascade` header set to `pass`, which allows nesting and stacking
|
99
|
+
# of routes (see [Rack::Mount](https://github.com/josh/rack-mount) for more information). To prevent
|
100
|
+
# this behavior, and not add the `X-Cascade` header, one can set the `:cascade` option to `false`.
|
101
|
+
def cascade?
|
102
|
+
options[:version_options] && (options[:version_options].has_key?(:cascade) ? options[:version_options][:cascade] : true)
|
103
|
+
end
|
104
|
+
|
105
|
+
def error_headers
|
106
|
+
cascade? ? { 'X-Cascade' => 'pass' } : {}
|
107
|
+
end
|
108
|
+
|
99
109
|
# @param [String] media_type a content type
|
100
110
|
# @return [Boolean] whether the content type sets a vendor
|
101
111
|
def has_vendor?(media_type)
|
@@ -30,7 +30,7 @@ module Grape
|
|
30
30
|
potential_version = request.params[paramkey]
|
31
31
|
|
32
32
|
unless potential_version.nil?
|
33
|
-
if options[:versions] && !options[:versions].
|
33
|
+
if options[:versions] && ! options[:versions].find { |v| v.to_s == potential_version }
|
34
34
|
throw :error, :status => 404, :message => "404 API Version Not Found", :headers => {'X-Cascade' => 'pass'}
|
35
35
|
end
|
36
36
|
env['api.version'] = potential_version
|
@@ -34,7 +34,7 @@ module Grape
|
|
34
34
|
pieces = path.split('/')
|
35
35
|
potential_version = pieces[1]
|
36
36
|
if potential_version =~ options[:pattern]
|
37
|
-
if options[:versions] && !options[:versions].
|
37
|
+
if options[:versions] && ! options[:versions].find { |v| v.to_s == potential_version }
|
38
38
|
throw :error, :status => 404, :message => "404 API Version Not Found"
|
39
39
|
end
|
40
40
|
|
data/lib/grape/validations.rb
CHANGED
@@ -1,7 +1,3 @@
|
|
1
|
-
require 'virtus'
|
2
|
-
require 'i18n'
|
3
|
-
|
4
|
-
I18n.load_path << File.expand_path('../locale/en.yml', __FILE__)
|
5
1
|
module Grape
|
6
2
|
|
7
3
|
module Validations
|
@@ -18,25 +14,22 @@ module Grape
|
|
18
14
|
@scope = scope
|
19
15
|
|
20
16
|
if options.is_a?(Hash) && !options.empty?
|
21
|
-
raise
|
17
|
+
raise Grape::Exceptions.UnknownOptions.new(options.keys)
|
22
18
|
end
|
23
19
|
end
|
24
20
|
|
25
21
|
def validate!(params)
|
26
22
|
params = @scope.params(params)
|
27
23
|
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
(params.is_a?(Array) ? params : [params]).each do |resource_params|
|
25
|
+
@attrs.each do |attr_name|
|
26
|
+
if @required || resource_params.has_key?(attr_name)
|
27
|
+
validate_param!(attr_name, resource_params)
|
28
|
+
end
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
34
32
|
|
35
|
-
def i18n_message(type, attribute)
|
36
|
-
i18n_attr = I18n.t("grape.errors.attributes.#{attribute}", :default => attribute.to_s)
|
37
|
-
I18n.t("grape.errors.messages.#{type}", :attribute => i18n_attr)
|
38
|
-
end
|
39
|
-
|
40
33
|
private
|
41
34
|
|
42
35
|
def self.convert_to_short_name(klass)
|
@@ -167,7 +160,7 @@ module Grape
|
|
167
160
|
if validator_class
|
168
161
|
(@api.settings.peek[:validations] ||= []) << validator_class.new(attrs, options, doc_attrs[:required], self)
|
169
162
|
else
|
170
|
-
raise
|
163
|
+
raise Grape::Exceptions::UnknownValidator.new(type)
|
171
164
|
end
|
172
165
|
end
|
173
166
|
|
@@ -12,7 +12,8 @@ module Grape
|
|
12
12
|
if valid_type?(new_value)
|
13
13
|
params[attr_name] = new_value
|
14
14
|
else
|
15
|
-
raise Grape::Exceptions::
|
15
|
+
raise Grape::Exceptions::Validation, :status => 400,
|
16
|
+
:param => @scope.full_name(attr_name), :message_key => :coerce
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
@@ -3,7 +3,8 @@ module Grape
|
|
3
3
|
class PresenceValidator < Validator
|
4
4
|
def validate_param!(attr_name, params)
|
5
5
|
unless params.has_key?(attr_name)
|
6
|
-
raise Grape::Exceptions::
|
6
|
+
raise Grape::Exceptions::Validation, :status => 400,
|
7
|
+
:param => @scope.full_name(attr_name), :message_key => :presence
|
7
8
|
end
|
8
9
|
end
|
9
10
|
end
|
@@ -4,7 +4,8 @@ module Grape
|
|
4
4
|
class RegexpValidator < SingleOptionValidator
|
5
5
|
def validate_param!(attr_name, params)
|
6
6
|
if params[attr_name] && !( params[attr_name].to_s =~ @option )
|
7
|
-
raise Grape::Exceptions::
|
7
|
+
raise Grape::Exceptions::Validation, :status => 400,
|
8
|
+
:param => @scope.full_name(attr_name), :message_key => :regexp
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
data/lib/grape/version.rb
CHANGED
data/spec/grape/api_spec.rb
CHANGED
@@ -100,7 +100,7 @@ describe Grape::API do
|
|
100
100
|
|
101
101
|
describe '.represent' do
|
102
102
|
it 'requires a :with option' do
|
103
|
-
expect{ subject.represent Object, {} }.to raise_error(
|
103
|
+
expect{ subject.represent Object, {} }.to raise_error(Grape::Exceptions::InvalidWithOptionForRepresent)
|
104
104
|
end
|
105
105
|
|
106
106
|
it 'adds the association to the :representations setting' do
|
@@ -108,6 +108,7 @@ describe Grape::API do
|
|
108
108
|
subject.represent Object, :with => klass
|
109
109
|
subject.settings[:representations][Object].should == klass
|
110
110
|
end
|
111
|
+
|
111
112
|
end
|
112
113
|
|
113
114
|
describe '.namespace' do
|
@@ -359,7 +360,7 @@ describe Grape::API do
|
|
359
360
|
send(verb, '/example')
|
360
361
|
last_response.body.should eql verb == 'head' ? '' : verb
|
361
362
|
# Call it with a method other than the properly constrained one.
|
362
|
-
send(used_verb = verbs[(verbs.index(verb) +
|
363
|
+
send(used_verb = verbs[(verbs.index(verb) + 2) % verbs.size], '/example')
|
363
364
|
last_response.status.should eql used_verb == 'options' ? 204 :405
|
364
365
|
end
|
365
366
|
end
|
@@ -391,18 +392,70 @@ describe Grape::API do
|
|
391
392
|
"example"
|
392
393
|
end
|
393
394
|
put '/example'
|
394
|
-
last_response.headers['Allow'].should eql 'OPTIONS, GET, POST'
|
395
|
+
last_response.headers['Allow'].should eql 'OPTIONS, GET, POST, HEAD'
|
395
396
|
end
|
396
397
|
|
397
398
|
it 'adds an OPTIONS route that returns a 204 and an Allow header' do
|
398
399
|
subject.get 'example' do
|
399
400
|
"example"
|
400
401
|
end
|
402
|
+
options '/example'
|
403
|
+
last_response.status.should eql 204
|
404
|
+
last_response.body.should eql ''
|
405
|
+
last_response.headers['Allow'].should eql 'OPTIONS, GET, HEAD'
|
406
|
+
end
|
407
|
+
|
408
|
+
it 'allows HEAD on a GET request' do
|
409
|
+
subject.get 'example' do
|
410
|
+
"example"
|
411
|
+
end
|
412
|
+
head '/example'
|
413
|
+
last_response.status.should eql 200
|
414
|
+
last_response.body.should eql ''
|
415
|
+
end
|
416
|
+
|
417
|
+
it 'overwrites the default HEAD request' do
|
418
|
+
subject.head 'example' do
|
419
|
+
error! 'nothing to see here', 400
|
420
|
+
end
|
421
|
+
subject.get 'example' do
|
422
|
+
"example"
|
423
|
+
end
|
424
|
+
head '/example'
|
425
|
+
last_response.status.should eql 400
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
context "do_not_route_head!" do
|
430
|
+
before :each do
|
431
|
+
subject.do_not_route_head!
|
432
|
+
subject.get 'example' do
|
433
|
+
"example"
|
434
|
+
end
|
435
|
+
end
|
436
|
+
it 'options does not contain HEAD' do
|
401
437
|
options '/example'
|
402
438
|
last_response.status.should eql 204
|
403
439
|
last_response.body.should eql ''
|
404
440
|
last_response.headers['Allow'].should eql 'OPTIONS, GET'
|
405
441
|
end
|
442
|
+
it 'does not allow HEAD on a GET request' do
|
443
|
+
head '/example'
|
444
|
+
last_response.status.should eql 405
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
context "do_not_route_options!" do
|
449
|
+
before :each do
|
450
|
+
subject.do_not_route_options!
|
451
|
+
subject.get 'example' do
|
452
|
+
"example"
|
453
|
+
end
|
454
|
+
end
|
455
|
+
it 'options does not exist' do
|
456
|
+
options '/example'
|
457
|
+
last_response.status.should eql 405
|
458
|
+
end
|
406
459
|
end
|
407
460
|
|
408
461
|
describe 'filters' do
|
@@ -1013,7 +1066,7 @@ describe Grape::API do
|
|
1013
1066
|
end
|
1014
1067
|
get '/excel.json'
|
1015
1068
|
last_response.status.should == 406
|
1016
|
-
last_response.body.should == "The requested format is not supported."
|
1069
|
+
last_response.body.should == "The requested format 'txt' is not supported."
|
1017
1070
|
end
|
1018
1071
|
end
|
1019
1072
|
|
@@ -1116,7 +1169,7 @@ describe Grape::API do
|
|
1116
1169
|
last_response.body.should eql "elpmis"
|
1117
1170
|
end
|
1118
1171
|
end
|
1119
|
-
context "
|
1172
|
+
context "multi_xml" do
|
1120
1173
|
it "doesn't parse yaml" do
|
1121
1174
|
subject.put :yaml do
|
1122
1175
|
params[:tag]
|
@@ -1512,6 +1565,34 @@ describe Grape::API do
|
|
1512
1565
|
subject.routes.first.route_path.should =~ /\/cool\/awesome/
|
1513
1566
|
subject.routes.last.route_path.should =~ /\/cool\/sauce/
|
1514
1567
|
end
|
1568
|
+
|
1569
|
+
it 'mounts on a path' do
|
1570
|
+
subject.namespace :cool do
|
1571
|
+
app = Class.new(Grape::API)
|
1572
|
+
app.get '/awesome' do
|
1573
|
+
"sauce"
|
1574
|
+
end
|
1575
|
+
mount app => '/mounted'
|
1576
|
+
end
|
1577
|
+
get "/mounted/cool/awesome"
|
1578
|
+
last_response.status.should == 200
|
1579
|
+
last_response.body.should == "sauce"
|
1580
|
+
end
|
1581
|
+
|
1582
|
+
it 'mounts on a nested path' do
|
1583
|
+
app1 = Class.new(Grape::API)
|
1584
|
+
app2 = Class.new(Grape::API)
|
1585
|
+
app2.get '/nice' do
|
1586
|
+
"play"
|
1587
|
+
end
|
1588
|
+
# note that the reverse won't work, mount from outside-in
|
1589
|
+
subject.mount app1 => '/app1'
|
1590
|
+
app1.mount app2 => '/app2'
|
1591
|
+
get "/app1/app2/nice"
|
1592
|
+
last_response.status.should == 200
|
1593
|
+
last_response.body.should == "play"
|
1594
|
+
end
|
1595
|
+
|
1515
1596
|
end
|
1516
1597
|
end
|
1517
1598
|
|
@@ -1690,6 +1771,55 @@ describe Grape::API do
|
|
1690
1771
|
last_response.body.should == '[{"abc":"def"},{"abc":"def"}]'
|
1691
1772
|
end
|
1692
1773
|
end
|
1774
|
+
context ":xml" do
|
1775
|
+
before(:each) do
|
1776
|
+
subject.format :xml
|
1777
|
+
end
|
1778
|
+
it 'string' do
|
1779
|
+
subject.get "/example" do
|
1780
|
+
"example"
|
1781
|
+
end
|
1782
|
+
get '/example'
|
1783
|
+
last_response.status.should == 500
|
1784
|
+
last_response.body.should == <<-XML
|
1785
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
1786
|
+
<error>
|
1787
|
+
<message>cannot convert String to xml</message>
|
1788
|
+
</error>
|
1789
|
+
XML
|
1790
|
+
end
|
1791
|
+
it 'hash' do
|
1792
|
+
subject.get "/example" do
|
1793
|
+
ActiveSupport::OrderedHash[
|
1794
|
+
:example1, "example1",
|
1795
|
+
:example2, "example2"
|
1796
|
+
]
|
1797
|
+
end
|
1798
|
+
get '/example'
|
1799
|
+
last_response.status.should == 200
|
1800
|
+
last_response.body.should == <<-XML
|
1801
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
1802
|
+
<hash>
|
1803
|
+
<example1>example1</example1>
|
1804
|
+
<example2>example2</example2>
|
1805
|
+
</hash>
|
1806
|
+
XML
|
1807
|
+
end
|
1808
|
+
it 'array' do
|
1809
|
+
subject.get "/example" do
|
1810
|
+
[ "example1", "example2" ]
|
1811
|
+
end
|
1812
|
+
get '/example'
|
1813
|
+
last_response.status.should == 200
|
1814
|
+
last_response.body.should == <<-XML
|
1815
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
1816
|
+
<strings type="array">
|
1817
|
+
<string>example1</string>
|
1818
|
+
<string>example2</string>
|
1819
|
+
</strings>
|
1820
|
+
XML
|
1821
|
+
end
|
1822
|
+
end
|
1693
1823
|
end
|
1694
1824
|
|
1695
1825
|
context "catch-all" do
|
@@ -1725,4 +1855,19 @@ describe Grape::API do
|
|
1725
1855
|
end
|
1726
1856
|
end
|
1727
1857
|
|
1858
|
+
context "cascading" do
|
1859
|
+
it "cascades" do
|
1860
|
+
subject.version 'v1', :using => :path, :cascade => true
|
1861
|
+
get "/v1/hello"
|
1862
|
+
last_response.status.should == 404
|
1863
|
+
last_response.headers["X-Cascade"].should == "pass"
|
1864
|
+
end
|
1865
|
+
|
1866
|
+
it "does not cascade" do
|
1867
|
+
subject.version 'v2', :using => :path, :cascade => false
|
1868
|
+
get "/v2/hello"
|
1869
|
+
last_response.status.should == 404
|
1870
|
+
last_response.headers.keys.should_not include "X-Cascade"
|
1871
|
+
end
|
1872
|
+
end
|
1728
1873
|
end
|