grape 0.15.0 → 0.16.1
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 +16 -1
- data/Gemfile.lock +16 -15
- data/README.md +41 -47
- data/UPGRADING.md +62 -0
- data/gemfiles/rails_3.gemfile.lock +225 -0
- data/grape.gemspec +2 -2
- data/lib/grape.rb +31 -26
- data/lib/grape/api.rb +39 -23
- data/lib/grape/dsl/inside_route.rb +8 -4
- data/lib/grape/dsl/routing.rb +2 -1
- data/lib/grape/endpoint.rb +43 -62
- data/lib/grape/error_formatter.rb +4 -2
- data/lib/grape/error_formatter/base.rb +10 -6
- data/lib/grape/formatter.rb +4 -2
- data/lib/grape/http/headers.rb +1 -0
- data/lib/grape/middleware/formatter.rb +2 -2
- data/lib/grape/middleware/versioner/accept_version_header.rb +2 -2
- data/lib/grape/middleware/versioner/header.rb +2 -2
- data/lib/grape/middleware/versioner/path.rb +2 -2
- data/lib/grape/namespace.rb +1 -1
- data/lib/grape/parser.rb +4 -2
- data/lib/grape/path.rb +3 -3
- data/lib/grape/request.rb +2 -2
- data/lib/grape/router.rb +156 -0
- data/lib/grape/router/attribute_translator.rb +40 -0
- data/lib/grape/router/pattern.rb +55 -0
- data/lib/grape/router/route.rb +105 -0
- data/lib/grape/serve_file/file_body.rb +34 -0
- data/lib/grape/{util → serve_file}/file_response.rb +1 -1
- data/lib/grape/{util → serve_file}/sendfile_response.rb +1 -1
- data/lib/grape/util/env.rb +1 -1
- data/lib/grape/util/registrable.rb +13 -0
- data/lib/grape/validations/types/custom_type_coercer.rb +2 -0
- data/lib/grape/version.rb +1 -1
- data/spec/grape/api/invalid_format_spec.rb +43 -0
- data/spec/grape/api/recognize_path_spec.rb +21 -0
- data/spec/grape/api/required_parameters_with_invalid_method_spec.rb +26 -0
- data/spec/grape/api_spec.rb +110 -38
- data/spec/grape/dsl/inside_route_spec.rb +267 -240
- data/spec/grape/endpoint_spec.rb +10 -0
- data/spec/grape/entity_spec.rb +2 -2
- data/spec/grape/middleware/formatter_spec.rb +23 -4
- data/spec/grape/middleware/versioner/header_spec.rb +1 -1
- data/spec/grape/middleware/versioner/path_spec.rb +1 -1
- data/spec/grape/parser_spec.rb +82 -0
- data/spec/grape/request_spec.rb +2 -2
- data/spec/grape/validations/params_scope_spec.rb +2 -2
- data/spec/grape/validations/validators/coerce_spec.rb +51 -0
- data/spec/grape/validations_spec.rb +1 -1
- data/tmp/Gemfile.lock +63 -0
- metadata +70 -55
- data/lib/grape/route.rb +0 -32
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'grape/router/pattern'
|
2
|
+
require 'grape/router/attribute_translator'
|
3
|
+
require 'forwardable'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
module Grape
|
7
|
+
class Router
|
8
|
+
class Route
|
9
|
+
ROUTE_ATTRIBUTE_REGEXP = /route_([_a-zA-Z]\w*)/.freeze
|
10
|
+
SOURCE_LOCATION_REGEXP = /^(.*?):(\d+?)(?::in `.+?')?$/.freeze
|
11
|
+
TRANSLATION_ATTRIBUTES = [
|
12
|
+
:prefix,
|
13
|
+
:version,
|
14
|
+
:namespace,
|
15
|
+
:settings,
|
16
|
+
:format,
|
17
|
+
:description,
|
18
|
+
:http_codes,
|
19
|
+
:headers,
|
20
|
+
:entity,
|
21
|
+
:details,
|
22
|
+
:requirements,
|
23
|
+
:request_method
|
24
|
+
].freeze
|
25
|
+
|
26
|
+
attr_accessor :pattern, :translator, :app, :index, :regexp
|
27
|
+
|
28
|
+
alias_method :attributes, :translator
|
29
|
+
|
30
|
+
extend Forwardable
|
31
|
+
def_delegators :pattern, :path, :origin
|
32
|
+
|
33
|
+
def self.translate(*attributes)
|
34
|
+
AttributeTranslator.register(*attributes)
|
35
|
+
def_delegators :@translator, *attributes
|
36
|
+
end
|
37
|
+
|
38
|
+
translate(*TRANSLATION_ATTRIBUTES)
|
39
|
+
|
40
|
+
def method_missing(method_id, *arguments)
|
41
|
+
match = ROUTE_ATTRIBUTE_REGEXP.match(method_id.to_s)
|
42
|
+
if match
|
43
|
+
method_name = match.captures.last.to_sym
|
44
|
+
warn_route_methods(method_name, caller(1).shift)
|
45
|
+
@options[method_name]
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def route_method
|
52
|
+
warn_route_methods(:method, caller(1).shift, :request_method)
|
53
|
+
request_method
|
54
|
+
end
|
55
|
+
|
56
|
+
def route_path
|
57
|
+
warn_route_methods(:path, caller(1).shift)
|
58
|
+
pattern.path
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize(method, pattern, options = {})
|
62
|
+
@suffix = options[:suffix]
|
63
|
+
@options = options.merge(method: method.to_s.upcase)
|
64
|
+
@pattern = Pattern.new(pattern, options)
|
65
|
+
@translator = AttributeTranslator.new(options.merge(request_method: method.to_s.upcase))
|
66
|
+
end
|
67
|
+
|
68
|
+
def exec(env)
|
69
|
+
@app.call(env)
|
70
|
+
end
|
71
|
+
|
72
|
+
def apply(app)
|
73
|
+
@app = app
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
def match?(input)
|
78
|
+
translator.respond_to?(:forward_match) && translator.forward_match ? input.start_with?(pattern.origin) : pattern.match?(input)
|
79
|
+
end
|
80
|
+
|
81
|
+
def params(input = nil)
|
82
|
+
if input.nil?
|
83
|
+
default = pattern.named_captures.keys.each_with_object({}) do |key, defaults|
|
84
|
+
defaults[key] = ''
|
85
|
+
end
|
86
|
+
default.delete_if { |key, _| key == 'format' }.merge(translator.params)
|
87
|
+
else
|
88
|
+
parsed = pattern.params(input)
|
89
|
+
parsed ? parsed.delete_if { |_, value| value.nil? }.symbolize_keys : {}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def warn_route_methods(name, location, expected = nil)
|
96
|
+
path, line = *location.scan(SOURCE_LOCATION_REGEXP).first
|
97
|
+
path = File.realpath(path) if Pathname.new(path).relative?
|
98
|
+
expected ||= name
|
99
|
+
warn <<-EOS
|
100
|
+
#{path}:#{line}: The route_xxx methods such as route_#{name} have been deprecated, please use #{expected}.
|
101
|
+
EOS
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Grape
|
2
|
+
module ServeFile
|
3
|
+
CHUNK_SIZE = 16_384
|
4
|
+
|
5
|
+
# Class helps send file through API
|
6
|
+
class FileBody
|
7
|
+
attr_reader :path
|
8
|
+
|
9
|
+
# @param path [String]
|
10
|
+
def initialize(path)
|
11
|
+
@path = path
|
12
|
+
end
|
13
|
+
|
14
|
+
# Need for Rack::Sendfile middleware
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
def to_path
|
18
|
+
path
|
19
|
+
end
|
20
|
+
|
21
|
+
def each
|
22
|
+
File.open(path, 'rb') do |file|
|
23
|
+
while (chunk = file.read(CHUNK_SIZE))
|
24
|
+
yield chunk
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==(other)
|
30
|
+
path == other.path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/grape/util/env.rb
CHANGED
@@ -13,10 +13,10 @@ module Grape
|
|
13
13
|
RACK_REQUEST_QUERY_HASH = 'rack.request.query_hash'.freeze
|
14
14
|
RACK_REQUEST_FORM_HASH = 'rack.request.form_hash'.freeze
|
15
15
|
RACK_REQUEST_FORM_INPUT = 'rack.request.form_input'.freeze
|
16
|
-
RACK_ROUTING_ARGS = 'rack.routing_args'.freeze
|
17
16
|
|
18
17
|
GRAPE_REQUEST = 'grape.request'.freeze
|
19
18
|
GRAPE_REQUEST_HEADERS = 'grape.request.headers'.freeze
|
20
19
|
GRAPE_REQUEST_PARAMS = 'grape.request.params'.freeze
|
20
|
+
GRAPE_ROUTING_ARGS = 'grape.routing_args'.freeze
|
21
21
|
end
|
22
22
|
end
|
@@ -136,6 +136,8 @@ module Grape
|
|
136
136
|
# Note that this will fail unless a method is also
|
137
137
|
# passed, or if the type also implements a parse() method.
|
138
138
|
type
|
139
|
+
elsif type.is_a?(Enumerable)
|
140
|
+
->(value) { value.all? { |item| item.is_a? type[0] } }
|
139
141
|
else
|
140
142
|
# By default, do a simple type check
|
141
143
|
->(value) { value.is_a? type }
|
data/lib/grape/version.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Endpoint do
|
4
|
+
subject { Class.new(Grape::API) }
|
5
|
+
|
6
|
+
def app
|
7
|
+
subject
|
8
|
+
end
|
9
|
+
|
10
|
+
before do
|
11
|
+
subject.namespace do
|
12
|
+
format :json
|
13
|
+
content_type :json, 'application/json'
|
14
|
+
params do
|
15
|
+
requires :id, desc: 'Identifier.'
|
16
|
+
end
|
17
|
+
get ':id' do
|
18
|
+
{
|
19
|
+
id: params[:id],
|
20
|
+
format: params[:format]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'get' do
|
27
|
+
it 'no format' do
|
28
|
+
get '/foo'
|
29
|
+
expect(last_response.status).to eq 200
|
30
|
+
expect(last_response.body).to eq(MultiJson.dump(id: 'foo', format: nil))
|
31
|
+
end
|
32
|
+
it 'json format' do
|
33
|
+
get '/foo.json'
|
34
|
+
expect(last_response.status).to eq 200
|
35
|
+
expect(last_response.body).to eq(MultiJson.dump(id: 'foo', format: 'json'))
|
36
|
+
end
|
37
|
+
it 'invalid format' do
|
38
|
+
get '/foo.invalid'
|
39
|
+
expect(last_response.status).to eq 200
|
40
|
+
expect(last_response.body).to eq(MultiJson.dump(id: 'foo', format: 'invalid'))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::API do
|
4
|
+
describe '.recognize_path' do
|
5
|
+
subject { Class.new(Grape::API) }
|
6
|
+
|
7
|
+
it 'fetches endpoint by given path' do
|
8
|
+
subject.get('/foo/:id') {}
|
9
|
+
subject.get('/bar/:id') {}
|
10
|
+
subject.get('/baz/:id') {}
|
11
|
+
|
12
|
+
actual = subject.recognize_path('/bar/1234').routes[0].origin
|
13
|
+
expect(actual).to eq('/bar/:id')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns nil if given path does not match with registered routes' do
|
17
|
+
subject.get {}
|
18
|
+
expect(subject.recognize_path('/bar/1234')).to be_nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Grape::Endpoint do
|
4
|
+
subject { Class.new(Grape::API) }
|
5
|
+
|
6
|
+
def app
|
7
|
+
subject
|
8
|
+
end
|
9
|
+
|
10
|
+
before do
|
11
|
+
subject.namespace do
|
12
|
+
params do
|
13
|
+
requires :id, desc: 'Identifier.'
|
14
|
+
end
|
15
|
+
get ':id' do
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'post' do
|
21
|
+
it '405' do
|
22
|
+
post '/something'
|
23
|
+
expect(last_response.status).to eq 405
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/spec/grape/api_spec.rb
CHANGED
@@ -411,7 +411,7 @@ describe Grape::API do
|
|
411
411
|
end
|
412
412
|
|
413
413
|
subject.endpoints.first.routes.each do |route|
|
414
|
-
expect(route.
|
414
|
+
expect(route.path).to eql '/abc(.:format)'
|
415
415
|
end
|
416
416
|
|
417
417
|
get '/abc'
|
@@ -567,6 +567,72 @@ XML
|
|
567
567
|
expect(last_response.headers['Content-Type']).to eql 'text/plain'
|
568
568
|
end
|
569
569
|
|
570
|
+
describe 'adds an OPTIONS route that' do
|
571
|
+
before do
|
572
|
+
subject.before { header 'X-Custom-Header', 'foo' }
|
573
|
+
subject.get 'example' do
|
574
|
+
'example'
|
575
|
+
end
|
576
|
+
subject.route :any, '*path' do
|
577
|
+
error! :not_found, 404
|
578
|
+
end
|
579
|
+
options '/example'
|
580
|
+
end
|
581
|
+
|
582
|
+
it 'returns a 204' do
|
583
|
+
expect(last_response.status).to eql 204
|
584
|
+
end
|
585
|
+
|
586
|
+
it 'has an empty body' do
|
587
|
+
expect(last_response.body).to be_blank
|
588
|
+
end
|
589
|
+
|
590
|
+
it 'has an Allow header' do
|
591
|
+
expect(last_response.headers['Allow']).to eql 'OPTIONS, GET, HEAD'
|
592
|
+
end
|
593
|
+
|
594
|
+
it 'has a X-Custom-Header' do
|
595
|
+
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
|
596
|
+
end
|
597
|
+
|
598
|
+
it 'has no Content-Type' do
|
599
|
+
expect(last_response.content_type).to be_nil
|
600
|
+
end
|
601
|
+
|
602
|
+
it 'has no Content-Length' do
|
603
|
+
expect(last_response.content_length).to be_nil
|
604
|
+
end
|
605
|
+
end
|
606
|
+
|
607
|
+
describe 'adds a 405 Not Allowed route that' do
|
608
|
+
before do
|
609
|
+
subject.before { header 'X-Custom-Header', 'foo' }
|
610
|
+
subject.post :example do
|
611
|
+
'example'
|
612
|
+
end
|
613
|
+
subject.route :any, '*path' do
|
614
|
+
error! :not_found, 404
|
615
|
+
end
|
616
|
+
get '/example'
|
617
|
+
end
|
618
|
+
|
619
|
+
it 'returns a 405' do
|
620
|
+
expect(last_response.status).to eql 405
|
621
|
+
end
|
622
|
+
|
623
|
+
it 'contains error message in body' do
|
624
|
+
expect(last_response.body).to eq '405 Not Allowed'
|
625
|
+
end
|
626
|
+
|
627
|
+
it 'has an Allow header' do
|
628
|
+
expect(last_response.headers['Allow']).to eql 'OPTIONS, POST'
|
629
|
+
end
|
630
|
+
|
631
|
+
it 'has a X-Custom-Header' do
|
632
|
+
expect(last_response.headers['X-Custom-Header']).to eql 'foo'
|
633
|
+
end
|
634
|
+
end
|
635
|
+
|
570
636
|
context 'allows HEAD on a GET request that' do
|
571
637
|
before do
|
572
638
|
subject.get 'example' do
|
@@ -1980,7 +2046,7 @@ XML
|
|
1980
2046
|
end
|
1981
2047
|
|
1982
2048
|
it 'presented with' do
|
1983
|
-
error = { code: 408, with: error_presenter }
|
2049
|
+
error = { code: 408, with: error_presenter }.freeze
|
1984
2050
|
subject.get '/exception' do
|
1985
2051
|
error! error, 408
|
1986
2052
|
end
|
@@ -2006,9 +2072,9 @@ XML
|
|
2006
2072
|
it 'returns one route' do
|
2007
2073
|
expect(subject.routes.size).to eq(1)
|
2008
2074
|
route = subject.routes[0]
|
2009
|
-
expect(route.
|
2010
|
-
expect(route.
|
2011
|
-
expect(route.
|
2075
|
+
expect(route.version).to be_nil
|
2076
|
+
expect(route.path).to eq('/ping(.:format)')
|
2077
|
+
expect(route.request_method).to eq('GET')
|
2012
2078
|
end
|
2013
2079
|
end
|
2014
2080
|
describe 'api structure with two versions and a namespace' do
|
@@ -2036,18 +2102,18 @@ XML
|
|
2036
2102
|
end
|
2037
2103
|
it 'sets route paths' do
|
2038
2104
|
expect(subject.routes.size).to be >= 2
|
2039
|
-
expect(subject.routes[0].
|
2040
|
-
expect(subject.routes[1].
|
2105
|
+
expect(subject.routes[0].path).to eq('/:version/version(.:format)')
|
2106
|
+
expect(subject.routes[1].path).to eq('/p/:version/n1/n2/version(.:format)')
|
2041
2107
|
end
|
2042
2108
|
it 'sets route versions' do
|
2043
|
-
expect(subject.routes[0].
|
2044
|
-
expect(subject.routes[1].
|
2109
|
+
expect(subject.routes[0].version).to eq('v1')
|
2110
|
+
expect(subject.routes[1].version).to eq('v2')
|
2045
2111
|
end
|
2046
2112
|
it 'sets a nested namespace' do
|
2047
|
-
expect(subject.routes[1].
|
2113
|
+
expect(subject.routes[1].namespace).to eq('/n1/n2')
|
2048
2114
|
end
|
2049
2115
|
it 'sets prefix' do
|
2050
|
-
expect(subject.routes[1].
|
2116
|
+
expect(subject.routes[1].prefix).to eq('p')
|
2051
2117
|
end
|
2052
2118
|
end
|
2053
2119
|
describe 'api structure with additional parameters' do
|
@@ -2068,9 +2134,9 @@ XML
|
|
2068
2134
|
get '/split/a,b,c.json', token: ',', limit: '2'
|
2069
2135
|
expect(last_response.body).to eq('["a","b,c"]')
|
2070
2136
|
end
|
2071
|
-
it 'sets
|
2137
|
+
it 'sets params' do
|
2072
2138
|
expect(subject.routes.map { |route|
|
2073
|
-
{ params: route.
|
2139
|
+
{ params: route.params }
|
2074
2140
|
}).to eq [
|
2075
2141
|
{
|
2076
2142
|
params: {
|
@@ -2098,9 +2164,9 @@ XML
|
|
2098
2164
|
subject.get 'two' do
|
2099
2165
|
end
|
2100
2166
|
end
|
2101
|
-
it 'sets
|
2167
|
+
it 'sets params' do
|
2102
2168
|
expect(subject.routes.map { |route|
|
2103
|
-
{ params: route.
|
2169
|
+
{ params: route.params }
|
2104
2170
|
}).to eq [
|
2105
2171
|
{
|
2106
2172
|
params: {
|
@@ -2129,9 +2195,9 @@ XML
|
|
2129
2195
|
subject.get 'two' do
|
2130
2196
|
end
|
2131
2197
|
end
|
2132
|
-
it 'sets
|
2198
|
+
it 'sets params' do
|
2133
2199
|
expect(subject.routes.map { |route|
|
2134
|
-
{ params: route.
|
2200
|
+
{ params: route.params }
|
2135
2201
|
}).to eq [
|
2136
2202
|
{
|
2137
2203
|
params: {
|
@@ -2153,7 +2219,7 @@ XML
|
|
2153
2219
|
it 'exposed' do
|
2154
2220
|
expect(subject.routes.count).to eq 1
|
2155
2221
|
route = subject.routes.first
|
2156
|
-
expect(route.
|
2222
|
+
expect(route.settings[:custom]).to eq(key: 'value')
|
2157
2223
|
end
|
2158
2224
|
end
|
2159
2225
|
describe 'status' do
|
@@ -2187,9 +2253,9 @@ XML
|
|
2187
2253
|
subject.get :first do; end
|
2188
2254
|
expect(subject.routes.length).to eq(1)
|
2189
2255
|
route = subject.routes.first
|
2190
|
-
expect(route.
|
2256
|
+
expect(route.description).to eq('first method')
|
2191
2257
|
expect(route.route_foo).to be_nil
|
2192
|
-
expect(route.
|
2258
|
+
expect(route.params).to eq({})
|
2193
2259
|
end
|
2194
2260
|
it 'describes methods separately' do
|
2195
2261
|
subject.desc 'first method'
|
@@ -2198,7 +2264,7 @@ XML
|
|
2198
2264
|
subject.get :second do; end
|
2199
2265
|
expect(subject.routes.count).to eq(2)
|
2200
2266
|
expect(subject.routes.map { |route|
|
2201
|
-
{ description: route.
|
2267
|
+
{ description: route.description, params: route.params }
|
2202
2268
|
}).to eq [
|
2203
2269
|
{ description: 'first method', params: {} },
|
2204
2270
|
{ description: 'second method', params: {} }
|
@@ -2209,7 +2275,7 @@ XML
|
|
2209
2275
|
subject.get :first do; end
|
2210
2276
|
subject.get :second do; end
|
2211
2277
|
expect(subject.routes.map { |route|
|
2212
|
-
{ description: route.
|
2278
|
+
{ description: route.description, params: route.params }
|
2213
2279
|
}).to eq [
|
2214
2280
|
{ description: 'first method', params: {} },
|
2215
2281
|
{ description: nil, params: {} }
|
@@ -2221,7 +2287,7 @@ XML
|
|
2221
2287
|
get 'second' do; end
|
2222
2288
|
end
|
2223
2289
|
expect(subject.routes.map { |route|
|
2224
|
-
{ description: route.
|
2290
|
+
{ description: route.description, foo: route.route_foo, params: route.params }
|
2225
2291
|
}).to eq [
|
2226
2292
|
{ description: 'ns second', foo: 'bar', params: {} }
|
2227
2293
|
]
|
@@ -2230,7 +2296,7 @@ XML
|
|
2230
2296
|
subject.desc 'method', details: 'method details'
|
2231
2297
|
subject.get 'method' do; end
|
2232
2298
|
expect(subject.routes.map { |route|
|
2233
|
-
{ description: route.
|
2299
|
+
{ description: route.description, details: route.details, params: route.params }
|
2234
2300
|
}).to eq [
|
2235
2301
|
{ description: 'method', details: 'method details', params: {} }
|
2236
2302
|
]
|
@@ -2241,7 +2307,7 @@ XML
|
|
2241
2307
|
params[:s].reverse
|
2242
2308
|
end
|
2243
2309
|
expect(subject.routes.map { |route|
|
2244
|
-
{ description: route.
|
2310
|
+
{ description: route.description, params: route.params }
|
2245
2311
|
}).to eq [
|
2246
2312
|
{ description: 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } } }
|
2247
2313
|
]
|
@@ -2262,7 +2328,7 @@ XML
|
|
2262
2328
|
get do; end
|
2263
2329
|
end
|
2264
2330
|
routes_doc = subject.routes.map { |route|
|
2265
|
-
{ description: route.
|
2331
|
+
{ description: route.description, params: route.params }
|
2266
2332
|
}
|
2267
2333
|
expect(routes_doc).to eq [
|
2268
2334
|
{ description: 'global description',
|
@@ -2292,7 +2358,7 @@ XML
|
|
2292
2358
|
end
|
2293
2359
|
|
2294
2360
|
routes_doc = subject.routes.map { |route|
|
2295
|
-
{ description: route.
|
2361
|
+
{ description: route.description, params: route.params }
|
2296
2362
|
}
|
2297
2363
|
expect(routes_doc).to eq [
|
2298
2364
|
{ description: 'method',
|
@@ -2324,7 +2390,7 @@ XML
|
|
2324
2390
|
end
|
2325
2391
|
end
|
2326
2392
|
expect(subject.routes.map { |route|
|
2327
|
-
{ description: route.
|
2393
|
+
{ description: route.description, params: route.params }
|
2328
2394
|
}).to eq [
|
2329
2395
|
{ description: 'method',
|
2330
2396
|
params: {
|
@@ -2350,7 +2416,7 @@ XML
|
|
2350
2416
|
end
|
2351
2417
|
subject.get 'method' do; end
|
2352
2418
|
|
2353
|
-
expect(subject.routes.map(&:
|
2419
|
+
expect(subject.routes.map(&:params)).to eq [{
|
2354
2420
|
'group1' => { required: true, type: 'Array' },
|
2355
2421
|
'group1[param1]' => { required: false, desc: 'group1 param1 desc' },
|
2356
2422
|
'group1[param2]' => { required: true, desc: 'group1 param2 desc' },
|
@@ -2369,7 +2435,7 @@ XML
|
|
2369
2435
|
end
|
2370
2436
|
subject.get 'method' do; end
|
2371
2437
|
expect(subject.routes.map { |route|
|
2372
|
-
{ description: route.
|
2438
|
+
{ description: route.description, params: route.params }
|
2373
2439
|
}).to eq [
|
2374
2440
|
{ description: 'nesting',
|
2375
2441
|
params: {
|
@@ -2393,7 +2459,7 @@ XML
|
|
2393
2459
|
end
|
2394
2460
|
subject.get 'method' do; end
|
2395
2461
|
expect(subject.routes.map { |route|
|
2396
|
-
{ description: route.
|
2462
|
+
{ description: route.description, params: route.params }
|
2397
2463
|
}).to eq [
|
2398
2464
|
{ description: nil, params: { 'one_param' => { required: true, desc: 'one param' } } }
|
2399
2465
|
]
|
@@ -2404,7 +2470,7 @@ XML
|
|
2404
2470
|
params[:s].reverse
|
2405
2471
|
end
|
2406
2472
|
expect(subject.routes.map { |route|
|
2407
|
-
{ description: route.
|
2473
|
+
{ description: route.description, params: route.params }
|
2408
2474
|
}).to eq [
|
2409
2475
|
{ description: 'Reverses a string.', params: { 's' => { desc: 'string to reverse', type: 'string' } } }
|
2410
2476
|
]
|
@@ -2512,8 +2578,8 @@ XML
|
|
2512
2578
|
mount app
|
2513
2579
|
end
|
2514
2580
|
expect(subject.routes.size).to eq(2)
|
2515
|
-
expect(subject.routes.first.
|
2516
|
-
expect(subject.routes.last.
|
2581
|
+
expect(subject.routes.first.path).to match(%r{\/cool\/awesome})
|
2582
|
+
expect(subject.routes.last.path).to match(%r{\/cool\/sauce})
|
2517
2583
|
end
|
2518
2584
|
|
2519
2585
|
it 'mounts on a path' do
|
@@ -2732,10 +2798,10 @@ XML
|
|
2732
2798
|
context 'plain' do
|
2733
2799
|
before(:each) do
|
2734
2800
|
subject.get '/' do
|
2735
|
-
route.
|
2801
|
+
route.path
|
2736
2802
|
end
|
2737
2803
|
subject.get '/path' do
|
2738
|
-
route.
|
2804
|
+
route.path
|
2739
2805
|
end
|
2740
2806
|
end
|
2741
2807
|
it 'provides access to route info' do
|
@@ -2749,11 +2815,11 @@ XML
|
|
2749
2815
|
before(:each) do
|
2750
2816
|
subject.desc 'returns description'
|
2751
2817
|
subject.get '/description' do
|
2752
|
-
route.
|
2818
|
+
route.description
|
2753
2819
|
end
|
2754
2820
|
subject.desc 'returns parameters', params: { 'x' => 'y' }
|
2755
2821
|
subject.get '/params/:id' do
|
2756
|
-
route.
|
2822
|
+
route.params[params[:id]]
|
2757
2823
|
end
|
2758
2824
|
end
|
2759
2825
|
it 'returns route description' do
|
@@ -2980,6 +3046,12 @@ XML
|
|
2980
3046
|
get '/v2/hello'
|
2981
3047
|
expect(last_response.status).to eq(200)
|
2982
3048
|
expect(last_response.body).to eq('v2')
|
3049
|
+
options '/v2/hello'
|
3050
|
+
expect(last_response.status).to eq(204)
|
3051
|
+
expect(last_response.body).to be_blank
|
3052
|
+
head '/v2/hello'
|
3053
|
+
expect(last_response.status).to eq(200)
|
3054
|
+
expect(last_response.body).to be_blank
|
2983
3055
|
get '/foobar'
|
2984
3056
|
expect(last_response.status).to eq(404)
|
2985
3057
|
expect(last_response.body).to eq('Unrecognized request path: foobar - /foobar')
|