Skip to content

Commit cf031ab

Browse files
committed
Extract Grape::Path from Grape::Endpoint#prepare_path
1 parent 77d7262 commit cf031ab

File tree

4 files changed

+296
-18
lines changed

4 files changed

+296
-18
lines changed

lib/grape.rb

+4
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@
2222
module Grape
2323
autoload :API, 'grape/api'
2424
autoload :Endpoint, 'grape/endpoint'
25+
2526
autoload :Route, 'grape/route'
2627
autoload :Namespace, 'grape/namespace'
28+
29+
autoload :Path, 'grape/path'
30+
2731
autoload :Cookies, 'grape/cookies'
2832
autoload :Validations, 'grape/validations'
2933
autoload :Request, 'grape/http/request'

lib/grape/endpoint.rb

+1-18
Original file line numberDiff line numberDiff line change
@@ -127,24 +127,7 @@ def prepare_routes
127127
end
128128

129129
def prepare_path(path)
130-
parts = []
131-
parts << settings[:mount_path].to_s.split("/") if settings[:mount_path]
132-
parts << settings[:root_prefix].to_s.split("/") if settings[:root_prefix]
133-
134-
uses_path_versioning = settings[:version] && settings[:version_options][:using] == :path
135-
namespace_is_empty = namespace && (namespace.to_s =~ /^\s*$/ || namespace.to_s == '/')
136-
path_is_empty = path && (path.to_s =~ /^\s*$/ || path.to_s == '/')
137-
138-
parts << ':version' if uses_path_versioning
139-
if ! uses_path_versioning || (! namespace_is_empty || ! path_is_empty)
140-
parts << namespace.to_s if namespace
141-
parts << path.to_s if path
142-
format_suffix = '(.:format)'
143-
else
144-
format_suffix = '(/.:format)'
145-
end
146-
parts = parts.flatten.select { |part| part != '/' }
147-
Rack::Mount::Utils.normalize_path(parts.join('/') + format_suffix)
130+
Path.prepare(path, namespace, settings)
148131
end
149132

150133
def namespace

lib/grape/path.rb

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
module Grape
2+
class Path
3+
4+
def self.prepare(raw_path, namespace, settings)
5+
Path.new(raw_path, namespace, settings).path_with_suffix
6+
end
7+
8+
attr_reader :raw_path, :namespace, :settings
9+
10+
def initialize(raw_path, namespace, settings)
11+
@raw_path = raw_path
12+
@namespace = namespace
13+
@settings = settings
14+
end
15+
16+
def mount_path
17+
split_setting(:mount_path, '/')
18+
end
19+
20+
def root_prefix
21+
split_setting(:root_prefix, '/')
22+
end
23+
24+
def uses_path_versioning?
25+
settings[:version] && settings[:version_options][:using] == :path
26+
end
27+
28+
def has_namespace?
29+
namespace && namespace.to_s =~ /^\S/ && namespace != '/'
30+
end
31+
32+
def has_path?
33+
raw_path && raw_path.to_s =~ /^\S/ && raw_path != '/'
34+
end
35+
36+
def suffix
37+
if !uses_path_versioning? || (has_namespace? || has_path?)
38+
'(.:format)'
39+
else
40+
'(/.:format)'
41+
end
42+
end
43+
44+
def path
45+
Rack::Mount::Utils.normalize_path(parts.join('/'))
46+
end
47+
48+
def path_with_suffix
49+
"#{path}#{suffix}"
50+
end
51+
52+
def to_s
53+
path_with_suffix
54+
end
55+
56+
private
57+
58+
def parts
59+
parts = [mount_path, root_prefix].compact
60+
parts << ':version' if uses_path_versioning?
61+
parts << namespace.to_s
62+
parts << raw_path.to_s
63+
parts.flatten.reject { |part| part == '/' }
64+
end
65+
66+
def split_setting(key, delimiter)
67+
return if settings[key].nil?
68+
settings[key].to_s.split("/")
69+
end
70+
71+
end
72+
end

spec/grape/path_spec.rb

