grape 0.2.1.1 → 0.2.2

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.

@@ -0,0 +1,61 @@
1
+ module Grape
2
+
3
+ class API
4
+ Boolean = Virtus::Attribute::Boolean
5
+ end
6
+
7
+ module Validations
8
+
9
+ class CoerceValidator < SingleOptionValidator
10
+ def validate_param!(attr_name, params)
11
+ new_value = coerce_value(@option, params[attr_name])
12
+ if valid_type?(new_value)
13
+ params[attr_name] = new_value
14
+ else
15
+ raise ValidationError, :status => 400, :param => attr_name, :message => "invalid parameter: #{attr_name}"
16
+ end
17
+ end
18
+
19
+ class InvalidValue; end
20
+ private
21
+
22
+ def _valid_array_type?(type, values)
23
+ values.all? do |val|
24
+ _valid_single_type?(type, val)
25
+ end
26
+ end
27
+
28
+ def _valid_single_type?(klass, val)
29
+ # allow nil, to ignore when a parameter is absent
30
+ return true if val.nil?
31
+ if klass == Virtus::Attribute::Boolean
32
+ val.is_a?(TrueClass) || val.is_a?(FalseClass)
33
+ elsif klass == Rack::Multipart::UploadedFile
34
+ val.is_a?(Hashie::Mash) && val.key?(:tempfile)
35
+ else
36
+ val.is_a?(klass)
37
+ end
38
+ end
39
+
40
+ def valid_type?(val)
41
+ if @option.is_a?(Array)
42
+ _valid_array_type?(@option[0], val)
43
+ else
44
+ _valid_single_type?(@option, val)
45
+ end
46
+ end
47
+
48
+ def coerce_value(type, val)
49
+ converter = Virtus::Attribute.build(:a, type)
50
+ converter.coerce(val)
51
+
52
+ # not the prettiest but some invalid coercion can currently trigger
53
+ # errors in Virtus (see coerce_spec.rb)
54
+ rescue
55
+ InvalidValue.new
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,11 @@
1
+ module Grape
2
+ module Validations
3
+ class PresenceValidator < Validator
4
+ def validate_param!(attr_name, params)
5
+ unless params.has_key?(attr_name)
6
+ raise ValidationError, :status => 400, :param => attr_name, :message => "missing parameter: #{attr_name}"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Grape
2
+ module Validations
3
+
4
+ class RegexpValidator < SingleOptionValidator
5
+ def validate_param!(attr_name, params)
6
+ if params[attr_name] && !( params[attr_name].to_s =~ @option )
7
+ raise ValidationError, :status => 400, :param => attr_name, :message => "invalid parameter: #{attr_name}"
8
+ end
9
+ end
10
+ end
11
+
12
+ end
13
+ end
@@ -1,3 +1,3 @@
1
1
  module Grape
2
- VERSION = '0.2.1.1'
2
+ VERSION = '0.2.2'
3
3
  end
@@ -21,6 +21,21 @@ describe Grape::API do
21
21
  end
22
22
  end
23
23
 
24
+ describe '.version' do
25
+ context 'when defined' do
26
+ it 'should return version value' do
27
+ subject.version 'v1'
28
+ subject.version.should == 'v1'
29
+ end
30
+ end
31
+
32
+ context 'when not defined' do
33
+ it 'should return nil' do
34
+ subject.version.should be_nil
35
+ end
36
+ end
37
+ end
38
+
24
39
  describe '.version using path' do
25
40
  it_should_behave_like 'versioning' do
26
41
  let(:macro_options) do
@@ -68,7 +83,7 @@ describe Grape::API do
68
83
  end
69
84
 
70
85
  it 'should route if any media type is allowed' do
71
-
86
+
72
87
  end
73
88
  end
74
89
 
@@ -133,7 +148,7 @@ describe Grape::API do
133
148
  get '/members/23'
134
149
  last_response.body.should == "23"
