Skip to content

Commit 5117e20

Browse files
committed
Merge pull request ruby-grape#221 from adamgotterer/validation_exceptions
Added custom exceptions to Grape. Updated validation errors to use ValidationError.
2 parents 740b63a + 4633ad9 commit 5117e20

File tree

10 files changed

+83
-8
lines changed

10 files changed

+83
-8
lines changed

CHANGELOG.markdown

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Next Release
22
============
33

4+
* [#201](https://github.com/intridea/grape/pull/201): Added custom exceptions to Grape. Updated validations to use ValidationError that can be rescued. - [@adamgotterer](https://github.com/adamgotterer).
45
* [#211](https://github.com/intridea/grape/pull/211): Updates to validation and coercion: Fix #211 and force order of operations for presence and coercion - [@adamgotterer](https://github.com/adamgotterer).
56
* [#210](https://github.com/intridea/grape/pull/210): Fix: `Endpoint#body_params` causing undefined method 'size' - [@adamgotterer](https://github.com/adamgotterer).
67
* [#201](https://github.com/intridea/grape/pull/201): Rewritten `params` DSL, including support for coercion and validations - [@schmurfy](https://github.com/schmurfy).

lib/grape.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ module Grape
1111
autoload :Cookies, 'grape/cookies'
1212
autoload :Validations, 'grape/validations'
1313

14+
module Exceptions
15+
autoload :Base, 'grape/exceptions/base'
16+
end
17+
autoload :ValidationError, 'grape/exceptions/validation_error'
18+
1419
module Middleware
1520
autoload :Base, 'grape/middleware/base'
1621
autoload :Prefixer, 'grape/middleware/prefixer'

lib/grape/exceptions/base.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module Grape
2+
module Exceptions
3+
class Base < StandardError
4+
attr_reader :status, :message, :headers
5+
6+
def initialize(args = {})
7+
@status = args[:status] || nil
8+
@message = args[:message] || nil
9+
@headers = args[:headers] || nil
10+
end
11+
12+
def [](index)
13+
self.send(index)
14+
end
15+
end
16+
end
17+
end
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
require 'grape/exceptions/base'
2+
3+
class ValidationError < Grape::Exceptions::Base
4+
end

lib/grape/middleware/error.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,19 @@ def call!(env)
4444
return @app.call(@env)
4545
})
4646
rescue Exception => e
47-
raise unless options[:rescue_all] || (options[:rescued_errors] || []).include?(e.class)
48-
handler = options[:rescue_handlers][e.class] || options[:rescue_handlers][:all]
47+
is_rescuable = rescuable?(e.class)
48+
if e.is_a?(Grape::Exceptions::Base) && !is_rescuable
49+
handler = lambda { error_response(e) }
50+
else
51+
raise unless is_rescuable
52+
handler = options[:rescue_handlers][e.class] || options[:rescue_handlers][:all]
53+
end
4954
handler.nil? ? handle_error(e) : self.instance_exec(e, &handler)
5055
end
51-
56+
end
57+
58+
def rescuable?(klass)
59+
options[:rescue_all] || (options[:rescued_errors] || []).include?(klass)
5260
end
5361

5462
def handle_error(e)

lib/grape/validations/coerce.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def validate_param!(attr_name, params)
1212
if valid_type?(new_value)
1313
params[attr_name] = new_value
1414
else
15-
throw :error, :status => 400, :message => "invalid parameter: #{attr_name}"
15+
raise ValidationError, :status => 400, :message => "invalid parameter: #{attr_name}"
1616
end
1717
end
1818

lib/grape/validations/presence.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
module Grape
22
module Validations
3-
43
class PresenceValidator < Validator
54
def validate_param!(attr_name, params)
65
unless params.has_key?(attr_name)
7-
throw :error, :status => 400, :message => "missing parameter: #{attr_name}"
6+
raise ValidationError, :status => 400, :message => "missing parameter: #{attr_name}"
87
end
98
end
109
end
11-
1210
end
1311
end

lib/grape/validations/regexp.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Validations
44
class RegexpValidator < SingleOptionValidator
55
def validate_param!(attr_name, params)
66
if params[attr_name] && !( params[attr_name].to_s =~ @option )
7-
throw :error, :status => 400, :message => "invalid parameter: #{attr_name}"
7+
raise ValidationError, :status => 400, :message => "invalid parameter: #{attr_name}"
88
end
99
end
1010
end

spec/grape/api_spec.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,27 @@ def three
669669

670670
lambda{ get '/unrescued' }.should raise_error
671671
end
672+
673+
it 'should not re-raise exceptions of type Grape::Exception::Base' do
674+
class CustomError < Grape::Exceptions::Base; end
675+
subject.get('/custom_exception'){ raise CustomError }
676+
677+
lambda{ get '/custom_exception' }.should_not raise_error
678+
end
679+
680+
it 'should rescue custom grape exceptions' do
681+
class CustomError < Grape::Exceptions::Base; end
682+
subject.rescue_from CustomError do |e|
683+
rack_response('New Error', e.status)
684+
end
685+
subject.get '/custom_error' do
686+
raise CustomError, :status => 400, :message => 'Custom Error'
687+
end
688+
689+
get '/custom_error'
690+
last_response.status.should == 400
691+
last_response.body.should == 'New Error'
692+
end
672693
end
673694

674695
describe ".rescue_from klass, block" do

spec/grape/middleware/exception_spec.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ def call(env)
3434
end
3535
end
3636
end
37+
38+
# raises a custom error
39+
class CustomError < Grape::Exceptions::Base; end
40+
class CustomErrorApp
41+
class << self
42+
def call(env)
43+
raise CustomError, :status => 400, :message => 'failed validation'
44+
end
45+
end
46+
end
3747

3848
def app
3949
@app
@@ -116,6 +126,17 @@ def app
116126
get '/'
117127
last_response.status.should == 401
118128
end
129+
130+
it 'should respond to custom Grape exceptions appropriately' do
131+
@app ||= Rack::Builder.app do
132+
use Grape::Middleware::Error, :rescue_all => false
133+
run CustomErrorApp
134+
end
135+
136+
get '/'
137+
last_response.status.should == 400
138+
last_response.body.should == 'failed validation'
139+
end
119140

120141
end
121142
end

0 commit comments

Comments
 (0)