+219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
require 'spec_helper'
2+
3+
module Grape
4+
describe Path do
5+
6+
describe "#initialize" do
7+
it "remembers the path" do
8+
path = Path.new('/:id', anything, anything)
9+
expect(path.raw_path).to eql('/:id')
10+
end
11+
12+
it "remembers the namespace" do
13+
path = Path.new(anything, '/users', anything)
14+
expect(path.namespace).to eql('/users')
15+
end
16+
17+
it "remebers the settings" do
18+
path = Path.new(anything, anything, foo: 'bar')
19+
expect(path.settings).to eql(foo: 'bar')
20+
end
21+
end
22+
23+
describe "#mount_path" do
24+
it "is nil when no mount path setting exists" do
25+
path = Path.new(anything, anything, {})
26+
expect(path.mount_path).to be_nil
27+
end
28+
29+
it "is nil when the mount path is nil" do
30+
path = Path.new(anything, anything, mount_path: nil)
31+
expect(path.mount_path).to be_nil
32+
end
33+
34+
it "splits the mount path" do
35+
path = Path.new(anything, anything, mount_path: 'foo/bar')
36+
expect(path.mount_path).to eql(['foo', 'bar'])
37+
end
38+
end
39+
40+
describe "#root_prefix" do
41+
it "is nil when no root prefix setting exists" do
42+
path = Path.new(anything, anything, {})
43+
expect(path.root_prefix).to be_nil
44+
end
45+
46+
it "is nil when the mount path is nil" do
47+
path = Path.new(anything, anything, root_prefix: nil)
48+
expect(path.root_prefix).to be_nil
49+
end
50+
51+
it "splits the mount path" do
52+
path = Path.new(anything, anything, root_prefix: 'hello/world')
53+
expect(path.root_prefix).to eql(['hello', 'world'])
54+
end
55+
end
56+
57+
describe "#uses_path_versioning?" do
58+
it "is false when the version setting is nil" do
59+
path = Path.new(anything, anything, version: nil)
60+
expect(path.uses_path_versioning?).to be_false
61+
end
62+
63+
it "is false when the version option is header" do
64+
path = Path.new(anything, anything, {
65+
version: 'v1',
66+
version_options: { using: :header }
67+
})
68+
69+
expect(path.uses_path_versioning?).to be_false
70+
end
71+
72+
it "is true when the version option is path" do
73+
path = Path.new(anything, anything, {
74+
version: 'v1',
75+
version_options: { using: :path }
76+
})
77+
78+
expect(path.uses_path_versioning?).to be_true
79+
end
80+
end
81+
82+
describe "#has_namespace?" do
83+
it "is false when the namespace is nil" do
84+
path = Path.new(anything, nil, anything)
85+
expect(path).not_to have_namespace
86+
end
87+
88+
it "is false when the namespace starts with whitespace" do
89+
path = Path.new(anything, ' /foo', anything)
90+
expect(path).not_to have_namespace
91+
end
92+
93+
it "is false when the namespace is the root path" do
94+
path = Path.new(anything, '/', anything)
95+
expect(path).not_to have_namespace
96+
end
97+
98+
it "is true otherwise" do
99+
path = Path.new(anything, '/world', anything)
100+
expect(path).to have_namespace
101+
end
102+
end
103+
104+
describe "#has_path?" do
105+
it "is false when the path is nil" do
106+
path = Path.new(nil, anything, anything)
107+
expect(path).not_to have_path
108+
end
109+
110+
it "is false when the path starts with whitespace" do
111+
path = Path.new(' /foo', anything, anything)
112+
expect(path).not_to have_path
113+
end
114+
115+
it "is false when the path is the root path" do
116+
path = Path.new('/', anything, anything)
117+
expect(path).not_to have_path
118+
end
119+
120+
it "is true otherwise" do
121+
path = Path.new('/hello', anything, anything)
122+
expect(path).to have_path
123+
end
124+
end
125+
126+
describe "#path" do
127+
context "mount_path" do
128+
it "is not included when it is nil" do
129+
path = Path.new(nil, nil, { mount_path: '/foo/bar' })
130+
expect(path.path).to eql '/foo/bar'
131+
end
132+
133+
it "is included when it is not nil" do
134+
path = Path.new(nil, nil, {})
135+
expect(path.path).to eql('/')
136+
end
137+
end
138+
139+
context "root_prefix" do
140+
it "is not included when it is nil" do
141+
path = Path.new(nil, nil, {})
142+
expect(path.path).to eql('/')
143+
end
144+
145+
it "is included after the mount path" do
146+
path = Path.new(nil, nil, {
147+
mount_path: '/foo',
148+
root_prefix: '/hello'
149+
})
150+
151+
expect(path.path).to eql('/foo/hello')
152+
end
153+
end
154+
155+
it "uses the namespace after the mount path and root prefix" do
156+
path = Path.new(nil, 'namespace', {
157+
mount_path: '/foo',
158+
root_prefix: '/hello'
159+
})
160+
161+
expect(path.path).to eql('/foo/hello/namespace')
162+
end
163+
164+
it "uses the raw path after the namespace" do
165+
path = Path.new('raw_path', 'namespace', {
166+
mount_path: '/foo',
167+
root_prefix: '/hello'
168+
})
169+
170+
expect(path.path).to eql('/foo/hello/namespace/raw_path')
171+
end
172+
end
173+
174+
describe "#suffix" do
175+
context "when path versioning is used" do
176+
it "includes a '/'" do
177+
path = Path.new(nil, nil, {})
178+
path.stub(:uses_path_versioning?) { true }
179+
180+
expect(path.suffix).to eql('(/.:format)')
181+
end
182+
end
183+
184+
context "when path versioning is not used" do
185+
it "does not include a '/' when the path has a namespace" do
186+
path = Path.new(nil, 'namespace', {})
187+
path.stub(:uses_path_versioning?) { true }
188+
189+
expect(path.suffix).to eql('(.:format)')
190+
end
191+
192+
it "does not include a '/' when the path has a path" do
193+
path = Path.new('/path', nil, {})
194+
path.stub(:uses_path_versioning?) { true }
195+
196+
expect(path.suffix).to eql('(.:format)')
197+
end
198+
199+
it "includes a '/' otherwise" do
200+
path = Path.new(nil, nil, {})
201+
path.stub(:uses_path_versioning?) { true }
202+
203+
expect(path.suffix).to eql('(/.:format)')
204+
end
205+
end
206+
end
207+
208+
describe "#path_with_suffix" do
209+
it "combines the path and suffix" do
210+
path = Path.new(nil, nil, {})
211+
path.stub(:path) { '/the/path' }
212+
path.stub(:suffix) { 'suffix' }
213+
214+
expect(path.path_with_suffix).to eql('/the/pathsuffix')
215+
end
216+
end
217+
218+
end
219+
end

0 commit comments

Comments
 (0)