135
150
  end
136
-
151
+
137
152
  it 'should be callable with nil just to push onto the stack' do
138
153
  subject.namespace do
139
154
  version 'v2', :using => :path
@@ -146,7 +161,7 @@ describe Grape::API do
146
161
  get '/hello'
147
162
  last_response.body.should == "outer"
148
163
  end
149
-
164
+
150
165
  %w(group resource resources segment).each do |als|
151
166
  it "`.#{als}` should be an alias" do
152
167
  subject.send(als, :awesome) do
@@ -191,7 +206,7 @@ describe Grape::API do
191
206
  RSpec::Mocks::Mock.new(:to_json => 'abc', :to_txt => 'def')
192
207
  end
193
208
  end
194
-
209
+
195
210
  it "should allow .json" do
196
211
  get '/abc.json'
197
212
  last_response.status.should == 200
@@ -245,7 +260,7 @@ describe Grape::API do
245
260
  subject.route([:get, :post], '/:id/first') do
246
261
  "first"
247
262
  end
248
-
263
+
249
264
  subject.route([:get, :post], '/:id') do
250
265
  "ola"
251
266
  end
@@ -286,8 +301,8 @@ describe Grape::API do
286
301
  send(verb, '/example')
287
302
  last_response.body.should eql verb == 'head' ? '' : verb
288
303
  # Call it with a method other than the properly constrained one.
289
- send(verbs[(verbs.index(verb) + 1) % verbs.size], '/example')
290
- last_response.status.should eql 404
304
+ send(used_verb = verbs[(verbs.index(verb) + 1) % verbs.size], '/example')
305
+ last_response.status.should eql used_verb == 'options' ? 204 :405
291
306
  end
292
307
  end
293
308
 
@@ -300,6 +315,36 @@ describe Grape::API do
300
315
  last_response.status.should eql 201
301
316
  last_response.body.should eql 'Created'
302
317
  end
318
+
319
+ it 'should return a 405 for an unsupported method' do
320
+ subject.get 'example' do
321
+ "example"
322
+ end
323
+ put '/example'
324
+ last_response.status.should eql 405
325
+ last_response.body.should eql ''
326
+ end
327
+
328
+ specify '405 responses should include an Allow header specifying supported methods' do
329
+ subject.get 'example' do
330
+ "example"
331
+ end
332
+ subject.post 'example' do
333
+ "example"
334
+ end
335
+ put '/example'
336
+ last_response.headers['Allow'].should eql 'OPTIONS, GET, POST'
337
+ end
338
+
339
+ it 'should add an OPTIONS route that returns a 204 and an Allow header' do
340
+ subject.get 'example' do
341
+ "example"
342
+ end
343
+ options '/example'
344
+ last_response.status.should eql 204
345
+ last_response.body.should eql ''
346
+ last_response.headers['Allow'].should eql 'OPTIONS, GET'
347
+ end
303
348
  end
304
349
 
305
350
  describe 'filters' do
@@ -314,6 +359,20 @@ describe Grape::API do
314
359
  last_response.body.should eql 'first second'
315
360
  end
316
361
 
362
+ it 'should add a after_validation filter' do
363
+ subject.after_validation { @foo = "first #{params[:id]}:#{params[:id].class}" }
364
+ subject.after_validation { @bar = 'second' }
365
+ subject.params do
366
+ requires :id, :type => Integer
367
+ end
368
+ subject.get '/' do
369
+ "#{@foo} #{@bar}"
370
+ end
371
+
372
+ get '/', :id => "32"
373
+ last_response.body.should eql 'first 32:Fixnum second'
374
+ end
375
+
317
376
  it 'should add a after filter' do
318
377
  m = double('after mock')
319
378
  subject.after { m.do_something! }
@@ -356,65 +415,67 @@ describe Grape::API do
356
415
  last_response.headers['Content-Type'].should eql 'application/json'
357
416
  end
