grape 0.14.0 → 0.15.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/CHANGELOG.md +32 -4
- data/Gemfile.lock +13 -13
- data/README.md +290 -12
- data/UPGRADING.md +68 -1
- data/gemfiles/rails_3.gemfile +1 -1
- data/lib/grape.rb +8 -2
- data/lib/grape/api.rb +40 -34
- data/lib/grape/dsl/configuration.rb +2 -115
- data/lib/grape/dsl/desc.rb +101 -0
- data/lib/grape/dsl/headers.rb +16 -0
- data/lib/grape/dsl/helpers.rb +5 -9
- data/lib/grape/dsl/inside_route.rb +3 -11
- data/lib/grape/dsl/logger.rb +20 -0
- data/lib/grape/dsl/parameters.rb +12 -10
- data/lib/grape/dsl/request_response.rb +17 -4
- data/lib/grape/dsl/routing.rb +24 -7
- data/lib/grape/dsl/settings.rb +8 -2
- data/lib/grape/endpoint.rb +30 -26
- data/lib/grape/error_formatter.rb +31 -0
- data/lib/grape/error_formatter/base.rb +0 -28
- data/lib/grape/error_formatter/json.rb +13 -2
- data/lib/grape/error_formatter/txt.rb +3 -1
- data/lib/grape/error_formatter/xml.rb +3 -1
- data/lib/grape/exceptions/base.rb +11 -4
- data/lib/grape/exceptions/incompatible_option_values.rb +1 -1
- data/lib/grape/exceptions/invalid_accept_header.rb +1 -1
- data/lib/grape/exceptions/invalid_formatter.rb +1 -1
- data/lib/grape/exceptions/invalid_message_body.rb +1 -1
- data/lib/grape/exceptions/invalid_version_header.rb +1 -1
- data/lib/grape/exceptions/invalid_versioner_option.rb +1 -1
- data/lib/grape/exceptions/invalid_with_option_for_represent.rb +1 -1
- data/lib/grape/exceptions/method_not_allowed.rb +10 -0
- data/lib/grape/exceptions/missing_group_type.rb +1 -1
- data/lib/grape/exceptions/missing_mime_type.rb +1 -1
- data/lib/grape/exceptions/missing_option.rb +1 -1
- data/lib/grape/exceptions/missing_vendor_option.rb +1 -1
- data/lib/grape/exceptions/unknown_options.rb +1 -1
- data/lib/grape/exceptions/unknown_parameter.rb +1 -1
- data/lib/grape/exceptions/unknown_validator.rb +1 -1
- data/lib/grape/exceptions/unsupported_group_type.rb +1 -1
- data/lib/grape/exceptions/validation.rb +2 -1
- data/lib/grape/formatter.rb +31 -0
- data/lib/grape/middleware/base.rb +28 -2
- data/lib/grape/middleware/error.rb +24 -1
- data/lib/grape/middleware/formatter.rb +4 -3
- data/lib/grape/middleware/versioner/param.rb +13 -2
- data/lib/grape/parser.rb +29 -0
- data/lib/grape/util/sendfile_response.rb +19 -0
- data/lib/grape/util/strict_hash_configuration.rb +1 -1
- data/lib/grape/validations/params_scope.rb +39 -9
- data/lib/grape/validations/types.rb +16 -0
- data/lib/grape/validations/validators/all_or_none.rb +1 -1
- data/lib/grape/validations/validators/allow_blank.rb +2 -2
- data/lib/grape/validations/validators/at_least_one_of.rb +1 -1
- data/lib/grape/validations/validators/base.rb +26 -0
- data/lib/grape/validations/validators/coerce.rb +16 -14
- data/lib/grape/validations/validators/default.rb +1 -1
- data/lib/grape/validations/validators/exactly_one_of.rb +10 -1
- data/lib/grape/validations/validators/mutual_exclusion.rb +1 -1
- data/lib/grape/validations/validators/presence.rb +1 -1
- data/lib/grape/validations/validators/regexp.rb +2 -2
- data/lib/grape/validations/validators/values.rb +2 -2
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/custom_validations_spec.rb +156 -21
- data/spec/grape/api/namespace_parameters_in_route_spec.rb +38 -0
- data/spec/grape/api/optional_parameters_in_route_spec.rb +43 -0
- data/spec/grape/api/required_parameters_in_route_spec.rb +37 -0
- data/spec/grape/api_spec.rb +118 -60
- data/spec/grape/dsl/configuration_spec.rb +0 -75
- data/spec/grape/dsl/desc_spec.rb +77 -0
- data/spec/grape/dsl/headers_spec.rb +32 -0
- data/spec/grape/dsl/inside_route_spec.rb +0 -18
- data/spec/grape/dsl/logger_spec.rb +26 -0
- data/spec/grape/dsl/parameters_spec.rb +13 -7
- data/spec/grape/dsl/request_response_spec.rb +17 -3
- data/spec/grape/dsl/routing_spec.rb +8 -1
- data/spec/grape/dsl/settings_spec.rb +42 -0
- data/spec/grape/endpoint_spec.rb +60 -9
- data/spec/grape/exceptions/validation_errors_spec.rb +2 -2
- data/spec/grape/exceptions/validation_spec.rb +7 -0
- data/spec/grape/integration/rack_sendfile_spec.rb +44 -0
- data/spec/grape/middleware/base_spec.rb +100 -0
- data/spec/grape/middleware/exception_spec.rb +1 -2
- data/spec/grape/middleware/formatter_spec.rb +12 -2
- data/spec/grape/middleware/versioner/accept_version_header_spec.rb +1 -1
- data/spec/grape/middleware/versioner/header_spec.rb +11 -1
- data/spec/grape/middleware/versioner/param_spec.rb +105 -1
- data/spec/grape/validations/params_scope_spec.rb +77 -0
- data/spec/grape/validations/validators/allow_blank_spec.rb +277 -0
- data/spec/grape/validations/validators/coerce_spec.rb +91 -0
- data/spec/grape/validations/validators/default_spec.rb +6 -0
- data/spec/grape/validations/validators/presence_spec.rb +27 -0
- data/spec/grape/validations/validators/regexp_spec.rb +36 -0
- data/spec/grape/validations/validators/values_spec.rb +44 -0
- data/spec/grape/validations_spec.rb +149 -4
- data/spec/spec_helper.rb +1 -0
- metadata +26 -5
- data/lib/grape/formatter/base.rb +0 -31
- data/lib/grape/parser/base.rb +0 -29
- data/pkg/grape-0.13.0.gem +0 -0
@@ -0,0 +1,31 @@
|
|
1
|
+
module Grape
|
2
|
+
module ErrorFormatter
|
3
|
+
class << self
|
4
|
+
def builtin_formatters
|
5
|
+
{
|
6
|
+
serializable_hash: Grape::ErrorFormatter::Json,
|
7
|
+
json: Grape::ErrorFormatter::Json,
|
8
|
+
jsonapi: Grape::ErrorFormatter::Json,
|
9
|
+
txt: Grape::ErrorFormatter::Txt,
|
10
|
+
xml: Grape::ErrorFormatter::Xml
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def formatters(options)
|
15
|
+
builtin_formatters.merge(options[:error_formatters] || {})
|
16
|
+
end
|
17
|
+
|
18
|
+
def formatter_for(api_format, options = {})
|
19
|
+
spec = formatters(options)[api_format]
|
20
|
+
case spec
|
21
|
+
when nil
|
22
|
+
options[:default_error_formatter] || Grape::ErrorFormatter::Txt
|
23
|
+
when Symbol
|
24
|
+
method(spec)
|
25
|
+
else
|
26
|
+
spec
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,34 +1,6 @@
|
|
1
1
|
module Grape
|
2
2
|
module ErrorFormatter
|
3
3
|
module Base
|
4
|
-
class << self
|
5
|
-
FORMATTERS = {
|
6
|
-
serializable_hash: Grape::ErrorFormatter::Json,
|
7
|
-
json: Grape::ErrorFormatter::Json,
|
8
|
-
jsonapi: Grape::ErrorFormatter::Json,
|
9
|
-
txt: Grape::ErrorFormatter::Txt,
|
10
|
-
xml: Grape::ErrorFormatter::Xml
|
11
|
-
}
|
12
|
-
|
13
|
-
def formatters(options)
|
14
|
-
FORMATTERS.merge(options[:error_formatters] || {})
|
15
|
-
end
|
16
|
-
|
17
|
-
def formatter_for(api_format, options = {})
|
18
|
-
spec = formatters(options)[api_format]
|
19
|
-
case spec
|
20
|
-
when nil
|
21
|
-
options[:default_error_formatter] || Grape::ErrorFormatter::Txt
|
22
|
-
when Symbol
|
23
|
-
method(spec)
|
24
|
-
else
|
25
|
-
spec
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
module_function
|
31
|
-
|
32
4
|
def present(message, env)
|
33
5
|
present_options = {}
|
34
6
|
present_options[:with] = message.delete(:with) if message.is_a?(Hash)
|
@@ -1,16 +1,27 @@
|
|
1
1
|
module Grape
|
2
2
|
module ErrorFormatter
|
3
3
|
module Json
|
4
|
+
extend Base
|
5
|
+
|
4
6
|
class << self
|
5
7
|
def call(message, backtrace, options = {}, env = nil)
|
6
|
-
|
8
|
+
result = wrap_message(present(message, env))
|
7
9
|
|
8
|
-
result = message.is_a?(String) ? { error: message } : message
|
9
10
|
if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
|
10
11
|
result = result.merge(backtrace: backtrace)
|
11
12
|
end
|
12
13
|
MultiJson.dump(result)
|
13
14
|
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def wrap_message(message)
|
19
|
+
if message.is_a?(Exceptions::ValidationErrors) || message.is_a?(Hash)
|
20
|
+
message
|
21
|
+
else
|
22
|
+
{ error: message }
|
23
|
+
end
|
24
|
+
end
|
14
25
|
end
|
15
26
|
end
|
16
27
|
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module Grape
|
2
2
|
module ErrorFormatter
|
3
3
|
module Txt
|
4
|
+
extend Base
|
5
|
+
|
4
6
|
class << self
|
5
7
|
def call(message, backtrace, options = {}, env = nil)
|
6
|
-
message =
|
8
|
+
message = present(message, env)
|
7
9
|
|
8
10
|
result = message.is_a?(Hash) ? MultiJson.dump(message) : message
|
9
11
|
if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module Grape
|
2
2
|
module ErrorFormatter
|
3
3
|
module Xml
|
4
|
+
extend Base
|
5
|
+
|
4
6
|
class << self
|
5
7
|
def call(message, backtrace, options = {}, env = nil)
|
6
|
-
message =
|
8
|
+
message = present(message, env)
|
7
9
|
|
8
10
|
result = message.is_a?(Hash) ? message : { message: message }
|
9
11
|
if (options[:rescue_options] || {})[:backtrace] && backtrace && !backtrace.empty?
|
@@ -38,15 +38,15 @@ module Grape
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def problem(key, attributes)
|
41
|
-
translate_message("#{key}.problem", attributes)
|
41
|
+
translate_message("#{key}.problem".to_sym, attributes)
|
42
42
|
end
|
43
43
|
|
44
44
|
def summary(key, attributes)
|
45
|
-
translate_message("#{key}.summary", attributes)
|
45
|
+
translate_message("#{key}.summary".to_sym, attributes)
|
46
46
|
end
|
47
47
|
|
48
48
|
def resolution(key, attributes)
|
49
|
-
translate_message("#{key}.resolution", attributes)
|
49
|
+
translate_message("#{key}.resolution".to_sym, attributes)
|
50
50
|
end
|
51
51
|
|
52
52
|
def translate_attributes(keys, options = {})
|
@@ -60,7 +60,14 @@ module Grape
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def translate_message(key, options = {})
|
63
|
-
|
63
|
+
case key
|
64
|
+
when Symbol
|
65
|
+
translate("#{BASE_MESSAGES_KEY}.#{key}", options.reverse_merge(default: ''))
|
66
|
+
when Proc
|
67
|
+
key.call
|
68
|
+
else
|
69
|
+
key
|
70
|
+
end
|
64
71
|
end
|
65
72
|
|
66
73
|
def translate(key, options = {})
|
@@ -3,7 +3,7 @@ module Grape
|
|
3
3
|
module Exceptions
|
4
4
|
class IncompatibleOptionValues < Base
|
5
5
|
def initialize(option1, value1, option2, value2)
|
6
|
-
super(message: compose_message(
|
6
|
+
super(message: compose_message(:incompatible_option_values, option1: option1, value1: value1, option2: option2, value2: value2))
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -3,7 +3,7 @@ module Grape
|
|
3
3
|
module Exceptions
|
4
4
|
class InvalidAcceptHeader < Base
|
5
5
|
def initialize(message, headers)
|
6
|
-
super(message: compose_message(
|
6
|
+
super(message: compose_message(:invalid_accept_header, message: message), status: 406, headers: headers)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -3,7 +3,7 @@ module Grape
|
|
3
3
|
module Exceptions
|
4
4
|
class InvalidFormatter < Base
|
5
5
|
def initialize(klass, to_format)
|
6
|
-
super(message: compose_message(
|
6
|
+
super(message: compose_message(:invalid_formatter, klass: klass, to_format: to_format))
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -3,7 +3,7 @@ module Grape
|
|
3
3
|
module Exceptions
|
4
4
|
class InvalidMessageBody < Base
|
5
5
|
def initialize(body_format)
|
6
|
-
super(message: compose_message(
|
6
|
+
super(message: compose_message(:invalid_message_body, body_format: body_format), status: 400)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -3,7 +3,7 @@ module Grape
|
|
3
3
|
module Exceptions
|
4
4
|
class InvalidVersionHeader < Base
|
5
5
|
def initialize(message, headers)
|
6
|
-
super(message: compose_message(
|
6
|
+
super(message: compose_message(:invalid_version_header, message: message), status: 406, headers: headers)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -3,7 +3,7 @@ module Grape
|
|
3
3
|
module Exceptions
|
4
4
|
class InvalidVersionerOption < Base
|
5
5
|
def initialize(strategy)
|
6
|
-
super(message: compose_message(
|
6
|
+
super(message: compose_message(:invalid_versioner_option, strategy: strategy))
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -3,7 +3,7 @@ module Grape
|
|
3
3
|
module Exceptions
|
4
4
|
class UnknownValidator < Base
|
5
5
|
def initialize(validator_type)
|
6
|
-
super(message: compose_message(
|
6
|
+
super(message: compose_message(:unknown_validator, validator_type: validator_type))
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -4,11 +4,12 @@ module Grape
|
|
4
4
|
module Exceptions
|
5
5
|
class Validation < Grape::Exceptions::Base
|
6
6
|
attr_accessor :params
|
7
|
+
attr_accessor :message_key
|
7
8
|
|
8
9
|
def initialize(args = {})
|
9
10
|
fail 'Params are missing:' unless args.key? :params
|
10
11
|
@params = args[:params]
|
11
|
-
args[:message] = translate_message(args[:
|
12
|
+
args[:message] = translate_message(args[:message]) if args.key? :message
|
12
13
|
super
|
13
14
|
end
|
14
15
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Grape
|
2
|
+
module Formatter
|
3
|
+
class << self
|
4
|
+
def builtin_formmaters
|
5
|
+
{
|
6
|
+
json: Grape::Formatter::Json,
|
7
|
+
jsonapi: Grape::Formatter::Json,
|
8
|
+
serializable_hash: Grape::Formatter::SerializableHash,
|
9
|
+
txt: Grape::Formatter::Txt,
|
10
|
+
xml: Grape::Formatter::Xml
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def formatters(options)
|
15
|
+
builtin_formmaters.merge(options[:formatters] || {})
|
16
|
+
end
|
17
|
+
|
18
|
+
def formatter_for(api_format, options = {})
|
19
|
+
spec = formatters(options)[api_format]
|
20
|
+
case spec
|
21
|
+
when nil
|
22
|
+
->(obj, _env) { obj }
|
23
|
+
when Symbol
|
24
|
+
method(spec)
|
25
|
+
else
|
26
|
+
spec
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,9 +1,13 @@
|
|
1
|
+
require 'grape/dsl/headers'
|
2
|
+
|
1
3
|
module Grape
|
2
4
|
module Middleware
|
3
5
|
class Base
|
4
6
|
attr_reader :app, :env, :options
|
5
7
|
TEXT_HTML = 'text/html'.freeze
|
6
8
|
|
9
|
+
include Grape::DSL::Headers
|
10
|
+
|
7
11
|
# @param [Rack Application] app The standard argument for a Rack middleware.
|
8
12
|
# @param [Hash] options A hash of options, simply stored for use by subclasses.
|
9
13
|
def initialize(app, options = {})
|
@@ -22,8 +26,20 @@ module Grape
|
|
22
26
|
def call!(env)
|
23
27
|
@env = env
|
24
28
|
before
|
25
|
-
|
26
|
-
|
29
|
+
begin
|
30
|
+
@app_response = @app.call(@env)
|
31
|
+
ensure
|
32
|
+
begin
|
33
|
+
after_response = after
|
34
|
+
rescue StandardError => e
|
35
|
+
warn "caught error of type #{e.class} in after callback inside #{self.class.name} : #{e.message}"
|
36
|
+
raise e
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
response = after_response || @app_response
|
41
|
+
merge_headers response
|
42
|
+
response
|
27
43
|
end
|
28
44
|
|
29
45
|
# @abstract
|
@@ -61,6 +77,16 @@ module Grape
|
|
61
77
|
end
|
62
78
|
types_without_params
|
63
79
|
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def merge_headers(response)
|
84
|
+
return unless headers.is_a?(Hash)
|
85
|
+
case response
|
86
|
+
when Rack::Response then response.headers.merge!(headers)
|
87
|
+
when Array then response[1].merge!(headers)
|
88
|
+
end
|
89
|
+
end
|
64
90
|
end
|
65
91
|
end
|
66
92
|
end
|
@@ -22,6 +22,8 @@ module Grape
|
|
22
22
|
def call!(env)
|
23
23
|
@env = env
|
24
24
|
|
25
|
+
inject_helpers!
|
26
|
+
|
25
27
|
begin
|
26
28
|
error_response(catch(:error) do
|
27
29
|
return @app.call(@env)
|
@@ -43,6 +45,14 @@ module Grape
|
|
43
45
|
handler = options[:rescue_handlers].find(-> { [] }) { |error, _| klass <= error }[1]
|
44
46
|
handler ||= options[:base_only_rescue_handlers][klass]
|
45
47
|
handler ||= options[:all_rescue_handler]
|
48
|
+
with_option = options[:rescue_options][:with]
|
49
|
+
if with_option.instance_of?(Symbol)
|
50
|
+
if respond_to?(with_option)
|
51
|
+
handler ||= self.class.instance_method(with_option).bind(self)
|
52
|
+
else
|
53
|
+
fail NoMethodError, "undefined method `#{with_option}'"
|
54
|
+
end
|
55
|
+
end
|
46
56
|
handler
|
47
57
|
end
|
48
58
|
|
@@ -59,6 +69,19 @@ module Grape
|
|
59
69
|
end
|
60
70
|
end
|
61
71
|
|
72
|
+
def inject_helpers!
|
73
|
+
return if helpers_available?
|
74
|
+
endpoint = @env['api.endpoint']
|
75
|
+
self.class.instance_eval do
|
76
|
+
include endpoint.send(:helpers)
|
77
|
+
end if endpoint.is_a?(Grape::Endpoint)
|
78
|
+
@helpers = true
|
79
|
+
end
|
80
|
+
|
81
|
+
def helpers_available?
|
82
|
+
@helpers
|
83
|
+
end
|
84
|
+
|
62
85
|
def error!(message, status = options[:default_status], headers = {}, backtrace = [])
|
63
86
|
headers = headers.reverse_merge(Grape::Http::Headers::CONTENT_TYPE => content_type)
|
64
87
|
rack_response(format_message(message, backtrace), status, headers)
|
@@ -84,7 +107,7 @@ module Grape
|
|
84
107
|
|
85
108
|
def format_message(message, backtrace)
|
86
109
|
format = env[Grape::Env::API_FORMAT] || options[:format]
|
87
|
-
formatter = Grape::ErrorFormatter
|
110
|
+
formatter = Grape::ErrorFormatter.formatter_for(format, options)
|
88
111
|
throw :error, status: 406, message: "The requested format '#{format}' is not supported." unless formatter
|
89
112
|
formatter.call(message, backtrace, options, env)
|
90
113
|
end
|