Skip to content

Commit 740b63a

Browse files
committed
Merge rescue settings in mounted apps. Issue ruby-grape#214.
1 parent fcde90e commit 740b63a

File tree

2 files changed

+111
-90
lines changed

2 files changed

+111
-90
lines changed

lib/grape/endpoint.rb

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,12 @@ def header(key = nil, val = nil)
197197
@header
198198
end
199199
end
200-
200+
201201
# Set response content-type
202202
def content_type(val)
203203
header('Content-Type', val)
204204
end
205-
205+
206206
# Set or get a cookie
207207
#
208208
# @example
@@ -268,7 +268,7 @@ def present(object, options = {})
268268
representation = { root => representation } if root
269269
body representation
270270
end
271-
271+
272272
# Returns route information for the current request.
273273
#
274274
# @example
@@ -290,16 +290,16 @@ def run(env)
290290

291291
self.extend helpers
292292
cookies.read(@request)
293-
293+
294294
Array(settings[:validations]).each do |validator|
295295
validator.validate!(params)
296296
end
297-
297+
298298
run_filters befores
299299
response_text = instance_eval &self.block
300300
run_filters afters
301301
cookies.write(header)
302-
302+
303303
[status, header, [body || response_text]]
304304
end
305305

@@ -310,10 +310,10 @@ def build_middleware
310310
b.use Grape::Middleware::Error,
311311
:default_status => settings[:default_error_status] || 403,
312312
:rescue_all => settings[:rescue_all],
313-
:rescued_errors => settings[:rescued_errors],
313+
:rescued_errors => aggregate_setting(:rescued_errors),
314314
:format => settings[:error_format] || :txt,
315315
:rescue_options => settings[:rescue_options],
316-
:rescue_handlers => settings[:rescue_handlers] || {}
316+
:rescue_handlers => merged_setting(:rescue_handlers)
317317

318318
b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
319319
b.use Rack::Auth::Digest::MD5, settings[:auth][:realm], settings[:auth][:opaque], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_digest
@@ -325,7 +325,7 @@ def build_middleware
325325
:version_options => settings[:version_options]
326326
}
327327
end
328-
328+
329329
b.use Grape::Middleware::Formatter,
330330
:format => settings[:format],
331331
:default_format => settings[:default_format] || :txt,
@@ -356,6 +356,12 @@ def aggregate_setting(key)
356356
end
357357
end
358358

359+
def merged_setting(key)
360+
settings.stack.inject({}) do |merged, frame|
361+
merged.merge(frame[key] || {})
362+
end
363+
end
364+
359365
def run_filters(filters)
360366
(filters || []).each do |filter|
361367
instance_eval &filter

spec/grape/api_spec.rb

Lines changed: 96 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def app; subject end
6868
end
6969

7070
it 'should route if any media type is allowed' do
71-
71+
7272
end
7373
end
7474

