Skip to content

Commit 67efd57

Browse files
author
Michael Bleigh
committed
Brings in basic mounting of Rack apps.
1 parent ca7ad7d commit 67efd57

File tree

2 files changed

+66
-11
lines changed

2 files changed

+66
-11
lines changed

lib/grape/api.rb

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,17 @@ def http_digest(options = {}, &block)
205205
auth :http_digest, options, &block
206206
end
207207

208+
def mount(mounts)
209+
mounts = {mounts => '/'} unless mounts.respond_to?(:each_pair)
210+
211+
mounts.each_pair do |app, path|
212+
next unless app.respond_to?(:call)
213+
route_set.add_route(app,
214+
path_info: compile_path(path, false)
215+
)
216+
end
217+
end
218+
208219
# Defines a route that will be recognized
209220
# by the Grape API.
210221
#
@@ -225,16 +236,12 @@ def route(methods, paths = ['/'], route_options = {}, &block)
225236

226237
endpoint = build_endpoint(&block)
227238

228-
endpoint_options = {}
229-
endpoint_options[:version] = /#{version.join('|')}/ if version
230-
231239
route_options ||= {}
232240

233241
methods.each do |method|
234242
paths.each do |path|
235-
236-
compiled_path = compile_path(path)
237-
path = Rack::Mount::Strexp.compile(compiled_path, endpoint_options, %w( / . ? ), true)
243+
prepared_path = prepare_path(path)
244+
path = compile_path(path)
238245
regex = Rack::Mount::RegexpWithNamedGroups.new(path)
239246
path_params = regex.named_captures.map { |nc| nc[0] } - [ 'version', 'format' ]
240247
path_params |= (route_options[:params] || [])
@@ -245,7 +252,7 @@ def route(methods, paths = ['/'], route_options = {}, &block)
245252
:version => version ? version.join('|') : nil,
246253
:namespace => namespace,
247254
:method => request_method,
248-
:path => compiled_path,
255+
:path => prepared_path,
249256
:params => path_params}))
250257

251258
route_set.add_route(endpoint,
@@ -386,7 +393,7 @@ def route_set
386393
@route_set ||= Rack::Mount::RouteSet.new
387394
end
388395

389-
def compile_path(path)
396+
def prepare_path(path)
390397
parts = []
391398
parts << prefix if prefix
392399
parts << ':version' if version
@@ -395,6 +402,13 @@ def compile_path(path)
395402
parts.last << '(.:format)'
396403
Rack::Mount::Utils.normalize_path(parts.join('/'))
397404
end
405+
406+
def compile_path(path, anchor = true)
407+
endpoint_options = {}
408+
endpoint_options[:version] = /#{version.join('|')}/ if version
409+
410+
Rack::Mount::Strexp.compile(prepare_path(path), endpoint_options, %w( / . ? ), anchor)
411+
end
398412
end
399413

400414
reset!

spec/grape/api_spec.rb

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def app; subject end
125125
subject.version :v1
126126

127127
subject.namespace :awesome do
128-
compile_path('hello').should == '/rad/:version/awesome/hello(.:format)'
128+
prepare_path('hello').should == '/rad/:version/awesome/hello(.:format)'
129129
end
130130
end
131131

@@ -163,9 +163,9 @@ def app; subject end
163163
it 'should be callable with nil just to push onto the stack' do
164164
subject.namespace do
165165
version 'v2'
166-
compile_path('hello').should == '/:version/hello(.:format)'
166+
prepare_path('hello').should == '/:version/hello(.:format)'
167167
end
168-
subject.send(:compile_path, 'hello').should == '/hello(.:format)'
168+
subject.send(:prepare_path, 'hello').should == '/hello(.:format)'
169169
end
170170

171171
%w(group resource resources segment).each do |als|
@@ -818,4 +818,45 @@ class CommunicationError < RuntimeError; end
818818
lambda { get '/uncaught' }.should raise_error(CommunicationError)
819819
end
820820
end
821+
822+
describe '.mount.' do
823+
let(:mounted_app){ lambda{|env| [200, {}, ["MOUNTED"]]} }
824+
825+
context 'with a bare rack app' do
826+
before do
827+
subject.mount mounted_app => '/mounty'
828+
end
829+
830+
it 'should make a bare Rack app available at the endpoint' do
831+
get '/mounty'
832+
last_response.body.should == 'MOUNTED'
833+
end
834+
835+
it 'should anchor the routes, passing all subroutes to it' do
836+
get '/mounty/awesome'
837+
last_response.body.should == 'MOUNTED'
838+
end
839+
840+
it 'should be able to cascade' do
841+
subject.mount lambda{ |env|
842+
headers = {}
843+
headers['X-Cascade'] == 'pass' unless env['PATH_INFO'].include?('boo')
844+
[200, headers, ["Farfegnugen"]]
845+
} => '/'
846+
847+
get '/boo'
848+
last_response.body.should == 'Farfegnugen'
849+
get '/mounty'
850+
last_response.body.should == 'MOUNTED'
851+
end
852+
end
853+
854+
context 'without a hash' do
855+
it 'should call through setting the route to "/"' do
856+
subject.mount mounted_app
857+
get '/'
858+
last_response.body.should == 'MOUNTED'
859+
end
860+
end
861+
end
821862
end

0 commit comments

Comments
 (0)