grape 0.13.0 → 0.14.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.
- checksums.yaml +4 -4
- data/Appraisals +9 -4
- data/CHANGELOG.md +28 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +166 -0
- data/README.md +305 -163
- data/Rakefile +30 -33
- data/UPGRADING.md +31 -0
- data/benchmark/simple.rb +27 -0
- data/gemfiles/rack_1.5.2.gemfile +13 -0
- data/gemfiles/rails_3.gemfile +2 -2
- data/gemfiles/rails_4.gemfile +1 -2
- data/grape.gemspec +5 -4
- data/lib/grape.rb +9 -5
- data/lib/grape/dsl/configuration.rb +5 -2
- data/lib/grape/dsl/helpers.rb +8 -3
- data/lib/grape/dsl/inside_route.rb +67 -44
- data/lib/grape/dsl/parameters.rb +21 -12
- data/lib/grape/dsl/request_response.rb +1 -1
- data/lib/grape/dsl/routing.rb +3 -4
- data/lib/grape/endpoint.rb +63 -28
- data/lib/grape/error_formatter/base.rb +6 -6
- data/lib/grape/exceptions/base.rb +5 -5
- data/lib/grape/exceptions/invalid_version_header.rb +10 -0
- data/lib/grape/formatter/serializable_hash.rb +3 -2
- data/lib/grape/locale/en.yml +4 -1
- data/lib/grape/middleware/auth/base.rb +2 -2
- data/lib/grape/middleware/auth/dsl.rb +1 -1
- data/lib/grape/middleware/auth/strategies.rb +1 -1
- data/lib/grape/middleware/base.rb +7 -4
- data/lib/grape/middleware/error.rb +3 -2
- data/lib/grape/middleware/filter.rb +1 -1
- data/lib/grape/middleware/formatter.rb +47 -44
- data/lib/grape/middleware/globals.rb +3 -3
- data/lib/grape/middleware/versioner/accept_version_header.rb +5 -7
- data/lib/grape/middleware/versioner/header.rb +113 -50
- data/lib/grape/middleware/versioner/param.rb +5 -8
- data/lib/grape/middleware/versioner/parse_media_type_patch.rb +20 -0
- data/lib/grape/middleware/versioner/path.rb +3 -6
- data/lib/grape/path.rb +3 -3
- data/lib/grape/request.rb +40 -0
- data/lib/grape/util/content_types.rb +9 -9
- data/lib/grape/util/env.rb +22 -0
- data/lib/grape/util/strict_hash_configuration.rb +2 -1
- data/lib/grape/validations/attributes_iterator.rb +8 -3
- data/lib/grape/validations/params_scope.rb +83 -15
- data/lib/grape/validations/types.rb +144 -0
- data/lib/grape/validations/types/build_coercer.rb +53 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +183 -0
- data/lib/grape/validations/types/file.rb +28 -0
- data/lib/grape/validations/types/json.rb +65 -0
- data/lib/grape/validations/types/multiple_type_coercer.rb +76 -0
- data/lib/grape/validations/types/variant_collection_coercer.rb +59 -0
- data/lib/grape/validations/types/virtus_collection_patch.rb +16 -0
- data/lib/grape/validations/validators/all_or_none.rb +1 -1
- data/lib/grape/validations/validators/allow_blank.rb +3 -3
- data/lib/grape/validations/validators/base.rb +7 -0
- data/lib/grape/validations/validators/coerce.rb +31 -42
- data/lib/grape/validations/validators/presence.rb +2 -3
- data/lib/grape/validations/validators/regexp.rb +2 -4
- data/lib/grape/validations/validators/values.rb +3 -3
- data/lib/grape/version.rb +1 -1
- data/pkg/grape-0.13.0.gem +0 -0
- data/spec/grape/api/custom_validations_spec.rb +5 -4
- data/spec/grape/api/deeply_included_options_spec.rb +7 -7
- data/spec/grape/api/nested_helpers_spec.rb +4 -2
- data/spec/grape/api/shared_helpers_spec.rb +8 -8
- data/spec/grape/api_spec.rb +88 -54
- data/spec/grape/dsl/configuration_spec.rb +13 -0
- data/spec/grape/dsl/helpers_spec.rb +16 -2
- data/spec/grape/dsl/inside_route_spec.rb +3 -2
- data/spec/grape/dsl/parameters_spec.rb +0 -6
- data/spec/grape/dsl/routing_spec.rb +1 -1
- data/spec/grape/endpoint_spec.rb +61 -20
- data/spec/grape/entity_spec.rb +10 -8
- data/spec/grape/exceptions/invalid_accept_header_spec.rb +1 -15
- data/spec/grape/integration/rack_spec.rb +3 -2
- data/spec/grape/middleware/base_spec.rb +7 -5
- data/spec/grape/middleware/error_spec.rb +16 -15
- data/spec/grape/middleware/exception_spec.rb +45 -43
- data/spec/grape/middleware/formatter_spec.rb +34 -0
- data/spec/grape/middleware/versioner/header_spec.rb +79 -47
- data/spec/grape/path_spec.rb +10 -10
- data/spec/grape/presenters/presenter_spec.rb +2 -2
- data/spec/grape/request_spec.rb +100 -0
- data/spec/grape/validations/params_scope_spec.rb +11 -9
- data/spec/grape/validations/types_spec.rb +95 -0
- data/spec/grape/validations/validators/coerce_spec.rb +335 -2
- data/spec/grape/validations/validators/values_spec.rb +15 -15
- data/spec/grape/validations_spec.rb +53 -24
- data/spec/shared/versioning_examples.rb +2 -2
- data/spec/spec_helper.rb +0 -1
- data/spec/support/versioned_helpers.rb +2 -2
- metadata +51 -13
- data/.gitignore +0 -46
- data/.rspec +0 -2
- data/.rubocop.yml +0 -7
- data/.rubocop_todo.yml +0 -84
- data/.travis.yml +0 -20
- data/.yardopts +0 -2
- data/lib/grape/http/request.rb +0 -35
- data/lib/grape/util/parameter_types.rb +0 -58
- data/spec/grape/util/parameter_types_spec.rb +0 -54
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'virtus/attribute/collection'
|
2
|
+
|
3
|
+
# See https://github.com/solnic/virtus/pull/343
|
4
|
+
# This monkey-patch fixes type validation for collections,
|
5
|
+
# ensuring that type assertions are applied to collection
|
6
|
+
# members.
|
7
|
+
#
|
8
|
+
# This patch duplicates the code in the above pull request.
|
9
|
+
# Once the request, or equivalent functionality, has been
|
10
|
+
# published into the +virtus+ gem this file should be deleted.
|
11
|
+
Virtus::Attribute::Collection.class_eval do
|
12
|
+
# @api public
|
13
|
+
def value_coerced?(value)
|
14
|
+
super && value.all? { |item| member_type.value_coerced? item }
|
15
|
+
end
|
16
|
+
end
|
@@ -13,7 +13,7 @@ module Grape
|
|
13
13
|
private
|
14
14
|
|
15
15
|
def only_subset_present
|
16
|
-
scoped_params.any? { |resource_params| keys_in_common(resource_params).length > 0 && keys_in_common(resource_params).length < attrs.length
|
16
|
+
scoped_params.any? { |resource_params| keys_in_common(resource_params).length > 0 && keys_in_common(resource_params).length < attrs.length }
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
@@ -21,9 +21,9 @@ module Grape
|
|
21
21
|
|
22
22
|
return unless should_validate
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
return if value == false || value.present?
|
25
|
+
|
26
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :blank
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
@@ -3,6 +3,13 @@ module Grape
|
|
3
3
|
class Base
|
4
4
|
attr_reader :attrs
|
5
5
|
|
6
|
+
# Creates a new Validator from options specified
|
7
|
+
# by a +requires+ or +optional+ directive during
|
8
|
+
# parameter definition.
|
9
|
+
# @param attrs [Array] names of attributes to which the Validator applies
|
10
|
+
# @param options [Object] implementation-dependent Validator options
|
11
|
+
# @param required [Boolean] attribute(s) are required or optional
|
12
|
+
# @param scope [ParamsScope] parent scope for this Validator
|
6
13
|
def initialize(attrs, options, required, scope)
|
7
14
|
@attrs = Array(attrs)
|
8
15
|
@option = options
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Grape
|
2
2
|
class API
|
3
|
-
Boolean = Virtus::Attribute::Boolean
|
3
|
+
Boolean = Virtus::Attribute::Boolean
|
4
4
|
end
|
5
5
|
|
6
6
|
module Validations
|
7
7
|
class CoerceValidator < Base
|
8
8
|
def validate_param!(attr_name, params)
|
9
9
|
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :coerce unless params.is_a? Hash
|
10
|
-
new_value = coerce_value(
|
10
|
+
new_value = coerce_value(params[attr_name])
|
11
11
|
if valid_type?(new_value)
|
12
12
|
params[attr_name] = new_value
|
13
13
|
else
|
@@ -15,61 +15,50 @@ module Grape
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
class InvalidValue; end
|
19
|
-
|
20
18
|
private
|
21
19
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
20
|
+
def valid_type?(val)
|
21
|
+
# Special value to denote coercion failure
|
22
|
+
return false if val.instance_of?(Types::InvalidValue)
|
27
23
|
|
28
|
-
|
29
|
-
# allow nil, to ignore when a parameter is absent
|
24
|
+
# Allow nil, to ignore when a parameter is absent
|
30
25
|
return true if val.nil?
|
31
|
-
if klass == Virtus::Attribute::Boolean
|
32
|
-
val.is_a?(TrueClass) || val.is_a?(FalseClass) || (val.is_a?(String) && val.empty?)
|
33
|
-
elsif klass == Rack::Multipart::UploadedFile
|
34
|
-
val.is_a?(Hashie::Mash) && val.key?(:tempfile)
|
35
|
-
elsif [DateTime, Date, Numeric].any? { |vclass| vclass >= klass }
|
36
|
-
return true if val.is_a?(String) && val.empty?
|
37
|
-
val.is_a?(klass)
|
38
|
-
else
|
39
|
-
val.is_a?(klass)
|
40
|
-
end
|
41
|
-
end
|
42
26
|
|
43
|
-
|
44
|
-
if val.instance_of?(InvalidValue)
|
45
|
-
false
|
46
|
-
elsif @option.is_a?(Array) || @option.is_a?(Set)
|
47
|
-
_valid_array_type?(@option.first, val)
|
48
|
-
else
|
49
|
-
_valid_single_type?(@option, val)
|
50
|
-
end
|
27
|
+
converter.value_coerced? val
|
51
28
|
end
|
52
29
|
|
53
|
-
def coerce_value(
|
30
|
+
def coerce_value(val)
|
54
31
|
# Don't coerce things other than nil to Arrays or Hashes
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
# To support custom types that Virtus can't easily coerce, pass in an
|
60
|
-
# explicit coercer. Custom types must implement a `parse` class method.
|
61
|
-
converter_options = {}
|
62
|
-
if ParameterTypes.custom_type?(type)
|
63
|
-
converter_options[:coercer] = type.method(:parse)
|
32
|
+
unless (@option[:method] && !val.nil?) || type.is_a?(Virtus::Attribute)
|
33
|
+
return val || [] if type == Array
|
34
|
+
return val || Set.new if type == Set
|
35
|
+
return val || {} if type == Hash
|
64
36
|
end
|
65
37
|
|
66
|
-
converter = Virtus::Attribute.build(type, converter_options)
|
67
38
|
converter.coerce(val)
|
68
39
|
|
69
40
|
# not the prettiest but some invalid coercion can currently trigger
|
70
41
|
# errors in Virtus (see coerce_spec.rb:75)
|
71
42
|
rescue
|
72
|
-
InvalidValue.new
|
43
|
+
Types::InvalidValue.new
|
44
|
+
end
|
45
|
+
|
46
|
+
# Type to which the parameter will be coerced.
|
47
|
+
#
|
48
|
+
# @return [Class]
|
49
|
+
def type
|
50
|
+
@option[:type]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Create and cache the attribute object
|
54
|
+
# that will be used for parameter coercion
|
55
|
+
# and type checking.
|
56
|
+
#
|
57
|
+
# See {Types.build_coercer}
|
58
|
+
#
|
59
|
+
# @return [Virtus::Attribute]
|
60
|
+
def converter
|
61
|
+
@converter ||= Types.build_coercer(type, @option[:method])
|
73
62
|
end
|
74
63
|
end
|
75
64
|
end
|
@@ -7,9 +7,8 @@ module Grape
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def validate_param!(attr_name, params)
|
10
|
-
|
11
|
-
|
12
|
-
end
|
10
|
+
return if params.respond_to?(:key?) && params.key?(attr_name)
|
11
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :presence
|
13
12
|
end
|
14
13
|
end
|
15
14
|
end
|
@@ -2,10 +2,8 @@ module Grape
|
|
2
2
|
module Validations
|
3
3
|
class RegexpValidator < Base
|
4
4
|
def validate_param!(attr_name, params)
|
5
|
-
|
6
|
-
|
7
|
-
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :regexp
|
8
|
-
end
|
5
|
+
return unless params.key?(attr_name) && !params[attr_name].nil? && !(params[attr_name].to_s =~ @option)
|
6
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :regexp
|
9
7
|
end
|
10
8
|
end
|
11
9
|
end
|
@@ -7,13 +7,13 @@ module Grape
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def validate_param!(attr_name, params)
|
10
|
+
return unless params.is_a?(Hash)
|
10
11
|
return unless params[attr_name] || required_for_root_scope?
|
11
12
|
|
12
13
|
values = @values.is_a?(Proc) ? @values.call : @values
|
13
14
|
param_array = params[attr_name].nil? ? [nil] : Array.wrap(params[attr_name])
|
14
|
-
|
15
|
-
|
16
|
-
end
|
15
|
+
return if param_array.all? { |param| values.include?(param) }
|
16
|
+
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message_key: :values
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
data/lib/grape/version.rb
CHANGED
Binary file
|
@@ -2,10 +2,11 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Grape::Validations do
|
4
4
|
before do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
module CustomValidationsSpec
|
6
|
+
class DefaultLength < Grape::Validations::Base
|
7
|
+
def validate_param!(attr_name, params)
|
8
|
+
@option = params[:max].to_i if params.key?(:max)
|
9
|
+
return if params[attr_name].length <= @option
|
9
10
|
fail Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], message: "must be at the most #{@option} characters long"
|
10
11
|
end
|
11
12
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
module
|
3
|
+
module DeeplyIncludedOptionsSpec
|
4
4
|
module Defaults
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
included do
|
@@ -11,11 +11,11 @@ module API
|
|
11
11
|
module Admin
|
12
12
|
module Defaults
|
13
13
|
extend ActiveSupport::Concern
|
14
|
-
include
|
14
|
+
include DeeplyIncludedOptionsSpec::Defaults
|
15
15
|
end
|
16
16
|
|
17
17
|
class Users < Grape::API
|
18
|
-
include
|
18
|
+
include DeeplyIncludedOptionsSpec::Admin::Defaults
|
19
19
|
|
20
20
|
resource :users do
|
21
21
|
get do
|
@@ -24,14 +24,14 @@ module API
|
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
|
-
end
|
28
27
|
|
29
|
-
class Main < Grape::API
|
30
|
-
|
28
|
+
class Main < Grape::API
|
29
|
+
mount DeeplyIncludedOptionsSpec::Admin::Users
|
30
|
+
end
|
31
31
|
end
|
32
32
|
|
33
33
|
describe Grape::API do
|
34
|
-
subject { Main }
|
34
|
+
subject { DeeplyIncludedOptionsSpec::Main }
|
35
35
|
|
36
36
|
def app
|
37
37
|
subject
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Grape::API::Helpers do
|
4
|
-
|
4
|
+
module NestedHelpersSpec
|
5
5
|
module HelperMethods
|
6
6
|
extend Grape::API::Helpers
|
7
7
|
def current_user
|
@@ -28,8 +28,10 @@ describe Grape::API::Helpers do
|
|
28
28
|
class Main < Grape::API
|
29
29
|
mount Nested
|
30
30
|
end
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
+
subject do
|
34
|
+
NestedHelpersSpec::Main
|
33
35
|
end
|
34
36
|
|
35
37
|
def app
|
@@ -1,18 +1,18 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Grape::API::Helpers do
|
4
|
-
|
5
|
-
|
4
|
+
subject do
|
5
|
+
shared_params = Module.new do
|
6
|
+
extend Grape::API::Helpers
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
params :pagination do
|
9
|
+
optional :page, type: Integer
|
10
|
+
optional :size, type: Integer
|
11
|
+
end
|
10
12
|
end
|
11
|
-
end
|
12
13
|
|
13
|
-
subject do
|
14
14
|
Class.new(Grape::API) do
|
15
|
-
helpers
|
15
|
+
helpers shared_params
|
16
16
|
format :json
|
17
17
|
|
18
18
|
params do
|
data/spec/grape/api_spec.rb
CHANGED
@@ -537,16 +537,38 @@ describe Grape::API do
|
|
537
537
|
expect(last_response.headers['Content-Type']).to eql 'text/plain'
|
538
538
|
end
|
539
539
|
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
'example'
|
540
|
+
describe 'adds an OPTIONS route that' do
|
541
|
+
before do
|
542
|
+
subject.before { header 'X-Custom-Header', 'foo' }
|
543
|
+
subject.get 'example' do
|
544
|
+
'example'
|
545
|
+
end
|
546
|
+
options '/example'
|
547
|
+
end
|
548
|
+
|
549
|
+
it 'returns a 204' do
|
550
|
+
expect(last_response.status).to eql 204
|
551
|
+
end
|
552
|
+
|
553
|
+
it 'has an empty body' do
|
554
|
+
expect(last_response.body).to be_blank
|
555
|
+
end
|
556
|
+
|
557
|
+
it 'has an Allow header' do
|
558
|
+
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
|
559
|
+
end
|
560
|
+
|
561
|
+
it 'has a X-Custom-Header' do
|
562
|
+
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
|
563
|
+
end
|
564
|
+
|
565
|
+
it 'has no Content-Type' do
|
566
|
+
expect(last_response.content_type).to be_nil
|
567
|
+
end
|
568
|
+
|
569
|
+
it 'has no Content-Length' do
|
570
|
+
expect(last_response.content_length).to be_nil
|
544
571
|
end
|
545
|
-
options '/example'
|
546
|
-
expect(last_response.status).to eql 204
|
547
|
-
expect(last_response.body).to eql ''
|
548
|
-
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
|
549
|
-
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
|
550
572
|
end
|
551
573
|
|
552
574
|
it 'allows HEAD on a GET request' do
|
@@ -640,7 +662,7 @@ describe Grape::API do
|
|
640
662
|
end
|
641
663
|
|
642
664
|
it 'adds a after_validation filter' do
|
643
|
-
subject.after_validation { @foo = "first #{params[:id]
|
665
|
+
subject.after_validation { @foo = "first #{params[:id]}:#{params[:id].class}" }
|
644
666
|
subject.after_validation { @bar = 'second' }
|
645
667
|
subject.params do
|
646
668
|
requires :id, type: Integer
|
@@ -787,7 +809,7 @@ describe Grape::API do
|
|
787
809
|
|
788
810
|
it 'returns raw data when content type binary' do
|
789
811
|
image_filename = 'grape.png'
|
790
|
-
file = File.open(image_filename, 'rb'
|
812
|
+
file = File.open(image_filename, 'rb', &:read)
|
791
813
|
subject.format :binary
|
792
814
|
subject.get('/binary_file') { File.binread(image_filename) }
|
793
815
|
get '/binary_file'
|
@@ -1066,7 +1088,7 @@ describe Grape::API do
|
|
1066
1088
|
|
1067
1089
|
subject.get(:hello) { 'Hello, world.' }
|
1068
1090
|
get '/hello', {}, 'HTTP_AUTHORIZATION' => encode_basic_auth('allow', 'whatever')
|
1069
|
-
expect(basic_auth_context).to
|
1091
|
+
expect(basic_auth_context).to be_a_kind_of(Grape::Endpoint)
|
1070
1092
|
end
|
1071
1093
|
|
1072
1094
|
it 'has access to helper methods' do
|
@@ -1309,21 +1331,23 @@ describe Grape::API do
|
|
1309
1331
|
|
1310
1332
|
context 'CustomError subclass of Grape::Exceptions::Base' do
|
1311
1333
|
before do
|
1312
|
-
|
1334
|
+
module ApiSpec
|
1335
|
+
class CustomError < Grape::Exceptions::Base; end
|
1336
|
+
end
|
1313
1337
|
end
|
1314
1338
|
|
1315
1339
|
it 'does not re-raise exceptions of type Grape::Exceptions::Base' do
|
1316
|
-
subject.get('/custom_exception') { fail CustomError }
|
1340
|
+
subject.get('/custom_exception') { fail ApiSpec::CustomError }
|
1317
1341
|
|
1318
1342
|
expect { get '/custom_exception' }.not_to raise_error
|
1319
1343
|
end
|
1320
1344
|
|
1321
1345
|
it 'rescues custom grape exceptions' do
|
1322
|
-
subject.rescue_from CustomError do |e|
|
1346
|
+
subject.rescue_from ApiSpec::CustomError do |e|
|
1323
1347
|
rack_response('New Error', e.status)
|
1324
1348
|
end
|
1325
1349
|
subject.get '/custom_error' do
|
1326
|
-
fail CustomError, status: 400, message: 'Custom Error'
|
1350
|
+
fail ApiSpec::CustomError, status: 400, message: 'Custom Error'
|
1327
1351
|
end
|
1328
1352
|
|
1329
1353
|
get '/custom_error'
|
@@ -1435,7 +1459,7 @@ describe Grape::API do
|
|
1435
1459
|
|
1436
1460
|
describe '.rescue_from klass, lambda' do
|
1437
1461
|
it 'rescues an error with the lambda' do
|
1438
|
-
subject.rescue_from ArgumentError,
|
1462
|
+
subject.rescue_from ArgumentError, lambda {
|
1439
1463
|
rack_response('rescued with a lambda', 400)
|
1440
1464
|
}
|
1441
1465
|
subject.get('/rescue_lambda') { fail ArgumentError }
|
@@ -1446,7 +1470,7 @@ describe Grape::API do
|
|
1446
1470
|
end
|
1447
1471
|
|
1448
1472
|
it 'can execute the lambda with an argument' do
|
1449
|
-
subject.rescue_from ArgumentError,
|
1473
|
+
subject.rescue_from ArgumentError, lambda { |e|
|
1450
1474
|
rack_response(e.message, 400)
|
1451
1475
|
}
|
1452
1476
|
subject.get('/rescue_lambda') { fail ArgumentError, 'lambda takes an argument' }
|
@@ -1474,21 +1498,23 @@ describe Grape::API do
|
|
1474
1498
|
|
1475
1499
|
describe '.rescue_from klass, rescue_subclasses: boolean' do
|
1476
1500
|
before do
|
1477
|
-
module
|
1478
|
-
|
1479
|
-
|
1501
|
+
module ApiSpec
|
1502
|
+
module APIErrors
|
1503
|
+
class ParentError < StandardError; end
|
1504
|
+
class ChildError < ParentError; end
|
1505
|
+
end
|
1480
1506
|
end
|
1481
1507
|
end
|
1482
1508
|
|
1483
1509
|
it 'rescues error as well as subclass errors with rescue_subclasses option set' do
|
1484
|
-
subject.rescue_from APIErrors::ParentError, rescue_subclasses: true do |e|
|
1510
|
+
subject.rescue_from ApiSpec::APIErrors::ParentError, rescue_subclasses: true do |e|
|
1485
1511
|
rack_response("rescued from #{e.class.name}", 500)
|
1486
1512
|
end
|
1487
1513
|
subject.get '/caught_child' do
|
1488
|
-
fail APIErrors::ChildError
|
1514
|
+
fail ApiSpec::APIErrors::ChildError
|
1489
1515
|
end
|
1490
1516
|
subject.get '/caught_parent' do
|
1491
|
-
fail APIErrors::ParentError
|
1517
|
+
fail ApiSpec::APIErrors::ParentError
|
1492
1518
|
end
|
1493
1519
|
subject.get '/uncaught_parent' do
|
1494
1520
|
fail StandardError
|
@@ -1502,11 +1528,11 @@ describe Grape::API do
|
|
1502
1528
|
end
|
1503
1529
|
|
1504
1530
|
it 'sets rescue_subclasses to true by default' do
|
1505
|
-
subject.rescue_from APIErrors::ParentError do |e|
|
1531
|
+
subject.rescue_from ApiSpec::APIErrors::ParentError do |e|
|
1506
1532
|
rack_response("rescued from #{e.class.name}", 500)
|
1507
1533
|
end
|
1508
1534
|
subject.get '/caught_child' do
|
1509
|
-
fail APIErrors::ChildError
|
1535
|
+
fail ApiSpec::APIErrors::ChildError
|
1510
1536
|
end
|
1511
1537
|
|
1512
1538
|
get '/caught_child'
|
@@ -1514,13 +1540,13 @@ describe Grape::API do
|
|
1514
1540
|
end
|
1515
1541
|
|
1516
1542
|
it 'does not rescue child errors if rescue_subclasses is false' do
|
1517
|
-
subject.rescue_from APIErrors::ParentError, rescue_subclasses: false do |e|
|
1543
|
+
subject.rescue_from ApiSpec::APIErrors::ParentError, rescue_subclasses: false do |e|
|
1518
1544
|
rack_response("rescued from #{e.class.name}", 500)
|
1519
1545
|
end
|
1520
1546
|
subject.get '/uncaught' do
|
1521
|
-
fail APIErrors::ChildError
|
1547
|
+
fail ApiSpec::APIErrors::ChildError
|
1522
1548
|
end
|
1523
|
-
expect { get '/uncaught' }.to raise_error(APIErrors::ChildError)
|
1549
|
+
expect { get '/uncaught' }.to raise_error(ApiSpec::APIErrors::ChildError)
|
1524
1550
|
end
|
1525
1551
|
end
|
1526
1552
|
|
@@ -1572,15 +1598,17 @@ describe Grape::API do
|
|
1572
1598
|
|
1573
1599
|
context 'class' do
|
1574
1600
|
before :each do
|
1575
|
-
|
1576
|
-
|
1577
|
-
|
1601
|
+
module ApiSpec
|
1602
|
+
class CustomErrorFormatter
|
1603
|
+
def self.call(message, _backtrace, _options, _env)
|
1604
|
+
"message: #{message} @backtrace"
|
1605
|
+
end
|
1578
1606
|
end
|
1579
1607
|
end
|
1580
1608
|
end
|
1581
1609
|
it 'returns a custom error format' do
|
1582
1610
|
subject.rescue_from :all, backtrace: true
|
1583
|
-
subject.error_formatter :txt, CustomErrorFormatter
|
1611
|
+
subject.error_formatter :txt, ApiSpec::CustomErrorFormatter
|
1584
1612
|
subject.get '/exception' do
|
1585
1613
|
fail 'rain!'
|
1586
1614
|
end
|
@@ -1592,16 +1620,18 @@ describe Grape::API do
|
|
1592
1620
|
describe 'with' do
|
1593
1621
|
context 'class' do
|
1594
1622
|
before :each do
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1623
|
+
module ApiSpec
|
1624
|
+
class CustomErrorFormatter
|
1625
|
+
def self.call(message, _backtrace, _option, _env)
|
1626
|
+
"message: #{message} @backtrace"
|
1627
|
+
end
|
1598
1628
|
end
|
1599
1629
|
end
|
1600
1630
|
end
|
1601
1631
|
|
1602
1632
|
it 'returns a custom error format' do
|
1603
1633
|
subject.rescue_from :all, backtrace: true
|
1604
|
-
subject.error_formatter :txt, with: CustomErrorFormatter
|
1634
|
+
subject.error_formatter :txt, with: ApiSpec::CustomErrorFormatter
|
1605
1635
|
subject.get('/exception') { fail 'rain!' }
|
1606
1636
|
|
1607
1637
|
get '/exception'
|
@@ -1679,8 +1709,8 @@ describe Grape::API do
|
|
1679
1709
|
describe '.formatter' do
|
1680
1710
|
context 'multiple formatters' do
|
1681
1711
|
before :each do
|
1682
|
-
subject.formatter :json, ->(object, _env) { "{\"custom_formatter\":\"#{object[:some]
|
1683
|
-
subject.formatter :txt, ->(object, _env) { "custom_formatter: #{object[:some]
|
1712
|
+
subject.formatter :json, ->(object, _env) { "{\"custom_formatter\":\"#{object[:some]}\"}" }
|
1713
|
+
subject.formatter :txt, ->(object, _env) { "custom_formatter: #{object[:some]}" }
|
1684
1714
|
subject.get :simple do
|
1685
1715
|
{ some: 'hash' }
|
1686
1716
|
end
|
@@ -1698,7 +1728,7 @@ describe Grape::API do
|
|
1698
1728
|
before :each do
|
1699
1729
|
subject.content_type :json, 'application/json'
|
1700
1730
|
subject.content_type :custom, 'application/custom'
|
1701
|
-
subject.formatter :custom, ->(object, _env) { "{\"custom_formatter\":\"#{object[:some]
|
1731
|
+
subject.formatter :custom, ->(object, _env) { "{\"custom_formatter\":\"#{object[:some]}\"}" }
|
1702
1732
|
subject.get :simple do
|
1703
1733
|
{ some: 'hash' }
|
1704
1734
|
end
|
@@ -1713,15 +1743,17 @@ describe Grape::API do
|
|
1713
1743
|
end
|
1714
1744
|
end
|
1715
1745
|
context 'custom formatter class' do
|
1716
|
-
module
|
1717
|
-
|
1718
|
-
|
1746
|
+
module ApiSpec
|
1747
|
+
module CustomFormatter
|
1748
|
+
def self.call(object, _env)
|
1749
|
+
"{\"custom_formatter\":\"#{object[:some]}\"}"
|
1750
|
+
end
|
1719
1751
|
end
|
1720
1752
|
end
|
1721
1753
|
before :each do
|
1722
1754
|
subject.content_type :json, 'application/json'
|
1723
1755
|
subject.content_type :custom, 'application/custom'
|
1724
|
-
subject.formatter :custom, CustomFormatter
|
1756
|
+
subject.formatter :custom, ApiSpec::CustomFormatter
|
1725
1757
|
subject.get :simple do
|
1726
1758
|
{ some: 'hash' }
|
1727
1759
|
end
|
@@ -1765,15 +1797,17 @@ describe Grape::API do
|
|
1765
1797
|
end
|
1766
1798
|
end
|
1767
1799
|
context 'custom parser class' do
|
1768
|
-
module
|
1769
|
-
|
1770
|
-
|
1800
|
+
module ApiSpec
|
1801
|
+
module CustomParser
|
1802
|
+
def self.call(object, _env)
|
1803
|
+
{ object.to_sym => object.to_s.reverse }
|
1804
|
+
end
|
1771
1805
|
end
|
1772
1806
|
end
|
1773
1807
|
before :each do
|
1774
1808
|
subject.content_type :txt, 'text/plain'
|
1775
1809
|
subject.content_type :custom, 'text/custom'
|
1776
|
-
subject.parser :custom, CustomParser
|
1810
|
+
subject.parser :custom, ApiSpec::CustomParser
|
1777
1811
|
subject.put :simple do
|
1778
1812
|
params[:simple]
|
1779
1813
|
end
|
@@ -1798,7 +1832,7 @@ describe Grape::API do
|
|
1798
1832
|
before :each do
|
1799
1833
|
subject.parser :json, nil
|
1800
1834
|
subject.put 'data' do
|
1801
|
-
"body: #{env['api.request.body']
|
1835
|
+
"body: #{env['api.request.body']}"
|
1802
1836
|
end
|
1803
1837
|
end
|
1804
1838
|
it 'does not parse data' do
|
@@ -2812,10 +2846,10 @@ XML
|
|
2812
2846
|
end
|
2813
2847
|
it 'hash' do
|
2814
2848
|
subject.get '/example' do
|
2815
|
-
|
2816
|
-
:
|
2817
|
-
:
|
2818
|
-
|
2849
|
+
{
|
2850
|
+
example1: 'example1',
|
2851
|
+
example2: 'example2'
|
2852
|
+
}
|
2819
2853
|
end
|
2820
2854
|
get '/example'
|
2821
2855
|
expect(last_response.status).to eq(200)
|
@@ -2880,7 +2914,7 @@ XML
|
|
2880
2914
|
[true, false].each do |anchor|
|
2881
2915
|
it "anchor=#{anchor}" do
|
2882
2916
|
subject.route :any, '*path', anchor: anchor do
|
2883
|
-
error!("Unrecognized request path: #{params[:path]
|
2917
|
+
error!("Unrecognized request path: #{params[:path]} - #{env['PATH_INFO']}#{env['SCRIPT_NAME']}", 404)
|
2884
2918
|
end
|
2885
2919
|
get '/v1/hello'
|
2886
2920
|
expect(last_response.status).to eq(200)
|