@@ -133,7 +133,7 @@ def app; subject end
133133
get '/members/23'
134134
last_response.body.should == "23"
135135
end
136-
136+
137137
it 'should be callable with nil just to push onto the stack' do
138138
subject.namespace do
139139
version 'v2', :using => :path
@@ -146,7 +146,7 @@ def app; subject end
146146
get '/hello'
147147
last_response.body.should == "outer"
148148
end
149-
149+
150150
%w(group resource resources segment).each do |als|
151151
it "`.#{als}` should be an alias" do
152152
subject.send(als, :awesome) do
@@ -191,7 +191,7 @@ def app; subject end
191191
RSpec::Mocks::Mock.new(:to_json => 'abc', :to_txt => 'def')
192192
end
193193
end
194-
194+
195195
it "should allow .json" do
196196
get '/abc.json'
197197
last_response.status.should == 200
@@ -245,7 +245,7 @@ def app; subject end
245245
subject.route([:get, :post], '/:id/first') do
246246
"first"
247247
end
248-
248+
249249
subject.route([:get, :post], '/:id') do
250250
"ola"
251251
end
@@ -356,7 +356,7 @@ def app; subject end
356356
last_response.headers['Content-Type'].should eql 'application/json'
357357
end
358358
end
359-
359+
360360
context 'custom middleware' do
361361
module ApiSpec
362362
class PhonyMiddleware
@@ -390,7 +390,7 @@ def call(env)
390390
{:middleware => [[ApiSpec::PhonyMiddleware, 'foo']]}
391391
]
392392
subject.stub!(:settings).and_return(settings)
393-
393+
394394
subject.middleware.should eql [
395395
[ApiSpec::PhonyMiddleware, 123],
396396
[ApiSpec::PhonyMiddleware, 'abc'],
@@ -630,7 +630,7 @@ def three
630630
subject.get '/def' do
631631
'def'
632632
end
633-
633+
634634
get '/new/abc'
635635
last_response.status.should eql 404
636636
get '/legacy/abc'
@@ -671,6 +671,76 @@ def three
671671
end
672672
end
673673

674+
describe ".rescue_from klass, block" do
675+
it 'should rescue Exception' do
676+
subject.rescue_from RuntimeError do |e|
677+
rack_response("rescued from #{e.message}", 202)
678+
end
679+
subject.get '/exception' do
680+
raise "rain!"
681+
end
682+
get '/exception'
683+
last_response.status.should eql 202
684+
last_response.body.should == 'rescued from rain!'
685+
end
686+
it 'should rescue an error via rescue_from :all' do
687+
class ConnectionError < RuntimeError; end
688+
subject.rescue_from :all do |e|
689+
rack_response("rescued from #{e.class.name}", 500)
690+
end
691+
subject.get '/exception' do
692+
raise ConnectionError
693+
end
694+
get '/exception'
695+
last_response.status.should eql 500
696+
last_response.body.should == 'rescued from ConnectionError'
697+
end
698+
it 'should rescue a specific error' do
699+
class ConnectionError < RuntimeError; end
700+
subject.rescue_from ConnectionError do |e|
701+
rack_response("rescued from #{e.class.name}", 500)
702+
end
703+
subject.get '/exception' do
704+
raise ConnectionError
705+
end
706+
get '/exception'
707+
last_response.status.should eql 500
708+
last_response.body.should == 'rescued from ConnectionError'
709+
end
710+
it 'should rescue multiple specific errors' do
711+
class ConnectionError < RuntimeError; end
712+
class DatabaseError < RuntimeError; end
713+
subject.rescue_from ConnectionError do |e|
714+
rack_response("rescued from #{e.class.name}", 500)
715+
end
716+
subject.rescue_from DatabaseError do |e|
717+
rack_response("rescued from #{e.class.name}", 500)
718+
end
719+
subject.get '/connection' do
720+
raise ConnectionError
721+
end
722+
subject.get '/database' do
723+
raise DatabaseError
724+
end
725+
get '/connection'
726+
last_response.status.should eql 500
727+
last_response.body.should == 'rescued from ConnectionError'
728+
get '/database'
729+
last_response.status.should eql 500
730+
last_response.body.should == 'rescued from DatabaseError'
731+
end
732+
it 'should not rescue a different error' do
733+
class CommunicationError < RuntimeError; end
734+
subject.rescue_from RuntimeError do |e|
735+
rack_response("rescued from #{e.class.name}", 500)
736+
end
737+
subject.get '/uncaught' do
738+
raise CommunicationError
739+
end
740+
lambda { get '/uncaught' }.should raise_error(CommunicationError)
741+
end
742+
end
743+
674744
describe ".error_format" do
675745
it 'should rescue all errors and return :txt' do
676746
subject.rescue_from :all
@@ -981,85 +1051,15 @@ def three
9811051
]
9821052
end
9831053
end
984-
985-
describe ".rescue_from klass, block" do
986-
it 'should rescue Exception' do
987-
subject.rescue_from RuntimeError do |e|
988-
rack_response("rescued from #{e.message}", 202)
989-
end
990-
subject.get '/exception' do
991-
raise "rain!"
992-
end
993-
get '/exception'
994-
last_response.status.should eql 202
995-
last_response.body.should == 'rescued from rain!'
996-
end
997-
it 'should rescue an error via rescue_from :all' do
998-
class ConnectionError < RuntimeError; end
999-
subject.rescue_from :all do |e|
1000-
rack_response("rescued from #{e.class.name}", 500)
1001-
end
1002-
subject.get '/exception' do
1003-
raise ConnectionError
1004-
end
1005-
get '/exception'
1006-
last_response.status.should eql 500
1007-
last_response.body.should == 'rescued from ConnectionError'
1008-
end
1009-
it 'should rescue a specific error' do
1010-
class ConnectionError < RuntimeError; end
1011-
subject.rescue_from ConnectionError do |e|
1012-
rack_response("rescued from #{e.class.name}", 500)
1013-
end
1014-
subject.get '/exception' do
1015-
raise ConnectionError
1016-
end
1017-
get '/exception'
1018-
last_response.status.should eql 500
1019-
last_response.body.should == 'rescued from ConnectionError'
1020-
end
1021-
it 'should rescue multiple specific errors' do
1022-
class ConnectionError < RuntimeError; end
1023-
class DatabaseError < RuntimeError; end
1024-
subject.rescue_from ConnectionError do |e|
1025-
rack_response("rescued from #{e.class.name}", 500)
1026-
end
1027-
subject.rescue_from DatabaseError do |e|
1028-
rack_response("rescued from #{e.class.name}", 500)
1029-
end
1030-
subject.get '/connection' do
1031-
raise ConnectionError
1032-
end
1033-
subject.get '/database' do
1034-
raise DatabaseError
1035-
end
1036-
get '/connection'
1037-
last_response.status.should eql 500
1038-
last_response.body.should == 'rescued from ConnectionError'
1039-
get '/database'
1040-
last_response.status.should eql 500
1041-
last_response.body.should == 'rescued from DatabaseError'
1042-
end
1043-
it 'should not rescue a different error' do
1044-
class CommunicationError < RuntimeError; end
1045-
subject.rescue_from RuntimeError do |e|
1046-
rack_response("rescued from #{e.class.name}", 500)
1047-
end
1048-
subject.get '/uncaught' do
1049-
raise CommunicationError
1050-
end
1051-
lambda { get '/uncaught' }.should raise_error(CommunicationError)
1052-
end
1053-
end
10541054