358
417
  end
359
-
418
+
360
419
  context 'custom middleware' do
361
- class PhonyMiddleware
362
- def initialize(app, *args)
363
- @args = args
364
- @app = app
365
- @block = true if block_given?
366
- end
420
+ module ApiSpec
421
+ class PhonyMiddleware
422
+ def initialize(app, *args)
423
+ @args = args
424
+ @app = app
425
+ @block = true if block_given?
426
+ end
367
427
 
368
- def call(env)
369
- env['phony.args'] ||= []
370
- env['phony.args'] << @args
371
- env['phony.block'] = true if @block
372
- @app.call(env)
428
+ def call(env)
429
+ env['phony.args'] ||= []
430
+ env['phony.args'] << @args
431
+ env['phony.block'] = true if @block
432
+ @app.call(env)
433
+ end
373
434
  end
374
435
  end
375
436
 
376
437
  describe '.middleware' do
377
438
  it 'should include middleware arguments from settings' do
378
439
  settings = Grape::Util::HashStack.new
379
- settings.stub!(:stack).and_return([{:middleware => [[PhonyMiddleware, 'abc', 123]]}])
440
+ settings.stub!(:stack).and_return([{:middleware => [[ApiSpec::PhonyMiddleware, 'abc', 123]]}])
380
441
  subject.stub!(:settings).and_return(settings)
381
- subject.middleware.should eql [[PhonyMiddleware, 'abc', 123]]
442
+ subject.middleware.should eql [[ApiSpec::PhonyMiddleware, 'abc', 123]]
382
443
  end
383
444
 
384
445
  it 'should include all middleware from stacked settings' do
385
446
  settings = Grape::Util::HashStack.new
386
447
  settings.stub!(:stack).and_return [
387
- {:middleware => [[PhonyMiddleware, 123],[PhonyMiddleware, 'abc']]},
388
- {:middleware => [[PhonyMiddleware, 'foo']]}
448
+ {:middleware => [[ApiSpec::PhonyMiddleware, 123],[ApiSpec::PhonyMiddleware, 'abc']]},
449
+ {:middleware => [[ApiSpec::PhonyMiddleware, 'foo']]}
389
450
  ]
390
451
  subject.stub!(:settings).and_return(settings)
391
-
452
+
392
453
  subject.middleware.should eql [
393
- [PhonyMiddleware, 123],
394
- [PhonyMiddleware, 'abc'],
395
- [PhonyMiddleware, 'foo']
454
+ [ApiSpec::PhonyMiddleware, 123],
455
+ [ApiSpec::PhonyMiddleware, 'abc'],
456
+ [ApiSpec::PhonyMiddleware, 'foo']
396
457
  ]
397
458
  end
398
459
  end
399
460
 
400
461
  describe '.use' do
401
462
  it 'should add middleware' do
402
- subject.use PhonyMiddleware, 123
403
- subject.middleware.should eql [[PhonyMiddleware, 123]]
463
+ subject.use ApiSpec::PhonyMiddleware, 123
464
+ subject.middleware.should eql [[ApiSpec::PhonyMiddleware, 123]]
404
465
  end
405
466
 
406
467
  it 'should not show up outside the namespace' do
407
- subject.use PhonyMiddleware, 123
468
+ subject.use ApiSpec::PhonyMiddleware, 123
408
469
  subject.namespace :awesome do
409
- use PhonyMiddleware, 'abc'
410
- middleware.should == [[PhonyMiddleware, 123],[PhonyMiddleware, 'abc']]
470
+ use ApiSpec::PhonyMiddleware, 'abc'
471
+ middleware.should == [[ApiSpec::PhonyMiddleware, 123],[ApiSpec::PhonyMiddleware, 'abc']]
411
472
  end
412
473
 
413
- subject.middleware.should eql [[PhonyMiddleware, 123]]
474
+ subject.middleware.should eql [[ApiSpec::PhonyMiddleware, 123]]
414
475
  end
