grape 0.1.4 → 0.1.5
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.
- data/.gitignore +1 -0
- data/Gemfile.lock +21 -20
- data/README.markdown +21 -0
- data/Rakefile +1 -2
- data/grape.gemspec +4 -2
- data/lib/grape.rb +1 -0
- data/lib/grape/api.rb +49 -3
- data/lib/grape/middleware/auth/digest.rb +30 -0
- data/lib/grape/middleware/auth/oauth2.rb +33 -15
- data/lib/grape/middleware/base.rb +48 -2
- data/lib/grape/middleware/error.rb +54 -5
- data/lib/grape/middleware/formatter.rb +2 -36
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api_spec.rb +138 -1
- data/spec/grape/endpoint_spec.rb +0 -2
- data/spec/grape/middleware/auth/digest_spec.rb +49 -0
- data/spec/grape/middleware/auth/oauth2_spec.rb +6 -4
- data/spec/grape/middleware/error_spec.rb +1 -1
- data/spec/grape/middleware/exception_spec.rb +120 -0
- metadata +42 -21
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
grape (0.1.
|
4
|
+
grape (0.1.5)
|
5
5
|
multi_json
|
6
6
|
multi_xml
|
7
7
|
rack
|
@@ -11,41 +11,42 @@ PATH
|
|
11
11
|
GEM
|
12
12
|
remote: http://rubygems.org/
|
13
13
|
specs:
|
14
|
+
ZenTest (4.5.0)
|
14
15
|
diff-lcs (1.1.2)
|
15
|
-
json_pure (1.
|
16
|
+
json_pure (1.5.2)
|
16
17
|
maruku (0.6.0)
|
17
18
|
syntax (>= 1.0.0)
|
18
|
-
|
19
|
-
rake
|
20
|
-
multi_json (0.0.5)
|
19
|
+
multi_json (1.0.3)
|
21
20
|
multi_xml (0.2.2)
|
22
|
-
rack (1.
|
23
|
-
rack-jsonp (1.
|
21
|
+
rack (1.3.0)
|
22
|
+
rack-jsonp (1.2.0)
|
24
23
|
rack
|
25
|
-
rack-mount (0.
|
24
|
+
rack-mount (0.8.1)
|
26
25
|
rack (>= 1.0.0)
|
27
|
-
rack-test (0.
|
26
|
+
rack-test (0.6.0)
|
28
27
|
rack (>= 1.0)
|
29
|
-
rake (0.
|
30
|
-
rspec (2.
|
31
|
-
rspec-core (~> 2.
|
32
|
-
rspec-expectations (~> 2.
|
33
|
-
rspec-mocks (~> 2.
|
34
|
-
rspec-core (2.
|
35
|
-
rspec-expectations (2.
|
28
|
+
rake (0.9.2)
|
29
|
+
rspec (2.6.0)
|
30
|
+
rspec-core (~> 2.6.0)
|
31
|
+
rspec-expectations (~> 2.6.0)
|
32
|
+
rspec-mocks (~> 2.6.0)
|
33
|
+
rspec-core (2.6.4)
|
34
|
+
rspec-expectations (2.6.0)
|
36
35
|
diff-lcs (~> 1.1.2)
|
37
|
-
rspec-mocks (2.
|
36
|
+
rspec-mocks (2.6.0)
|
38
37
|
syntax (1.0.0)
|
39
|
-
yard (0.
|
38
|
+
yard (0.7.1)
|
40
39
|
|
41
40
|
PLATFORMS
|
42
41
|
ruby
|
43
42
|
|
44
43
|
DEPENDENCIES
|
44
|
+
ZenTest
|
45
|
+
bundler
|
45
46
|
grape!
|
46
47
|
json_pure
|
47
48
|
maruku
|
48
|
-
mg
|
49
49
|
rack-test
|
50
|
-
|
50
|
+
rake
|
51
|
+
rspec (~> 2.6.0)
|
51
52
|
yard
|
data/README.markdown
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# Grape
|
2
|
+
[![Build Status](http://travis-ci.org/intridea/grape.png)](http://travis-ci.org/intridea/grape)
|
2
3
|
|
3
4
|
Grape is a REST-like API micro-framework for Ruby. It is built to complement existing web application frameworks such as Rails and Sinatra by providing a simple DSL to easily provide APIs. It has built-in support for common conventions such as multiple formats, subdomain/prefix restriction, and versioning.
|
4
5
|
|
@@ -62,6 +63,26 @@ And would respond to the following routes:
|
|
62
63
|
|
63
64
|
Serialization takes place automatically. For more detailed usage information, please visit the [Grape Wiki](http://github.com/intridea/grape/wiki).
|
64
65
|
|
66
|
+
## Raising Errors
|
67
|
+
|
68
|
+
You can raise errors explicitly.
|
69
|
+
|
70
|
+
error!("Access Denied", 401)
|
71
|
+
|
72
|
+
You can also return JSON formatted objects explicitly by raising error! and passing a hash instead of a message.
|
73
|
+
|
74
|
+
error!({ "error" => "unexpected error", "detail" => "missing widget" }, 500)
|
75
|
+
|
76
|
+
## Exception Handling
|
77
|
+
|
78
|
+
Grape can be told to rescue certain (or all) exceptions in your
|
79
|
+
application and instead display them in text or json form. To do this,
|
80
|
+
you simply use the `rescue_from` method inside your API declaration:
|
81
|
+
|
82
|
+
class Twitter::API < Grape::API
|
83
|
+
rescue_from ArgumentError, NotImplementedError # :all for all errors
|
84
|
+
end
|
85
|
+
|
65
86
|
## Note on Patches/Pull Requests
|
66
87
|
|
67
88
|
* Fork the project.
|
data/Rakefile
CHANGED
data/grape.gemspec
CHANGED
@@ -19,12 +19,14 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.add_runtime_dependency 'multi_json'
|
20
20
|
s.add_runtime_dependency 'multi_xml'
|
21
21
|
|
22
|
-
s.add_development_dependency '
|
22
|
+
s.add_development_dependency 'rake'
|
23
23
|
s.add_development_dependency 'maruku'
|
24
24
|
s.add_development_dependency 'yard'
|
25
25
|
s.add_development_dependency 'rack-test'
|
26
|
-
s.add_development_dependency 'rspec', '~> 2.
|
26
|
+
s.add_development_dependency 'rspec', '~> 2.6.0'
|
27
27
|
s.add_development_dependency 'json_pure'
|
28
|
+
s.add_development_dependency 'ZenTest'
|
29
|
+
s.add_development_dependency 'bundler'
|
28
30
|
|
29
31
|
s.files = `git ls-files`.split("\n")
|
30
32
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/lib/grape.rb
CHANGED
data/lib/grape/api.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rack/mount'
|
2
2
|
require 'rack/auth/basic'
|
3
|
+
require 'rack/auth/digest/md5'
|
3
4
|
require 'logger'
|
4
5
|
|
5
6
|
module Grape
|
@@ -50,7 +51,7 @@ module Grape
|
|
50
51
|
def prefix(prefix = nil)
|
51
52
|
prefix ? set(:root_prefix, prefix) : settings[:root_prefix]
|
52
53
|
end
|
53
|
-
|
54
|
+
|
54
55
|
# Specify an API version.
|
55
56
|
#
|
56
57
|
# @example API with legacy support.
|
@@ -79,6 +80,39 @@ module Grape
|
|
79
80
|
new_format ? set(:default_format, new_format.to_sym) : settings[:default_format]
|
80
81
|
end
|
81
82
|
|
83
|
+
# Specify the format for error messages.
|
84
|
+
# May be `:json` or `:txt` (default).
|
85
|
+
def error_format(new_format = nil)
|
86
|
+
new_format ? set(:error_format, new_format.to_sym) : settings[:error_format]
|
87
|
+
end
|
88
|
+
|
89
|
+
# Specify the default status code for errors.
|
90
|
+
def default_error_status(new_status = nil)
|
91
|
+
new_status ? set(:default_error_status, new_status) : settings[:default_error_status]
|
92
|
+
end
|
93
|
+
|
94
|
+
# Allows you to rescue certain exceptions that occur to return
|
95
|
+
# a grape error rather than raising all the way to the
|
96
|
+
# server level.
|
97
|
+
#
|
98
|
+
# @example Rescue from custom exceptions
|
99
|
+
# class ExampleAPI < Grape::API
|
100
|
+
# class CustomError < StandardError; end
|
101
|
+
#
|
102
|
+
# rescue_from CustomError
|
103
|
+
# end
|
104
|
+
#
|
105
|
+
# @overload rescue_from(*exception_classes, options = {})
|
106
|
+
# @param [Array] exception_classes A list of classes that you want to rescue, or
|
107
|
+
# the symbol :all to rescue from all exceptions.
|
108
|
+
# @param [Hash] options Options for the rescue usage.
|
109
|
+
# @option options [Boolean] :backtrace Include a backtrace in the rescue response.
|
110
|
+
def rescue_from(*args)
|
111
|
+
set(:rescue_options, args.pop) if args.last.is_a?(Hash)
|
112
|
+
set(:rescue_all, true) and return if args.include?(:all)
|
113
|
+
set(:rescued_errors, args)
|
114
|
+
end
|
115
|
+
|
82
116
|
# Add helper methods that will be accessible from any
|
83
117
|
# endpoint within this namespace (and child namespaces).
|
84
118
|
#
|
@@ -105,7 +139,7 @@ module Grape
|
|
105
139
|
end
|
106
140
|
|
107
141
|
# Add an authentication type to the API. Currently
|
108
|
-
# only `:http_basic`
|
142
|
+
# only `:http_basic`, `:http_digest` and `:oauth2` are supported.
|
109
143
|
def auth(type = nil, options = {}, &block)
|
110
144
|
if type
|
111
145
|
set(:auth, {:type => type.to_sym, :proc => block}.merge(options))
|
@@ -122,6 +156,12 @@ module Grape
|
|
122
156
|
options[:realm] ||= "API Authorization"
|
123
157
|
auth :http_basic, options, &block
|
124
158
|
end
|
159
|
+
|
160
|
+
def http_digest(options = {}, &block)
|
161
|
+
options[:realm] ||= "API Authorization"
|
162
|
+
options[:opaque] ||= "secret"
|
163
|
+
auth :http_digest, options, &block
|
164
|
+
end
|
125
165
|
|
126
166
|
# Defines a route that will be recognized
|
127
167
|
# by the Grape API.
|
@@ -217,8 +257,14 @@ module Grape
|
|
217
257
|
|
218
258
|
def build_endpoint(&block)
|
219
259
|
b = Rack::Builder.new
|
220
|
-
b.use Grape::Middleware::Error
|
260
|
+
b.use Grape::Middleware::Error,
|
261
|
+
:default_status => settings[:default_error_status] || 403,
|
262
|
+
:rescue_all => settings[:rescue_all],
|
263
|
+
:rescued_errors => settings[:rescued_errors],
|
264
|
+
:format => settings[:error_format] || :txt,
|
265
|
+
:rescue_options => settings[:rescue_options]
|
221
266
|
b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
|
267
|
+
b.use Rack::Auth::Digest::MD5, settings[:auth][:realm], settings[:auth][:opaque], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_digest
|
222
268
|
b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix
|
223
269
|
b.use Grape::Middleware::Versioner, :versions => (version if version.is_a?(Array)) if version
|
224
270
|
b.use Grape::Middleware::Formatter, :default_format => default_format || :json
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rack/auth/digest/md5'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Middleware
|
5
|
+
module Auth
|
6
|
+
class Digest < Grape::Middleware::Base
|
7
|
+
attr_reader :authenticator
|
8
|
+
|
9
|
+
def initialize(app, options = {}, &authenticator)
|
10
|
+
super(app, options)
|
11
|
+
@authenticator = authenticator
|
12
|
+
end
|
13
|
+
|
14
|
+
def digest_request
|
15
|
+
Rack::Auth::Digest::Request.new(env)
|
16
|
+
end
|
17
|
+
|
18
|
+
def credentials
|
19
|
+
digest_request.provided?? digest_request.credentials : [nil, nil]
|
20
|
+
end
|
21
|
+
|
22
|
+
def before
|
23
|
+
unless authenticator.call(*credentials)
|
24
|
+
throw :error, :status => 401, :message => "API Authorization Failed."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,18 +1,42 @@
|
|
1
1
|
module Grape::Middleware::Auth
|
2
|
+
# OAuth 2.0 authorization for Grape APIs.
|
2
3
|
class OAuth2 < Grape::Middleware::Base
|
3
4
|
def default_options
|
4
5
|
{
|
5
6
|
:token_class => 'AccessToken',
|
6
|
-
:realm => 'OAuth API'
|
7
|
+
:realm => 'OAuth API',
|
8
|
+
:parameter => %w(bearer_token oauth_token),
|
9
|
+
:accepted_headers => %w(HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION),
|
10
|
+
:header => [/Bearer (.*)/i, /OAuth (.*)/i]
|
7
11
|
}
|
8
12
|
end
|
9
13
|
|
10
14
|
def before
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
+
verify_token(token_parameter || token_header)
|
16
|
+
end
|
17
|
+
|
18
|
+
def token_parameter
|
19
|
+
Array(options[:parameter]).each do |p|
|
20
|
+
return request[p] if request[p]
|
21
|
+
end
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def token_header
|
26
|
+
return false unless authorization_header
|
27
|
+
Array(options[:header]).each do |regexp|
|
28
|
+
if authorization_header =~ regexp
|
29
|
+
return $1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def authorization_header
|
36
|
+
options[:accepted_headers].each do |head|
|
37
|
+
return env[head] if env[head]
|
15
38
|
end
|
39
|
+
nil
|
16
40
|
end
|
17
41
|
|
18
42
|
def token_class
|
@@ -21,10 +45,10 @@ module Grape::Middleware::Auth
|
|
21
45
|
|
22
46
|
def verify_token(token)
|
23
47
|
if token = token_class.verify(token)
|
24
|
-
if token.expired?
|
48
|
+
if token.respond_to?(:expired?) && token.expired?
|
25
49
|
error_out(401, 'expired_token')
|
26
50
|
else
|
27
|
-
if token.permission_for?(env)
|
51
|
+
if !token.respond_to?(:permission_for?) || token.permission_for?(env)
|
28
52
|
env['api.token'] = token
|
29
53
|
else
|
30
54
|
error_out(403, 'insufficient_scope')
|
@@ -35,15 +59,9 @@ module Grape::Middleware::Auth
|
|
35
59
|
end
|
36
60
|
end
|
37
61
|
|
38
|
-
def parse_authorization_header
|
39
|
-
if env['Authorization'] =~ /oauth (.*)/i
|
40
|
-
$1
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
62
|
def error_out(status, error)
|
45
63
|
throw :error, {
|
46
|
-
:message =>
|
64
|
+
:message => error,
|
47
65
|
:status => status,
|
48
66
|
:headers => {
|
49
67
|
'WWW-Authenticate' => "OAuth realm='#{options[:realm]}', error='#{error}'"
|
@@ -52,4 +70,4 @@ module Grape::Middleware::Auth
|
|
52
70
|
end
|
53
71
|
end
|
54
72
|
end
|
55
|
-
|
73
|
+
|
@@ -2,7 +2,7 @@ module Grape
|
|
2
2
|
module Middleware
|
3
3
|
class Base
|
4
4
|
attr_reader :app, :env, :options
|
5
|
-
|
5
|
+
|
6
6
|
# @param [Rack Application] app The standard argument for a Rack middleware.
|
7
7
|
# @param [Hash] options A hash of options, simply stored for use by subclasses.
|
8
8
|
def initialize(app, options = {})
|
@@ -38,6 +38,52 @@ module Grape
|
|
38
38
|
def response
|
39
39
|
Rack::Response.new(@app_response)
|
40
40
|
end
|
41
|
+
|
42
|
+
|
43
|
+
module Formats
|
44
|
+
|
45
|
+
CONTENT_TYPES = {
|
46
|
+
:xml => 'application/xml',
|
47
|
+
:json => 'application/json',
|
48
|
+
:atom => 'application/atom+xml',
|
49
|
+
:rss => 'application/rss+xml',
|
50
|
+
:txt => 'text/plain'
|
51
|
+
}
|
52
|
+
FORMATTERS = {
|
53
|
+
:json => :encode_json,
|
54
|
+
:txt => :encode_txt,
|
55
|
+
}
|
56
|
+
|
57
|
+
def formatters
|
58
|
+
FORMATTERS.merge(options[:formatters] || {})
|
59
|
+
end
|
60
|
+
|
61
|
+
def content_types
|
62
|
+
CONTENT_TYPES.merge(options[:content_types] || {})
|
63
|
+
end
|
64
|
+
|
65
|
+
def content_type
|
66
|
+
content_types[options[:format]] || 'text/html'
|
67
|
+
end
|
68
|
+
|
69
|
+
def mime_types
|
70
|
+
content_types.invert
|
71
|
+
end
|
72
|
+
|
73
|
+
def formatter_for(api_format)
|
74
|
+
spec = formatters[api_format]
|
75
|
+
case spec
|
76
|
+
when nil
|
77
|
+
lambda { |obj| obj }
|
78
|
+
when Symbol
|
79
|
+
method(spec)
|
80
|
+
else
|
81
|
+
spec
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
41
87
|
end
|
42
88
|
end
|
43
|
-
end
|
89
|
+
end
|
@@ -1,21 +1,70 @@
|
|
1
1
|
require 'grape/middleware/base'
|
2
|
+
require 'multi_json'
|
2
3
|
|
3
4
|
module Grape
|
4
5
|
module Middleware
|
5
6
|
class Error < Base
|
7
|
+
include Formats
|
8
|
+
|
9
|
+
def default_options
|
10
|
+
{
|
11
|
+
:default_status => 403, # default status returned on error
|
12
|
+
:default_message => "",
|
13
|
+
:format => :txt,
|
14
|
+
:formatters => {},
|
15
|
+
|
16
|
+
:rescue_all => false, # true to rescue all exceptions
|
17
|
+
:rescue_options => {:backtrace => false}, # true to display backtrace
|
18
|
+
:rescued_errors => []
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def encode_json(message, backtrace)
|
23
|
+
result = message.is_a?(Hash) ? message : { :error => message }
|
24
|
+
if (options[:rescue_options] || {})[:backtrace] && backtrace && ! backtrace.empty?
|
25
|
+
result = result.merge({ :backtrace => backtrace })
|
26
|
+
end
|
27
|
+
MultiJson.encode(result)
|
28
|
+
end
|
29
|
+
|
30
|
+
def encode_txt(message, backtrace)
|
31
|
+
result = message.is_a?(Hash) ? MultiJson.encode(message) : message
|
32
|
+
if (options[:rescue_options] || {})[:backtrace] && backtrace && ! backtrace.empty?
|
33
|
+
result += "\r\n "
|
34
|
+
result += backtrace.join("\r\n ")
|
35
|
+
end
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
6
39
|
def call!(env)
|
7
40
|
@env = env
|
8
|
-
|
9
|
-
|
41
|
+
|
42
|
+
begin
|
43
|
+
error_response(catch(:error){
|
44
|
+
return @app.call(@env)
|
45
|
+
})
|
46
|
+
rescue Exception => e
|
47
|
+
raise unless options[:rescue_all] || (options[:rescued_errors] || []).include?(e.class)
|
48
|
+
error_response({ :message => e.message, :backtrace => e.backtrace })
|
10
49
|
end
|
11
50
|
|
12
|
-
result ||= {}
|
13
|
-
result.is_a?(Hash) ? error_response(result) : result
|
14
51
|
end
|
15
52
|
|
16
53
|
def error_response(error = {})
|
17
|
-
|
54
|
+
status = error[:status] || options[:default_status]
|
55
|
+
message = error[:message] || options[:default_message]
|
56
|
+
headers = {'Content-Type' => content_type}
|
57
|
+
headers.merge!(error[:headers]) if error[:headers].is_a?(Hash)
|
58
|
+
backtrace = error[:backtrace] || []
|
59
|
+
Rack::Response.new([format_message(message, backtrace, status)], status, headers).finish
|
18
60
|
end
|
61
|
+
|
62
|
+
def format_message(message, backtrace, status)
|
63
|
+
formatter = formatter_for(options[:format])
|
64
|
+
throw :error, :status => 406, :message => "The requested format #{options[:format]} is not supported." unless formatter
|
65
|
+
formatter.call(message, backtrace)
|
66
|
+
end
|
67
|
+
|
19
68
|
end
|
20
69
|
end
|
21
70
|
end
|
@@ -4,18 +4,8 @@ require 'multi_json'
|
|
4
4
|
module Grape
|
5
5
|
module Middleware
|
6
6
|
class Formatter < Base
|
7
|
-
|
8
|
-
|
9
|
-
:json => 'application/json',
|
10
|
-
:atom => 'application/atom+xml',
|
11
|
-
:rss => 'application/rss+xml',
|
12
|
-
:txt => 'text/plain'
|
13
|
-
}
|
14
|
-
FORMATTERS = {
|
15
|
-
:json => :encode_json,
|
16
|
-
:txt => :encode_txt,
|
17
|
-
}
|
18
|
-
|
7
|
+
include Formats
|
8
|
+
|
19
9
|
def default_options
|
20
10
|
{
|
21
11
|
:default_format => :txt,
|
@@ -24,18 +14,6 @@ module Grape
|
|
24
14
|
}
|
25
15
|
end
|
26
16
|
|
27
|
-
def content_types
|
28
|
-
CONTENT_TYPES.merge(options[:content_types])
|
29
|
-
end
|
30
|
-
|
31
|
-
def formatters
|
32
|
-
FORMATTERS.merge(options[:formatters])
|
33
|
-
end
|
34
|
-
|
35
|
-
def mime_types
|
36
|
-
content_types.invert
|
37
|
-
end
|
38
|
-
|
39
17
|
def headers
|
40
18
|
env.dup.inject({}){|h,(k,v)| h[k.downcase] = v; h}
|
41
19
|
end
|
@@ -92,18 +70,6 @@ module Grape
|
|
92
70
|
Rack::Response.new(bodymap, status, headers).to_a
|
93
71
|
end
|
94
72
|
|
95
|
-
def formatter_for(api_format)
|
96
|
-
spec = formatters[api_format]
|
97
|
-
case spec
|
98
|
-
when nil
|
99
|
-
lambda { |obj| obj }
|
100
|
-
when Symbol
|
101
|
-
method(spec)
|
102
|
-
else
|
103
|
-
spec
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
73
|
def encode_json(object)
|
108
74
|
if object.respond_to? :serializable_hash
|
109
75
|
MultiJson.encode(object.serializable_hash)
|
data/lib/grape/version.rb
CHANGED
data/spec/grape/api_spec.rb
CHANGED
@@ -20,7 +20,7 @@ describe Grape::API do
|
|
20
20
|
last_response.status.should eql 404
|
21
21
|
end
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
describe '.version' do
|
25
25
|
it 'should set the API version' do
|
26
26
|
subject.version 'v1'
|
@@ -276,6 +276,35 @@ describe Grape::API do
|
|
276
276
|
last_response.body.should eql 'Created'
|
277
277
|
end
|
278
278
|
end
|
279
|
+
|
280
|
+
context 'format' do
|
281
|
+
before do
|
282
|
+
subject.get("/foo") { "bar" }
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'should set content type for txt format' do
|
286
|
+
get '/foo'
|
287
|
+
last_response.headers['Content-Type'].should eql 'text/plain'
|
288
|
+
end
|
289
|
+
|
290
|
+
it 'should set content type for json' do
|
291
|
+
get '/foo.json'
|
292
|
+
last_response.headers['Content-Type'].should eql 'application/json'
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'should set content type for error' do
|
296
|
+
subject.get('/error') { error!('error in plain text', 500) }
|
297
|
+
get '/error'
|
298
|
+
last_response.headers['Content-Type'].should eql 'text/plain'
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'should set content type for error' do
|
302
|
+
subject.error_format :json
|
303
|
+
subject.get('/error') { error!('error in json', 500) }
|
304
|
+
get '/error.json'
|
305
|
+
last_response.headers['Content-Type'].should eql 'application/json'
|
306
|
+
end
|
307
|
+
end
|
279
308
|
|
280
309
|
context 'custom middleware' do
|
281
310
|
class PhonyMiddleware
|
@@ -471,4 +500,112 @@ describe Grape::API do
|
|
471
500
|
last_response.status.should eql 200
|
472
501
|
end
|
473
502
|
end
|
503
|
+
|
504
|
+
describe ".rescue_from" do
|
505
|
+
it 'should not rescue errors when rescue_from is not set' do
|
506
|
+
subject.get '/exception' do
|
507
|
+
raise "rain!"
|
508
|
+
end
|
509
|
+
lambda { get '/exception' }.should raise_error
|
510
|
+
end
|
511
|
+
|
512
|
+
it 'should rescue all errors if rescue_from :all is called' do
|
513
|
+
subject.rescue_from :all
|
514
|
+
subject.get '/exception' do
|
515
|
+
raise "rain!"
|
516
|
+
end
|
517
|
+
get '/exception'
|
518
|
+
last_response.status.should eql 403
|
519
|
+
end
|
520
|
+
|
521
|
+
it 'should rescue only certain errors if rescue_from is called with specific errors' do
|
522
|
+
subject.rescue_from ArgumentError
|
523
|
+
subject.get('/rescued'){ raise ArgumentError }
|
524
|
+
subject.get('/unrescued'){ raise "beefcake" }
|
525
|
+
|
526
|
+
get '/rescued'
|
527
|
+
last_response.status.should eql 403
|
528
|
+
|
529
|
+
lambda{ get '/unrescued' }.should raise_error
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
describe ".error_format" do
|
534
|
+
it 'should rescue all errors and return :txt' do
|
535
|
+
subject.rescue_from :all
|
536
|
+
subject.error_format :txt
|
537
|
+
subject.get '/exception' do
|
538
|
+
raise "rain!"
|
539
|
+
end
|
540
|
+
get '/exception'
|
541
|
+
last_response.body.should eql "rain!"
|
542
|
+
end
|
543
|
+
|
544
|
+
it 'should rescue all errros and return :txt with backtrace' do
|
545
|
+
subject.rescue_from :all, :backtrace => true
|
546
|
+
subject.error_format :txt
|
547
|
+
subject.get '/exception' do
|
548
|
+
raise "rain!"
|
549
|
+
end
|
550
|
+
get '/exception'
|
551
|
+
last_response.body.start_with?("rain!\r\n").should be_true
|
552
|
+
end
|
553
|
+
|
554
|
+
it 'should rescue all errors and return :json' do
|
555
|
+
subject.rescue_from :all
|
556
|
+
subject.error_format :json
|
557
|
+
subject.get '/exception' do
|
558
|
+
raise "rain!"
|
559
|
+
end
|
560
|
+
get '/exception'
|
561
|
+
last_response.body.should eql '{"error":"rain!"}'
|
562
|
+
end
|
563
|
+
it 'should rescue all errors and return :json with backtrace' do
|
564
|
+
subject.rescue_from :all, :backtrace => true
|
565
|
+
subject.error_format :json
|
566
|
+
subject.get '/exception' do
|
567
|
+
raise "rain!"
|
568
|
+
end
|
569
|
+
get '/exception'
|
570
|
+
json = JSON.parse(last_response.body)
|
571
|
+
json["error"].should eql 'rain!'
|
572
|
+
json["backtrace"].length.should > 0
|
573
|
+
end
|
574
|
+
it 'should rescue error! and return txt' do
|
575
|
+
subject.error_format :txt
|
576
|
+
subject.get '/error' do
|
577
|
+
error!("Access Denied", 401)
|
578
|
+
end
|
579
|
+
get '/error'
|
580
|
+
last_response.body.should eql "Access Denied"
|
581
|
+
end
|
582
|
+
it 'should rescue error! and return json' do
|
583
|
+
subject.error_format :json
|
584
|
+
subject.get '/error' do
|
585
|
+
error!("Access Denied", 401)
|
586
|
+
end
|
587
|
+
get '/error'
|
588
|
+
last_response.body.should eql '{"error":"Access Denied"}'
|
589
|
+
end
|
590
|
+
end
|
591
|
+
|
592
|
+
describe ".default_error_status" do
|
593
|
+
it 'should allow setting default_error_status' do
|
594
|
+
subject.rescue_from :all
|
595
|
+
subject.default_error_status 200
|
596
|
+
subject.get '/exception' do
|
597
|
+
raise "rain!"
|
598
|
+
end
|
599
|
+
get '/exception'
|
600
|
+
last_response.status.should eql 200
|
601
|
+
end
|
602
|
+
it 'should have a default error status' do
|
603
|
+
subject.rescue_from :all
|
604
|
+
subject.get '/exception' do
|
605
|
+
raise "rain!"
|
606
|
+
end
|
607
|
+
get '/exception'
|
608
|
+
last_response.status.should eql 403
|
609
|
+
end
|
610
|
+
end
|
474
611
|
end
|
data/spec/grape/endpoint_spec.rb
CHANGED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec::Matchers.define :be_challenge do
|
4
|
+
match do |actual_response|
|
5
|
+
actual_response.status == 401 &&
|
6
|
+
actual_response['WWW-Authenticate'] =~ /^Digest / &&
|
7
|
+
actual_response.body.empty?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Test < Grape::API
|
12
|
+
version '1'
|
13
|
+
|
14
|
+
http_digest({:realm => 'Test Api', :opaque => 'secret'}) do |username|
|
15
|
+
{'foo' => 'bar'}[username]
|
16
|
+
end
|
17
|
+
|
18
|
+
get '/test' do
|
19
|
+
[{:hey => 'you'},{:there => 'bar'},{:foo => 'baz'}]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Grape::Middleware::Auth::Digest do
|
24
|
+
def app
|
25
|
+
Test
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should be a digest authentication challenge' do
|
29
|
+
get '/1/test'
|
30
|
+
last_response.should be_challenge
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should throw a 401 if no auth is given' do
|
34
|
+
get '/1/test'
|
35
|
+
last_response.status.should == 401
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should authenticate if given valid creds' do
|
39
|
+
digest_authorize "foo", "bar"
|
40
|
+
get '/1/test'
|
41
|
+
last_response.status.should == 200
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should throw a 401 if given invalid creds' do
|
45
|
+
digest_authorize "bar", "foo"
|
46
|
+
get '/1/test'
|
47
|
+
last_response.status.should == 401
|
48
|
+
end
|
49
|
+
end
|
@@ -65,9 +65,11 @@ describe Grape::Middleware::Auth::OAuth2 do
|
|
65
65
|
it { @err[:headers]['WWW-Authenticate'].should == "OAuth realm='OAuth API', error='expired_token'" }
|
66
66
|
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
%w(HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION REDIRECT_X_HTTP_AUTHORIZATION).each do |head|
|
69
|
+
context "with the token in the #{head} header" do
|
70
|
+
before { get '/awesome', {}, head => 'OAuth g123' }
|
71
|
+
it { last_response.body.should == 'g123' }
|
72
|
+
end
|
71
73
|
end
|
72
74
|
|
73
75
|
context 'with the token in the POST body' do
|
@@ -85,4 +87,4 @@ describe Grape::Middleware::Auth::OAuth2 do
|
|
85
87
|
it { @err[:headers]['WWW-Authenticate'].should == "OAuth realm='OAuth API', error='insufficient_scope'" }
|
86
88
|
it { @err[:status].should == 403 }
|
87
89
|
end
|
88
|
-
end
|
90
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Middleware::Error do
|
4
|
+
|
5
|
+
# raises a text exception
|
6
|
+
class ExceptionApp
|
7
|
+
class << self
|
8
|
+
def call(env)
|
9
|
+
raise "rain!"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# raises a hash error
|
15
|
+
class ErrorHashApp
|
16
|
+
class << self
|
17
|
+
def error!(message, status=403)
|
18
|
+
throw :error, :message => { :error => message, :detail => "missing widget" }, :status => status
|
19
|
+
end
|
20
|
+
def call(env)
|
21
|
+
error!("rain!", 401)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# raises an error!
|
27
|
+
class AccessDeniedApp
|
28
|
+
class << self
|
29
|
+
def error!(message, status=403)
|
30
|
+
throw :error, :message => message, :status => status
|
31
|
+
end
|
32
|
+
def call(env)
|
33
|
+
error!("Access Denied", 401)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def app
|
39
|
+
@app
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should not trap errors by default' do
|
43
|
+
@app ||= Rack::Builder.app do
|
44
|
+
use Grape::Middleware::Error
|
45
|
+
run ExceptionApp
|
46
|
+
end
|
47
|
+
lambda { get '/' }.should raise_error
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'with rescue_all set to true' do
|
51
|
+
it 'should set the message appropriately' do
|
52
|
+
@app ||= Rack::Builder.app do
|
53
|
+
use Grape::Middleware::Error, :rescue_all => true
|
54
|
+
run ExceptionApp
|
55
|
+
end
|
56
|
+
get '/'
|
57
|
+
last_response.body.should == "rain!"
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should default to a 403 status' do
|
61
|
+
@app ||= Rack::Builder.app do
|
62
|
+
use Grape::Middleware::Error, :rescue_all => true
|
63
|
+
run ExceptionApp
|
64
|
+
end
|
65
|
+
get '/'
|
66
|
+
last_response.status.should == 403
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should be possible to specify a different default status code' do
|
70
|
+
@app ||= Rack::Builder.app do
|
71
|
+
use Grape::Middleware::Error, :rescue_all => true, :default_status => 500
|
72
|
+
run ExceptionApp
|
73
|
+
end
|
74
|
+
get '/'
|
75
|
+
last_response.status.should == 500
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should be possible to return errors in json format' do
|
79
|
+
@app ||= Rack::Builder.app do
|
80
|
+
use Grape::Middleware::Error, :rescue_all => true, :format => :json
|
81
|
+
run ExceptionApp
|
82
|
+
end
|
83
|
+
get '/'
|
84
|
+
last_response.body.should == '{"error":"rain!"}'
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should be possible to return hash errors in json format' do
|
88
|
+
@app ||= Rack::Builder.app do
|
89
|
+
use Grape::Middleware::Error, :rescue_all => true, :format => :json
|
90
|
+
run ErrorHashApp
|
91
|
+
end
|
92
|
+
get '/'
|
93
|
+
['{"error":"rain!","detail":"missing widget"}',
|
94
|
+
'{"detail":"missing widget","error":"rain!"}'].should be_include(last_response.body)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should be possible to specify a custom formatter' do
|
98
|
+
@app ||= Rack::Builder.app do
|
99
|
+
use Grape::Middleware::Error,
|
100
|
+
:rescue_all => true,
|
101
|
+
:format => :custom,
|
102
|
+
:formatters => {
|
103
|
+
:custom => lambda { |message, backtrace| { :custom_formatter => message }.inspect }
|
104
|
+
}
|
105
|
+
run ExceptionApp
|
106
|
+
end
|
107
|
+
get '/'
|
108
|
+
last_response.body.should == '{:custom_formatter=>"rain!"}'
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should not trap regular error! codes' do
|
112
|
+
@app ||= Rack::Builder.app do
|
113
|
+
use Grape::Middleware::Error
|
114
|
+
run AccessDeniedApp
|
115
|
+
end
|
116
|
+
get '/'
|
117
|
+
last_response.status.should == 401
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: grape
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.1.
|
5
|
+
version: 0.1.5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Michael Bleigh
|
@@ -10,11 +10,12 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-07-14 00:00:00 -05:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rack
|
18
|
+
prerelease: false
|
18
19
|
requirement: &id001 !ruby/object:Gem::Requirement
|
19
20
|
none: false
|
20
21
|
requirements:
|
@@ -22,10 +23,10 @@ dependencies:
|
|
22
23
|
- !ruby/object:Gem::Version
|
23
24
|
version: "0"
|
24
25
|
type: :runtime
|
25
|
-
prerelease: false
|
26
26
|
version_requirements: *id001
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rack-mount
|
29
|
+
prerelease: false
|
29
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
30
31
|
none: false
|
31
32
|
requirements:
|
@@ -33,10 +34,10 @@ dependencies:
|
|
33
34
|
- !ruby/object:Gem::Version
|
34
35
|
version: "0"
|
35
36
|
type: :runtime
|
36
|
-
prerelease: false
|
37
37
|
version_requirements: *id002
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: rack-jsonp
|
40
|
+
prerelease: false
|
40
41
|
requirement: &id003 !ruby/object:Gem::Requirement
|
41
42
|
none: false
|
42
43
|
requirements:
|
@@ -44,10 +45,10 @@ dependencies:
|
|
44
45
|
- !ruby/object:Gem::Version
|
45
46
|
version: "0"
|
46
47
|
type: :runtime
|
47
|
-
prerelease: false
|
48
48
|
version_requirements: *id003
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
name: multi_json
|
51
|
+
prerelease: false
|
51
52
|
requirement: &id004 !ruby/object:Gem::Requirement
|
52
53
|
none: false
|
53
54
|
requirements:
|
@@ -55,10 +56,10 @@ dependencies:
|
|
55
56
|
- !ruby/object:Gem::Version
|
56
57
|
version: "0"
|
57
58
|
type: :runtime
|
58
|
-
prerelease: false
|
59
59
|
version_requirements: *id004
|
60
60
|
- !ruby/object:Gem::Dependency
|
61
61
|
name: multi_xml
|
62
|
+
prerelease: false
|
62
63
|
requirement: &id005 !ruby/object:Gem::Requirement
|
63
64
|
none: false
|
64
65
|
requirements:
|
@@ -66,10 +67,10 @@ dependencies:
|
|
66
67
|
- !ruby/object:Gem::Version
|
67
68
|
version: "0"
|
68
69
|
type: :runtime
|
69
|
-
prerelease: false
|
70
70
|
version_requirements: *id005
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
|
-
name:
|
72
|
+
name: rake
|
73
|
+
prerelease: false
|
73
74
|
requirement: &id006 !ruby/object:Gem::Requirement
|
74
75
|
none: false
|
75
76
|
requirements:
|
@@ -77,10 +78,10 @@ dependencies:
|
|
77
78
|
- !ruby/object:Gem::Version
|
78
79
|
version: "0"
|
79
80
|
type: :development
|
80
|
-
prerelease: false
|
81
81
|
version_requirements: *id006
|
82
82
|
- !ruby/object:Gem::Dependency
|
83
83
|
name: maruku
|
84
|
+
prerelease: false
|
84
85
|
requirement: &id007 !ruby/object:Gem::Requirement
|
85
86
|
none: false
|
86
87
|
requirements:
|
@@ -88,10 +89,10 @@ dependencies:
|
|
88
89
|
- !ruby/object:Gem::Version
|
89
90
|
version: "0"
|
90
91
|
type: :development
|
91
|
-
prerelease: false
|
92
92
|
version_requirements: *id007
|
93
93
|
- !ruby/object:Gem::Dependency
|
94
94
|
name: yard
|
95
|
+
prerelease: false
|
95
96
|
requirement: &id008 !ruby/object:Gem::Requirement
|
96
97
|
none: false
|
97
98
|
requirements:
|
@@ -99,10 +100,10 @@ dependencies:
|
|
99
100
|
- !ruby/object:Gem::Version
|
100
101
|
version: "0"
|
101
102
|
type: :development
|
102
|
-
prerelease: false
|
103
103
|
version_requirements: *id008
|
104
104
|
- !ruby/object:Gem::Dependency
|
105
105
|
name: rack-test
|
106
|
+
prerelease: false
|
106
107
|
requirement: &id009 !ruby/object:Gem::Requirement
|
107
108
|
none: false
|
108
109
|
requirements:
|
@@ -110,21 +111,21 @@ dependencies:
|
|
110
111
|
- !ruby/object:Gem::Version
|
111
112
|
version: "0"
|
112
113
|
type: :development
|
113
|
-
prerelease: false
|
114
114
|
version_requirements: *id009
|
115
115
|
- !ruby/object:Gem::Dependency
|
116
116
|
name: rspec
|
117
|
+
prerelease: false
|
117
118
|
requirement: &id010 !ruby/object:Gem::Requirement
|
118
119
|
none: false
|
119
120
|
requirements:
|
120
121
|
- - ~>
|
121
122
|
- !ruby/object:Gem::Version
|
122
|
-
version: 2.
|
123
|
+
version: 2.6.0
|
123
124
|
type: :development
|
124
|
-
prerelease: false
|
125
125
|
version_requirements: *id010
|
126
126
|
- !ruby/object:Gem::Dependency
|
127
127
|
name: json_pure
|
128
|
+
prerelease: false
|
128
129
|
requirement: &id011 !ruby/object:Gem::Requirement
|
129
130
|
none: false
|
130
131
|
requirements:
|
@@ -132,8 +133,29 @@ dependencies:
|
|
132
133
|
- !ruby/object:Gem::Version
|
133
134
|
version: "0"
|
134
135
|
type: :development
|
135
|
-
prerelease: false
|
136
136
|
version_requirements: *id011
|
137
|
+
- !ruby/object:Gem::Dependency
|
138
|
+
name: ZenTest
|
139
|
+
prerelease: false
|
140
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
141
|
+
none: false
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: "0"
|
146
|
+
type: :development
|
147
|
+
version_requirements: *id012
|
148
|
+
- !ruby/object:Gem::Dependency
|
149
|
+
name: bundler
|
150
|
+
prerelease: false
|
151
|
+
requirement: &id013 !ruby/object:Gem::Requirement
|
152
|
+
none: false
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: "0"
|
157
|
+
type: :development
|
158
|
+
version_requirements: *id013
|
137
159
|
description: A Ruby framework for rapid API development with great conventions.
|
138
160
|
email:
|
139
161
|
- michael@intridea.com
|
@@ -259,6 +281,7 @@ files:
|
|
259
281
|
- lib/grape/api.rb
|
260
282
|
- lib/grape/endpoint.rb
|
261
283
|
- lib/grape/middleware/auth/basic.rb
|
284
|
+
- lib/grape/middleware/auth/digest.rb
|
262
285
|
- lib/grape/middleware/auth/oauth2.rb
|
263
286
|
- lib/grape/middleware/base.rb
|
264
287
|
- lib/grape/middleware/error.rb
|
@@ -269,9 +292,11 @@ files:
|
|
269
292
|
- spec/grape/api_spec.rb
|
270
293
|
- spec/grape/endpoint_spec.rb
|
271
294
|
- spec/grape/middleware/auth/basic_spec.rb
|
295
|
+
- spec/grape/middleware/auth/digest_spec.rb
|
272
296
|
- spec/grape/middleware/auth/oauth2_spec.rb
|
273
297
|
- spec/grape/middleware/base_spec.rb
|
274
298
|
- spec/grape/middleware/error_spec.rb
|
299
|
+
- spec/grape/middleware/exception_spec.rb
|
275
300
|
- spec/grape/middleware/formatter_spec.rb
|
276
301
|
- spec/grape/middleware/prefixer_spec.rb
|
277
302
|
- spec/grape/middleware/versioner_spec.rb
|
@@ -291,18 +316,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
291
316
|
requirements:
|
292
317
|
- - ">="
|
293
318
|
- !ruby/object:Gem::Version
|
294
|
-
hash: -2709592695038958113
|
295
|
-
segments:
|
296
|
-
- 0
|
297
319
|
version: "0"
|
298
320
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
299
321
|
none: false
|
300
322
|
requirements:
|
301
323
|
- - ">="
|
302
324
|
- !ruby/object:Gem::Version
|
303
|
-
hash: -2709592695038958113
|
304
|
-
segments:
|
305
|
-
- 0
|
306
325
|
version: "0"
|
307
326
|
requirements: []
|
308
327
|
|
@@ -315,9 +334,11 @@ test_files:
|
|
315
334
|
- spec/grape/api_spec.rb
|
316
335
|
- spec/grape/endpoint_spec.rb
|
317
336
|
- spec/grape/middleware/auth/basic_spec.rb
|
337
|
+
- spec/grape/middleware/auth/digest_spec.rb
|
318
338
|
- spec/grape/middleware/auth/oauth2_spec.rb
|
319
339
|
- spec/grape/middleware/base_spec.rb
|
320
340
|
- spec/grape/middleware/error_spec.rb
|
341
|
+
- spec/grape/middleware/exception_spec.rb
|
321
342
|
- spec/grape/middleware/formatter_spec.rb
|
322
343
|
- spec/grape/middleware/prefixer_spec.rb
|
323
344
|
- spec/grape/middleware/versioner_spec.rb
|