10551055
describe '.mount' do
10561056
let(:mounted_app){ lambda{|env| [200, {}, ["MOUNTED"]]} }
1057-
1057+
10581058
context 'with a bare rack app' do
10591059
before do
10601060
subject.mount mounted_app => '/mounty'
10611061
end
1062-
1062+
10631063
it 'should make a bare Rack app available at the endpoint' do
10641064
get '/mounty'
10651065
last_response.body.should == 'MOUNTED'
@@ -1107,6 +1107,21 @@ class CommunicationError < RuntimeError; end
11071107
get '/v1/cool/awesome'
11081108
last_response.body.should == 'yo'
11091109
end
1110+
1111+
it 'should inherit rescues even when some defined by mounted' do
1112+
subject.rescue_from :all do |e|
1113+
rack_response("rescued from #{e.message}", 202)
1114+
end
1115+
subject.namespace :mounted do
1116+
app = Class.new(Grape::API)
1117+
app.rescue_from ArgumentError
1118+
app.get('/fail') { raise "doh!" }
1119+
mount app
1120+
end
1121+
get '/mounted/fail'
1122+
last_response.status.should eql 202
1123+
last_response.body.should == 'rescued from doh!'
1124+
end
11101125
end
11111126
end
11121127

@@ -1133,7 +1148,7 @@ class CommunicationError < RuntimeError; end
11331148
subject.instance.should be_nil
11341149
end
11351150
end
1136-
1151+
11371152
describe ".route" do
11381153
context "plain" do
11391154
before(:each) do

0 commit comments

Comments
 (0)