415
476
 
416
477
  it 'should actually call the middleware' do
417
- subject.use PhonyMiddleware, 'hello'
478
+ subject.use ApiSpec::PhonyMiddleware, 'hello'
418
479
  subject.get '/' do
419
480
  env['phony.args'].first.first
420
481
  end
@@ -425,13 +486,13 @@ describe Grape::API do
425
486
 
426
487
  it 'should add a block if one is given' do
427
488
  block = lambda{ }
428
- subject.use PhonyMiddleware, &block
429
- subject.middleware.should eql [[PhonyMiddleware, block]]
489
+ subject.use ApiSpec::PhonyMiddleware, &block
490
+ subject.middleware.should eql [[ApiSpec::PhonyMiddleware, block]]
430
491
  end
431
492
 
432
493
  it 'should use a block if one is given' do
433
494
  block = lambda{ }
434
- subject.use PhonyMiddleware, &block
495
+ subject.use ApiSpec::PhonyMiddleware, &block
435
496
  subject.get '/' do
436
497
  env['phony.block'].inspect
437
498
  end
@@ -442,7 +503,7 @@ describe Grape::API do
442
503
 
443
504
  it 'should not destroy the middleware settings on multiple runs' do
444
505
  block = lambda{ }
445
- subject.use PhonyMiddleware, &block
506
+ subject.use ApiSpec::PhonyMiddleware, &block
446
507
  subject.get '/' do
447
508
  env['phony.block'].inspect
448
509
  end
@@ -628,7 +689,7 @@ describe Grape::API do
628
689
  subject.get '/def' do
629
690
  'def'
630
691
  end
631
-
692
+
632
693
  get '/new/abc'
633
694
  last_response.status.should eql 404
634
695
  get '/legacy/abc'
@@ -667,6 +728,97 @@ describe Grape::API do
667
728
 
668
729
  lambda{ get '/unrescued' }.should raise_error
669
730
  end
731
+
732
+ it 'should not re-raise exceptions of type Grape::Exception::Base' do
733
+ class CustomError < Grape::Exceptions::Base; end
734
+ subject.get('/custom_exception'){ raise CustomError }
735
+
736
+ lambda{ get '/custom_exception' }.should_not raise_error
737
+ end
738
+
739
+ it 'should rescue custom grape exceptions' do
740
+ class CustomError < Grape::Exceptions::Base; end
741
+ subject.rescue_from CustomError do |e|
742
+ rack_response('New Error', e.status)
743
+ end
744
+ subject.get '/custom_error' do
745
+ raise CustomError, :status => 400, :message => 'Custom Error'
746
+ end
747
+
748
+ get '/custom_error'
749
+ last_response.status.should == 400
750
+ last_response.body.should == 'New Error'
751
+ end
752
+ end
753
+
754
+ describe ".rescue_from klass, block" do
755
+ it 'should rescue Exception' do
756
+ subject.rescue_from RuntimeError do |e|
757
+ rack_response("rescued from #{e.message}", 202)
758
+ end
759
+ subject.get '/exception' do
760
+ raise "rain!"
761
+ end
762
+ get '/exception'
763
+ last_response.status.should eql 202
764
+ last_response.body.should == 'rescued from rain!'
765
+ end
766
+ it 'should rescue an error via rescue_from :all' do
767
+ class ConnectionError < RuntimeError; end
768
+ subject.rescue_from :all do |e|
769
+ rack_response("rescued from #{e.class.name}", 500)
770
+ end
771
+ subject.get '/exception' do
772
+ raise ConnectionError
773
+ end
774
+ get '/exception'
775
+ last_response.status.should eql 500
776
+ last_response.body.should == 'rescued from ConnectionError'
777
+ end
778
+ it 'should rescue a specific error' do
779
+ class ConnectionError < RuntimeError; end
780
+ subject.rescue_from ConnectionError do |e|
781
+ rack_response("rescued from #{e.class.name}", 500)
782
+ end
783
+ subject.get '/exception' do
784
+ raise ConnectionError
785
+ end
786
+ get '/exception'
787
+ last_response.status.should eql 500
788
+ last_response.body.should == 'rescued from ConnectionError'
789
+ end
790
+ it 'should rescue multiple specific errors' do
791
+ class ConnectionError < RuntimeError; end
792
+ class DatabaseError < RuntimeError; end
793
+ subject.rescue_from ConnectionError do |e|
794
+ rack_response("rescued from #{e.class.name}", 500)
795
+ end
796
+ subject.rescue_from DatabaseError do |e|
797
+ rack_response("rescued from #{e.class.name}", 500)
798
+ end
799
+ subject.get '/connection' do
800
+ raise ConnectionError
801
+ end
802
+ subject.get '/database' do
803
+ raise DatabaseError
804
+ end
805
+ get '/connection'
806
+ last_response.status.should eql 500
807
+ last_response.body.should == 'rescued from ConnectionError'
808
+ get '/database'
809
+ last_response.status.should eql 500
810
+ last_response.body.should == 'rescued from DatabaseError'
811
+ end
812
+ it 'should not rescue a different error' do
813
+ class CommunicationError < RuntimeError; end
814
+ subject.rescue_from RuntimeError do |e|
815
+ rack_response("rescued from #{e.class.name}", 500)
816
+ end
817
+ subject.get '/uncaught' do
818
+ raise CommunicationError
819
+ end
820
+ lambda { get '/uncaught' }.should raise_error(CommunicationError)
821
+ end
670
822
  end
