grape 2.2.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +41 -18
- data/UPGRADING.md +75 -1
- data/grape.gemspec +5 -5
- data/lib/grape/api/instance.rb +25 -60
- data/lib/grape/api.rb +44 -76
- data/lib/grape/cookies.rb +31 -25
- data/lib/grape/dsl/api.rb +0 -2
- data/lib/grape/dsl/desc.rb +27 -24
- data/lib/grape/dsl/headers.rb +1 -1
- data/lib/grape/dsl/helpers.rb +1 -1
- data/lib/grape/dsl/inside_route.rb +17 -40
- data/lib/grape/dsl/parameters.rb +5 -5
- data/lib/grape/dsl/routing.rb +14 -13
- data/lib/grape/endpoint.rb +100 -106
- data/lib/grape/error_formatter/base.rb +51 -21
- data/lib/grape/error_formatter/json.rb +7 -24
- data/lib/grape/error_formatter/serializable_hash.rb +7 -0
- data/lib/grape/error_formatter/txt.rb +13 -20
- data/lib/grape/error_formatter/xml.rb +3 -13
- data/lib/grape/error_formatter.rb +4 -12
- data/lib/grape/exceptions/base.rb +18 -30
- data/lib/grape/exceptions/conflicting_types.rb +11 -0
- data/lib/grape/exceptions/invalid_parameters.rb +11 -0
- data/lib/grape/exceptions/too_deep_parameters.rb +11 -0
- data/lib/grape/exceptions/unknown_auth_strategy.rb +11 -0
- data/lib/grape/exceptions/unknown_params_builder.rb +11 -0
- data/lib/grape/exceptions/validation.rb +5 -4
- data/lib/grape/exceptions/validation_errors.rb +2 -2
- data/lib/grape/extensions/active_support/hash_with_indifferent_access.rb +2 -5
- data/lib/grape/extensions/hash.rb +2 -1
- data/lib/grape/extensions/hashie/mash.rb +3 -5
- data/lib/grape/formatter/base.rb +16 -0
- data/lib/grape/formatter/json.rb +4 -6
- data/lib/grape/formatter/serializable_hash.rb +1 -1
- data/lib/grape/formatter/txt.rb +3 -5
- data/lib/grape/formatter/xml.rb +4 -6
- data/lib/grape/formatter.rb +4 -12
- data/lib/grape/locale/en.yml +44 -44
- data/lib/grape/middleware/auth/base.rb +11 -32
- data/lib/grape/middleware/auth/dsl.rb +23 -29
- data/lib/grape/middleware/base.rb +30 -11
- data/lib/grape/middleware/error.rb +18 -24
- data/lib/grape/middleware/formatter.rb +39 -73
- data/lib/grape/middleware/stack.rb +26 -36
- data/lib/grape/middleware/versioner/accept_version_header.rb +1 -3
- data/lib/grape/middleware/versioner/base.rb +74 -0
- data/lib/grape/middleware/versioner/header.rb +4 -10
- data/lib/grape/middleware/versioner/param.rb +2 -5
- data/lib/grape/middleware/versioner/path.rb +0 -2
- data/lib/grape/middleware/versioner.rb +5 -3
- data/lib/grape/namespace.rb +1 -1
- data/lib/grape/params_builder/base.rb +18 -0
- data/lib/grape/params_builder/hash.rb +11 -0
- data/lib/grape/params_builder/hash_with_indifferent_access.rb +11 -0
- data/lib/grape/params_builder/hashie_mash.rb +11 -0
- data/lib/grape/params_builder.rb +32 -0
- data/lib/grape/parser/base.rb +16 -0
- data/lib/grape/parser/json.rb +6 -8
- data/lib/grape/parser/xml.rb +6 -8
- data/lib/grape/parser.rb +5 -7
- data/lib/grape/path.rb +39 -56
- data/lib/grape/request.rb +162 -23
- data/lib/grape/router/base_route.rb +2 -2
- data/lib/grape/router/greedy_route.rb +2 -2
- data/lib/grape/router/pattern.rb +23 -18
- data/lib/grape/router/route.rb +14 -6
- data/lib/grape/router.rb +30 -12
- data/lib/grape/util/registry.rb +27 -0
- data/lib/grape/validations/contract_scope.rb +2 -39
- data/lib/grape/validations/params_scope.rb +15 -14
- data/lib/grape/validations/types/dry_type_coercer.rb +10 -6
- data/lib/grape/validations/validator_factory.rb +2 -2
- data/lib/grape/validations/validators/allow_blank_validator.rb +1 -1
- data/lib/grape/validations/validators/base.rb +7 -11
- data/lib/grape/validations/validators/coerce_validator.rb +1 -1
- data/lib/grape/validations/validators/contract_scope_validator.rb +41 -0
- data/lib/grape/validations/validators/default_validator.rb +1 -1
- data/lib/grape/validations/validators/except_values_validator.rb +2 -2
- data/lib/grape/validations/validators/length_validator.rb +1 -1
- data/lib/grape/validations/validators/presence_validator.rb +1 -1
- data/lib/grape/validations/validators/regexp_validator.rb +2 -2
- data/lib/grape/validations/validators/values_validator.rb +15 -57
- data/lib/grape/validations.rb +8 -17
- data/lib/grape/version.rb +1 -1
- data/lib/grape.rb +14 -2
- metadata +24 -16
- data/lib/grape/http/headers.rb +0 -55
- data/lib/grape/middleware/helpers.rb +0 -12
- data/lib/grape/middleware/versioner_helpers.rb +0 -75
- data/lib/grape/util/lazy/object.rb +0 -45
- data/lib/grape/validations/types/build_coercer.rb +0 -92
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Exceptions
|
5
|
+
class UnknownParamsBuilder < Base
|
6
|
+
def initialize(params_builder_type)
|
7
|
+
super(message: compose_message(:unknown_params_builder, params_builder_type: params_builder_type))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -2,16 +2,17 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Exceptions
|
5
|
-
class Validation <
|
5
|
+
class Validation < Base
|
6
6
|
attr_accessor :params, :message_key
|
7
7
|
|
8
|
-
def initialize(params:, message: nil,
|
8
|
+
def initialize(params:, message: nil, status: nil, headers: nil)
|
9
9
|
@params = params
|
10
10
|
if message
|
11
11
|
@message_key = message if message.is_a?(Symbol)
|
12
|
-
|
12
|
+
message = translate_message(message)
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
|
+
super(status: status, message: message, headers: headers)
|
15
16
|
end
|
16
17
|
|
17
18
|
# Remove all the unnecessary stuff from Grape::Exceptions::Base like status
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Exceptions
|
5
|
-
class ValidationErrors <
|
5
|
+
class ValidationErrors < Base
|
6
6
|
ERRORS_FORMAT_KEY = 'grape.errors.format'
|
7
7
|
DEFAULT_ERRORS_FORMAT = '%<attributes>s %<message>s'
|
8
8
|
|
@@ -10,7 +10,7 @@ module Grape
|
|
10
10
|
|
11
11
|
attr_reader :errors
|
12
12
|
|
13
|
-
def initialize(errors: [], headers: {}
|
13
|
+
def initialize(errors: [], headers: {})
|
14
14
|
@errors = errors.group_by(&:params)
|
15
15
|
super(message: full_messages.join(', '), status: 400, headers: headers)
|
16
16
|
end
|
@@ -8,11 +8,8 @@ module Grape
|
|
8
8
|
extend ::ActiveSupport::Concern
|
9
9
|
|
10
10
|
included do
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def params_builder
|
15
|
-
Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder
|
11
|
+
Grape.deprecator.warn 'This concern has been deprecated. Use `build_with` with one of the following short_name (:hash, :hash_with_indifferent_access, :hashie_mash) instead.'
|
12
|
+
namespace_inheritable(:build_params_with, :hash_with_indifferent_access)
|
16
13
|
end
|
17
14
|
|
18
15
|
def build_params
|
@@ -7,7 +7,8 @@ module Grape
|
|
7
7
|
extend ::ActiveSupport::Concern
|
8
8
|
|
9
9
|
included do
|
10
|
-
|
10
|
+
Grape.deprecator.warn 'This concern has been deprecated. Use `build_with` with one of the following short_name (:hash, :hash_with_indifferent_access, :hashie_mash) instead.'
|
11
|
+
namespace_inheritable(:build_params_with, :hash)
|
11
12
|
end
|
12
13
|
|
13
14
|
def build_params
|
@@ -6,12 +6,10 @@ module Grape
|
|
6
6
|
module Mash
|
7
7
|
module ParamBuilder
|
8
8
|
extend ::ActiveSupport::Concern
|
9
|
-
included do
|
10
|
-
namespace_inheritable(:build_params_with, Grape::Extensions::Hashie::Mash::ParamBuilder)
|
11
|
-
end
|
12
9
|
|
13
|
-
|
14
|
-
Grape
|
10
|
+
included do
|
11
|
+
Grape.deprecator.warn 'This concern has been deprecated. Use `build_with` with one of the following short_name (:hash, :hash_with_indifferent_access, :hashie_mash) instead.'
|
12
|
+
namespace_inheritable(:build_params_with, :hashie_mash)
|
15
13
|
end
|
16
14
|
|
17
15
|
def build_params
|
data/lib/grape/formatter/json.rb
CHANGED
@@ -2,13 +2,11 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Formatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
return object.to_json if object.respond_to?(:to_json)
|
5
|
+
class Json < Base
|
6
|
+
def self.call(object, _env)
|
7
|
+
return object.to_json if object.respond_to?(:to_json)
|
9
8
|
|
10
|
-
|
11
|
-
end
|
9
|
+
::Grape::Json.dump(object)
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|
data/lib/grape/formatter/txt.rb
CHANGED
@@ -2,11 +2,9 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Formatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
object.respond_to?(:to_txt) ? object.to_txt : object.to_s
|
9
|
-
end
|
5
|
+
class Txt < Base
|
6
|
+
def self.call(object, _env)
|
7
|
+
object.respond_to?(:to_txt) ? object.to_txt : object.to_s
|
10
8
|
end
|
11
9
|
end
|
12
10
|
end
|
data/lib/grape/formatter/xml.rb
CHANGED
@@ -2,13 +2,11 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Formatter
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
return object.to_xml if object.respond_to?(:to_xml)
|
5
|
+
class Xml < Base
|
6
|
+
def self.call(object, _env)
|
7
|
+
return object.to_xml if object.respond_to?(:to_xml)
|
9
8
|
|
10
|
-
|
11
|
-
end
|
9
|
+
raise Grape::Exceptions::InvalidFormatter.new(object.class, 'xml')
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|
data/lib/grape/formatter.rb
CHANGED
@@ -2,24 +2,16 @@
|
|
2
2
|
|
3
3
|
module Grape
|
4
4
|
module Formatter
|
5
|
-
|
5
|
+
extend Grape::Util::Registry
|
6
6
|
|
7
|
-
|
8
|
-
json: Grape::Formatter::Json,
|
9
|
-
jsonapi: Grape::Formatter::Json,
|
10
|
-
serializable_hash: Grape::Formatter::SerializableHash,
|
11
|
-
txt: Grape::Formatter::Txt,
|
12
|
-
xml: Grape::Formatter::Xml
|
13
|
-
}.freeze
|
7
|
+
module_function
|
14
8
|
|
15
9
|
DEFAULT_LAMBDA_FORMATTER = ->(obj, _env) { obj }
|
16
10
|
|
17
11
|
def formatter_for(api_format, formatters)
|
18
|
-
|
19
|
-
end
|
12
|
+
return formatters[api_format] if formatters&.key?(api_format)
|
20
13
|
|
21
|
-
|
22
|
-
formatters&.key?(api_format) ? formatters[api_format] : DEFAULTS[api_format]
|
14
|
+
registry[api_format] || DEFAULT_LAMBDA_FORMATTER
|
23
15
|
end
|
24
16
|
end
|
25
17
|
end
|
data/lib/grape/locale/en.yml
CHANGED
@@ -1,59 +1,59 @@
|
|
1
|
+
---
|
1
2
|
en:
|
2
3
|
grape:
|
3
4
|
errors:
|
4
|
-
format:
|
5
|
+
format: '%{attributes} %{message}'
|
5
6
|
messages:
|
6
|
-
|
7
|
-
|
8
|
-
regexp: 'is invalid'
|
7
|
+
all_or_none: 'provide all or none of parameters'
|
8
|
+
at_least_one: 'are missing, at least one parameter must be provided'
|
9
9
|
blank: 'is empty'
|
10
|
-
|
10
|
+
coerce: 'is invalid'
|
11
|
+
conflicting_types: 'query params contains conflicting types'
|
12
|
+
empty_message_body: 'empty message body supplied with %{body_format} content-type'
|
13
|
+
exactly_one: 'are missing, exactly one parameter must be provided'
|
11
14
|
except_values: 'has a value not allowed'
|
12
|
-
|
15
|
+
incompatible_option_values: '%{option1}: %{value1} is incompatible with %{option2}: %{value2}'
|
16
|
+
invalid_accept_header:
|
17
|
+
problem: 'invalid accept header'
|
18
|
+
resolution: '%{message}'
|
19
|
+
invalid_formatter: 'cannot convert %{klass} to %{to_format}'
|
20
|
+
invalid_message_body:
|
21
|
+
problem: 'message body does not match declared format'
|
22
|
+
resolution: 'when specifying %{body_format} as content-type, you must pass valid %{body_format} in the request''s ''body'' '
|
23
|
+
invalid_parameters: 'query params contains invalid format or byte sequence'
|
24
|
+
invalid_response: 'Invalid response'
|
25
|
+
invalid_version_header:
|
26
|
+
problem: 'invalid version header'
|
27
|
+
resolution: '%{message}'
|
28
|
+
invalid_versioner_option:
|
29
|
+
problem: 'unknown :using for versioner: %{strategy}'
|
30
|
+
resolution: 'available strategy for :using is :path, :header, :accept_version_header, :param'
|
31
|
+
invalid_with_option_for_represent:
|
32
|
+
problem: 'you must specify an entity class in the :with option'
|
33
|
+
resolution: 'eg: represent User, :with => Entity::User'
|
13
34
|
length: 'is expected to have length within %{min} and %{max}'
|
14
35
|
length_is: 'is expected to have length exactly equal to %{is}'
|
15
|
-
length_min: 'is expected to have length greater than or equal to %{min}'
|
16
36
|
length_max: 'is expected to have length less than or equal to %{max}'
|
17
|
-
|
18
|
-
|
19
|
-
summary: 'when version using header, you must specify :vendor option'
|
20
|
-
resolution: "eg: version 'v1', using: :header, vendor: 'twitter'"
|
37
|
+
length_min: 'is expected to have length greater than or equal to %{min}'
|
38
|
+
missing_group_type: 'group type is required'
|
21
39
|
missing_mime_type:
|
22
40
|
problem: 'missing mime type for %{new_format}'
|
23
|
-
resolution:
|
24
|
-
"you can choose existing mime type from Grape::ContentTypes::CONTENT_TYPES
|
25
|
-
or add your own with content_type :%{new_format}, 'application/%{new_format}'
|
26
|
-
"
|
27
|
-
invalid_with_option_for_represent:
|
28
|
-
problem: 'you must specify an entity class in the :with option'
|
29
|
-
resolution: 'eg: represent User, :with => Entity::User'
|
41
|
+
resolution: 'you can choose existing mime type from Grape::ContentTypes::CONTENT_TYPES or add your own with content_type :%{new_format}, ''application/%{new_format}'' '
|
30
42
|
missing_option: 'you must specify :%{option} options'
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
43
|
+
missing_vendor_option:
|
44
|
+
problem: 'missing :vendor option'
|
45
|
+
resolution: 'eg: version ''v1'', using: :header, vendor: ''twitter'''
|
46
|
+
summary: 'when version using header, you must specify :vendor option'
|
47
|
+
mutual_exclusion: 'are mutually exclusive'
|
48
|
+
presence: 'is missing'
|
49
|
+
regexp: 'is invalid'
|
50
|
+
same_as: 'is not the same as %{parameter}'
|
51
|
+
too_deep_parameters: 'query params are recursively nested over the specified limit (%{limit})'
|
52
|
+
too_many_multipart_files: 'the number of uploaded files exceeded the system''s configured limit (%{limit})'
|
53
|
+
unknown_auth_strategy: 'unknown auth strategy: %{strategy}'
|
36
54
|
unknown_options: 'unknown options: %{options}'
|
37
55
|
unknown_parameter: 'unknown parameter: %{param}'
|
38
|
-
|
39
|
-
|
40
|
-
at_least_one: 'are missing, at least one parameter must be provided'
|
41
|
-
exactly_one: 'are missing, exactly one parameter must be provided'
|
42
|
-
all_or_none: 'provide all or none of parameters'
|
43
|
-
missing_group_type: 'group type is required'
|
56
|
+
unknown_params_builder: 'unknown params_builder: %{params_builder_type}'
|
57
|
+
unknown_validator: 'unknown validator: %{validator_type}'
|
44
58
|
unsupported_group_type: 'group type must be Array, Hash, JSON or Array[JSON]'
|
45
|
-
|
46
|
-
problem: "message body does not match declared format"
|
47
|
-
resolution:
|
48
|
-
"when specifying %{body_format} as content-type, you must pass valid
|
49
|
-
%{body_format} in the request's 'body'
|
50
|
-
"
|
51
|
-
empty_message_body: 'empty message body supplied with %{body_format} content-type'
|
52
|
-
too_many_multipart_files: "the number of uploaded files exceeded the system's configured limit (%{limit})"
|
53
|
-
invalid_accept_header:
|
54
|
-
problem: 'invalid accept header'
|
55
|
-
resolution: '%{message}'
|
56
|
-
invalid_version_header:
|
57
|
-
problem: 'invalid version header'
|
58
|
-
resolution: '%{message}'
|
59
|
-
invalid_response: 'Invalid response'
|
59
|
+
values: 'does not have a valid value'
|
@@ -3,40 +3,19 @@
|
|
3
3
|
module Grape
|
4
4
|
module Middleware
|
5
5
|
module Auth
|
6
|
-
class Base
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
@app = app
|
13
|
-
@options = options.shift
|
14
|
-
end
|
15
|
-
|
16
|
-
def call(env)
|
17
|
-
dup._call(env)
|
6
|
+
class Base < Grape::Middleware::Base
|
7
|
+
def initialize(app, **options)
|
8
|
+
super
|
9
|
+
@auth_strategy = Grape::Middleware::Auth::Strategies[options[:type]].tap do |auth_strategy|
|
10
|
+
raise Grape::Exceptions::UnknownAuthStrategy.new(strategy: options[:type]) unless auth_strategy
|
11
|
+
end
|
18
12
|
end
|
19
13
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
auth_proc_context = context
|
26
|
-
|
27
|
-
strategy_info = Grape::Middleware::Auth::Strategies[options[:type]]
|
28
|
-
|
29
|
-
throw(:error, status: 401, message: 'API Authorization Failed.') if strategy_info.blank?
|
30
|
-
|
31
|
-
strategy = strategy_info.create(@app, options) do |*args|
|
32
|
-
auth_proc_context.instance_exec(*args, &auth_proc)
|
33
|
-
end
|
34
|
-
|
35
|
-
strategy.call(env)
|
36
|
-
|
37
|
-
else
|
38
|
-
app.call(env)
|
39
|
-
end
|
14
|
+
def call!(env)
|
15
|
+
@env = env
|
16
|
+
@auth_strategy.create(app, options) do |*args|
|
17
|
+
context.instance_exec(*args, &options[:proc])
|
18
|
+
end.call(env)
|
40
19
|
end
|
41
20
|
end
|
42
21
|
end
|
@@ -4,40 +4,34 @@ module Grape
|
|
4
4
|
module Middleware
|
5
5
|
module Auth
|
6
6
|
module DSL
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
if type
|
14
|
-
namespace_inheritable(:auth, options.reverse_merge(type: type.to_sym, proc: block))
|
15
|
-
use Grape::Middleware::Auth::Base, namespace_inheritable(:auth)
|
16
|
-
else
|
17
|
-
namespace_inheritable(:auth)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
# Add HTTP Basic authorization to the API.
|
22
|
-
#
|
23
|
-
# @param [Hash] options A hash of options.
|
24
|
-
# @option options [String] :realm "API Authorization" The HTTP Basic realm.
|
25
|
-
def http_basic(options = {}, &block)
|
26
|
-
options[:realm] ||= 'API Authorization'
|
27
|
-
auth :http_basic, options, &block
|
7
|
+
def auth(type = nil, options = {}, &block)
|
8
|
+
if type
|
9
|
+
namespace_inheritable(:auth, options.reverse_merge(type: type.to_sym, proc: block))
|
10
|
+
use Grape::Middleware::Auth::Base, namespace_inheritable(:auth)
|
11
|
+
else
|
12
|
+
namespace_inheritable(:auth)
|
28
13
|
end
|
14
|
+
end
|
29
15
|
|
30
|
-
|
31
|
-
|
16
|
+
# Add HTTP Basic authorization to the API.
|
17
|
+
#
|
18
|
+
# @param [Hash] options A hash of options.
|
19
|
+
# @option options [String] :realm "API Authorization" The HTTP Basic realm.
|
20
|
+
def http_basic(options = {}, &block)
|
21
|
+
options[:realm] ||= 'API Authorization'
|
22
|
+
auth :http_basic, options, &block
|
23
|
+
end
|
32
24
|
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
options[:opaque] ||= 'secret'
|
37
|
-
end
|
25
|
+
def http_digest(options = {}, &block)
|
26
|
+
options[:realm] ||= 'API Authorization'
|
38
27
|
|
39
|
-
|
28
|
+
if options[:realm].respond_to?(:values_at)
|
29
|
+
options[:realm][:opaque] ||= 'secret'
|
30
|
+
else
|
31
|
+
options[:opaque] ||= 'secret'
|
40
32
|
end
|
33
|
+
|
34
|
+
auth :http_digest, options, &block
|
41
35
|
end
|
42
36
|
end
|
43
37
|
end
|
@@ -3,25 +3,18 @@
|
|
3
3
|
module Grape
|
4
4
|
module Middleware
|
5
5
|
class Base
|
6
|
-
include Helpers
|
7
6
|
include Grape::DSL::Headers
|
8
7
|
|
9
8
|
attr_reader :app, :env, :options
|
10
9
|
|
11
|
-
TEXT_HTML = 'text/html'
|
12
|
-
|
13
10
|
# @param [Rack Application] app The standard argument for a Rack middleware.
|
14
11
|
# @param [Hash] options A hash of options, simply stored for use by subclasses.
|
15
|
-
def initialize(app,
|
12
|
+
def initialize(app, **options)
|
16
13
|
@app = app
|
17
|
-
@options =
|
14
|
+
@options = merge_default_options(options)
|
18
15
|
@app_response = nil
|
19
16
|
end
|
20
17
|
|
21
|
-
def default_options
|
22
|
-
{}
|
23
|
-
end
|
24
|
-
|
25
18
|
def call(env)
|
26
19
|
dup.call!(env).to_a
|
27
20
|
end
|
@@ -54,10 +47,18 @@ module Grape
|
|
54
47
|
# @return [Response, nil] a Rack SPEC response or nil to call the application afterwards.
|
55
48
|
def after; end
|
56
49
|
|
50
|
+
def rack_request
|
51
|
+
@rack_request ||= Rack::Request.new(env)
|
52
|
+
end
|
53
|
+
|
54
|
+
def context
|
55
|
+
env[Grape::Env::API_ENDPOINT]
|
56
|
+
end
|
57
|
+
|
57
58
|
def response
|
58
59
|
return @app_response if @app_response.is_a?(Rack::Response)
|
59
60
|
|
60
|
-
@app_response = Rack::Response
|
61
|
+
@app_response = Rack::Response[*@app_response]
|
61
62
|
end
|
62
63
|
|
63
64
|
def content_types
|
@@ -73,7 +74,15 @@ module Grape
|
|
73
74
|
end
|
74
75
|
|
75
76
|
def content_type
|
76
|
-
content_type_for(env[Grape::Env::API_FORMAT] || options[:format]) ||
|
77
|
+
content_type_for(env[Grape::Env::API_FORMAT] || options[:format]) || 'text/html'
|
78
|
+
end
|
79
|
+
|
80
|
+
def query_params
|
81
|
+
rack_request.GET
|
82
|
+
rescue Rack::QueryParser::ParamsTooDeepError
|
83
|
+
raise Grape::Exceptions::TooDeepParameters.new(Rack::Utils.param_depth_limit)
|
84
|
+
rescue Rack::Utils::ParameterTypeError
|
85
|
+
raise Grape::Exceptions::ConflictingTypes
|
77
86
|
end
|
78
87
|
|
79
88
|
private
|
@@ -90,6 +99,16 @@ module Grape
|
|
90
99
|
def content_types_indifferent_access
|
91
100
|
@content_types_indifferent_access ||= content_types.with_indifferent_access
|
92
101
|
end
|
102
|
+
|
103
|
+
def merge_default_options(options)
|
104
|
+
if respond_to?(:default_options)
|
105
|
+
default_options.deep_merge(options)
|
106
|
+
elsif self.class.const_defined?(:DEFAULT_OPTIONS)
|
107
|
+
self.class::DEFAULT_OPTIONS.deep_merge(options)
|
108
|
+
else
|
109
|
+
options
|
110
|
+
end
|
111
|
+
end
|
93
112
|
end
|
94
113
|
end
|
95
114
|
end
|
@@ -3,30 +3,22 @@
|
|
3
3
|
module Grape
|
4
4
|
module Middleware
|
5
5
|
class Error < Base
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
},
|
21
|
-
rescue_handlers: {}, # rescue handler blocks
|
22
|
-
base_only_rescue_handlers: {}, # rescue handler blocks rescuing only the base class
|
23
|
-
all_rescue_handler: nil # rescue handler block to rescue from all exceptions
|
24
|
-
}
|
25
|
-
end
|
26
|
-
|
27
|
-
def initialize(app, *options)
|
6
|
+
DEFAULT_OPTIONS = {
|
7
|
+
default_status: 500,
|
8
|
+
default_message: '',
|
9
|
+
format: :txt,
|
10
|
+
rescue_all: false,
|
11
|
+
rescue_grape_exceptions: false,
|
12
|
+
rescue_subclasses: true,
|
13
|
+
rescue_options: {
|
14
|
+
backtrace: false,
|
15
|
+
original_exception: false
|
16
|
+
}.freeze
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
def initialize(app, **options)
|
28
20
|
super
|
29
|
-
self.class.include(
|
21
|
+
self.class.include(options[:helpers]) if options[:helpers]
|
30
22
|
end
|
31
23
|
|
32
24
|
def call!(env)
|
@@ -39,7 +31,7 @@ module Grape
|
|
39
31
|
private
|
40
32
|
|
41
33
|
def rack_response(status, headers, message)
|
42
|
-
message = Rack::Utils.escape_html(message) if headers[Rack::CONTENT_TYPE] ==
|
34
|
+
message = Rack::Utils.escape_html(message) if headers[Rack::CONTENT_TYPE] == 'text/html'
|
43
35
|
Rack::Response.new(Array.wrap(message), Rack::Utils.status_code(status), Grape::Util::Header.new.merge(headers))
|
44
36
|
end
|
45
37
|
|
@@ -65,6 +57,7 @@ module Grape
|
|
65
57
|
|
66
58
|
def error_response(error = {})
|
67
59
|
status = error[:status] || options[:default_status]
|
60
|
+
env[Grape::Env::API_ENDPOINT].status(status) # error! may not have been called
|
68
61
|
message = error[:message] || options[:default_message]
|
69
62
|
headers = { Rack::CONTENT_TYPE => content_type }.tap do |h|
|
70
63
|
h.merge!(error[:headers]) if error[:headers].is_a?(Hash)
|
@@ -130,6 +123,7 @@ module Grape
|
|
130
123
|
end
|
131
124
|
|
132
125
|
def error!(message, status = options[:default_status], headers = {}, backtrace = [], original_exception = nil)
|
126
|
+
env[Grape::Env::API_ENDPOINT].status(status) # not error! inside route
|
133
127
|
rack_response(
|
134
128
|
status, headers.reverse_merge(Rack::CONTENT_TYPE => content_type),
|
135
129
|
format_message(message, backtrace, original_exception)
|