671
823
 
672
824
  describe ".error_format" do
@@ -766,19 +918,7 @@ describe Grape::API do
766
918
  last_response.status.should eql 403
767
919
  end
768
920
  end
769
-
770
- context "muti_xml" do
771
- it "doesn't parse yaml" do
772
- subject.put :yaml do
773
- params[:tag]
774
- end
775
921
 
776
- expect {
777
- put '/yaml', '<tag type="symbol">a123</tag>', "CONTENT_TYPE" => "application/xml"
778
- }.to raise_error(MultiXml::DisallowedTypeError)
779
- end
780
- end
781
-
782
922
  context "routes" do
783
923
  describe "empty api structure" do
784
924
  it "returns an empty array of routes" do
@@ -816,6 +956,9 @@ describe Grape::API do
816
956
  end
817
957
  end
818
958
  end
959
+ it "should return the latest version set" do
960
+ subject.version.should == 'v2'
961
+ end
819
962
  it "should return versions" do
820
963
  subject.versions.should == [ 'v1', 'v2' ]
821
964
  end
@@ -933,99 +1076,99 @@ describe Grape::API do
933
1076
  { :description => "Reverses a string.", :params => { "s" => { :desc => "string to reverse", :type => "string" } } }
934
1077
  ]
935
1078
  end
936
- it "should not symbolize params" do
937
- subject.desc "Reverses a string.", { :params =>
938
- { "s" => { :desc => "string to reverse", :type => "string" }}
939
- }
940
- subject.get "reverse/:s" do
941
- params[:s].reverse
1079
+ it "should merge the parameters of the namespace with the parameters of the method" do
1080
+ subject.desc "namespace"
1081
+ subject.params do
1082
+ requires :ns_param, :desc => "namespace parameter"
1083
+ end
1084
+ subject.namespace "ns" do
1085
+ desc "method"
1086
+ params do
1087
+ optional :method_param, :desc => "method parameter"
1088
+ end
1089
+ get "method" do ; end
942
1090
  end
943
1091
  subject.routes.map { |route|
944
1092
  { :description => route.route_description, :params => route.route_params }
945
1093
  }.should eq [
946
- { :description => "Reverses a string.", :params => { "s" => { :desc => "string to reverse", :type => "string" } } }
1094
+ { :description => "method", :params => { "ns_param" => { :required => true, :desc => "namespace parameter", :full_name=>"ns_param" }, "method_param" => { :required => false, :desc => "method parameter", :full_name=>"method_param" } } }
947
1095
  ]
948
1096
  end
949
- end
950
-
951
- describe ".rescue_from klass, block" do
952
- it 'should rescue Exception' do
953
- subject.rescue_from RuntimeError do |e|
954
- rack_response("rescued from #{e.message}", 202)
955
- end
956
- subject.get '/exception' do
957
- raise "rain!"
958
- end
959
- get '/exception'
960
- last_response.status.should eql 202
961
- last_response.body.should == 'rescued from rain!'
962
- end
963
- it 'should rescue an error via rescue_from :all' do
964
- class ConnectionError < RuntimeError; end
965
- subject.rescue_from :all do |e|
966
- rack_response("rescued from #{e.class.name}", 500)
967
- end
968
- subject.get '/exception' do
969
- raise ConnectionError
1097
+ it "should merge the parameters of nested namespaces" do
1098
+ subject.desc "ns1"
1099
+ subject.params do
1100
+ optional :ns_param, :desc => "ns param 1"
1101
+ requires :ns1_param, :desc => "ns1 param"
1102
+ end
1103
+ subject.namespace "ns1" do
1104
+ desc "ns2"
1105
+ params do
1106
+ requires :ns_param, :desc => "ns param 2"
1107
+ requires :ns2_param, :desc => "ns2 param"
1108
+ end
1109
+ namespace "ns2" do
1110
+ desc "method"
1111
+ params do
1112
+ optional :method_param, :desc => "method param"
1113
+ end
1114
+ get "method" do ; end
1115
+ end
970
1116
  end
971
- get '/exception'
972
- last_response.status.should eql 500
973
- last_response.body.should == 'rescued from ConnectionError'
1117
+ subject.routes.map { |route|
1118
+ { :description => route.route_description, :params => route.route_params }
1119
+ }.should eq [
1120
+ { :description => "method", :params => { "ns_param" => { :required => true, :desc => "ns param 2", :full_name=>"ns_param" }, "ns1_param" => { :required => true, :desc => "ns1 param", :full_name=>"ns1_param" }, "ns2_param" => { :required => true, :desc => "ns2 param", :full_name=>"ns2_param" }, "method_param" => { :required => false, :desc => "method param", :full_name=>"method_param" } } }
1121
+ ]
974
1122
  end
975
- it 'should rescue a specific error' do
976
- class ConnectionError < RuntimeError; end
977
- subject.rescue_from ConnectionError do |e|
978
- rack_response("rescued from #{e.class.name}", 500)
979
- end
980
- subject.get '/exception' do
981
- raise ConnectionError
1123
+ it "should provide a full_name for parameters in nested groups" do
1124
+ subject.desc "nesting"
1125
+ subject.params do
1126
+ requires :root_param, :desc => "root param"
1127
+ group :nested do
1128
+ requires :nested_param, :desc => "nested param"
1129
+ end
982
1130
  end
983
- get '/exception'
984
- last_response.status.should eql 500
985
- last_response.body.should == 'rescued from ConnectionError'
1131
+ subject.get "method" do ; end
1132
+ subject.routes.map { |route|
1133
+ { :description => route.route_description, :params => route.route_params }
1134
+ }.should eq [
1135
+ { :description => "nesting", :params => { "root_param" => { :required => true, :desc => "root param", :full_name=>"root_param" }, "nested_param" => { :required => true, :desc => "nested param", :full_name=>"nested[nested_param]" } } }
1136
+ ]
986
1137
  end
987
- it 'should rescue multiple specific errors' do
988
- class ConnectionError < RuntimeError; end
989
- class DatabaseError < RuntimeError; end
990
- subject.rescue_from ConnectionError do |e|
991
- rack_response("rescued from #{e.class.name}", 500)
992
- end
993
- subject.rescue_from DatabaseError do |e|
994
- rack_response("rescued from #{e.class.name}", 500)
995
- end
996
- subject.get '/connection' do
997
- raise ConnectionError
1138
+ it "should parse parameters when no description is given" do
1139
+ subject.params do
1140
+ requires :one_param, :desc => "one param"
998
1141
  end
999
- subject.get '/database' do
1000
- raise DatabaseError
1001
- end
1002
- get '/connection'
1003
- last_response.status.should eql 500
1004
- last_response.body.should == 'rescued from ConnectionError'
1005
- get '/database'
1006
- last_response.status.should eql 500
1007
- last_response.body.should == 'rescued from DatabaseError'
1142
+ subject.get "method" do ; end
1143
+ subject.routes.map { |route|
1144
+ { :description => route.route_description, :params => route.route_params }
1145
+ }.should eq [
1146
+ { :description => nil, :params => { "one_param" => { :required => true, :desc => "one param", :full_name=>"one_param" } } }
1147
+ ]
1008
1148
  end
1009
- it 'should not rescue a different error' do
1010
- class CommunicationError < RuntimeError; end
1011
- subject.rescue_from RuntimeError do |e|
1012
- rack_response("rescued from #{e.class.name}", 500)
1013
- end
1014
- subject.get '/uncaught' do
1015
- raise CommunicationError
1149
+ it "should not symbolize params" do
1150
+ subject.desc "Reverses a string.", { :params =>
1151
+ { "s" => { :desc => "string to reverse", :type => "string" }}
1152
+ }
1153
+ subject.get "reverse/:s" do
1154
+ params[:s].reverse
1016
1155
  end
1017
- lambda { get '/uncaught' }.should raise_error(CommunicationError)
1156
+ subject.routes.map { |route|
1157
+ { :description => route.route_description, :params => route.route_params }
1158
+ }.should eq [
1159
+ { :description => "Reverses a string.", :params => { "s" => { :desc => "string to reverse", :type => "string" } } }
1160
+ ]
1018
1161
  end
1019
1162
  end
1020
1163
 
1021
1164
  describe '.mount' do
1022
1165
  let(:mounted_app){ lambda{|env| [200, {}, ["MOUNTED"]]} }
1023
-
1166
+
1024
1167
  context 'with a bare rack app' do
1025
1168
  before do
1026
1169
  subject.mount mounted_app => '/mounty'
1027
1170
  end
1028
-
1171
+
1029
1172
  it 'should make a bare Rack app available at the endpoint' do
1030
1173
  get '/mounty'
1031
1174
  last_response.body.should == 'MOUNTED'
@@ -1073,6 +1216,21 @@ describe Grape::API do
1073
1216
  get '/v1/cool/awesome'
1074
1217
  last_response.body.should == 'yo'
1075
1218
  end
1219
+
1220
+ it 'should inherit rescues even when some defined by mounted' do
1221
+ subject.rescue_from :all do |e|
1222
+ rack_response("rescued from #{e.message}", 202)
1223
+ end
1224
+ subject.namespace :mounted do
1225
+ app = Class.new(Grape::API)
1226
+ app.rescue_from ArgumentError
1227
+ app.get('/fail') { raise "doh!" }
1228
+ mount app
1229
+ end
1230
+ get '/mounted/fail'
1231
+ last_response.status.should eql 202
1232
+ last_response.body.should == 'rescued from doh!'
1233
+ end
1076
1234
  end
1077
1235
  end
1078
1236
 
@@ -1099,7 +1257,7 @@ describe Grape::API do
1099
1257
  subject.instance.should be_nil
1100
1258
  end
1101
1259
  end
1102
-
1260
+
1103
1261
  describe ".route" do
1104
1262
  context "plain" do
1105
1263
  before(:each) do