Skip to content

Commit

Permalink
Fix #60: Mounting of an API on a path.
Browse files Browse the repository at this point in the history
  • Loading branch information
dblock committed Feb 9, 2013
1 parent c15809a commit 70a93a7
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Next Release
* [#328](https://github.com/intridea/grape/issues/328): Version can now be specified as both String and Symbol - [@dblock](http://github.com/dblock).
* [#332](https://github.com/intridea/grape/pull/332): `Grape::Exceptions::Validation` now contains full nested parameter names - [@alovak](https://github.com/alovak).
* [#322](https://github.com/intridea/grape/issues/322): When returning a 406 status, Grape will include the requested format or content-type in the response body - [@dblock](http://github.com/dblock).
* [#60](https://github.com/intridea/grape/issues/60): Fix: mounting of a Grape API onto a path - [@dblock](http://github.com/dblock).
* Your contribution here.

0.2.6 (01/11/2013)
Expand Down
11 changes: 8 additions & 3 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,6 @@ In a Rails application, modify *config/routes*:
mount Twitter::API
```

Note that when using Rails you will need to restart the server to pick up changes in your API classes
(see [Issue 131](https://github.com/intridea/grape/issues/131)).

### Modules

You can mount multiple API implementations inside another one. These don't have to be
Expand All @@ -158,6 +155,14 @@ class Twitter::API < Grape::API
end
```

You can also mount on a path, which is similar to using `prefix` inside the mounted API itself.

```ruby
class Twitter::API < Grape::API
mount Twitter::APIv1 => '/v1'
end
```

## Versioning

There are three strategies in which clients can reach your API's endpoints: `:header`,
Expand Down
11 changes: 7 additions & 4 deletions lib/grape/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -287,16 +287,19 @@ def http_digest(options = {}, &block)
end

def mount(mounts)
mounts = {mounts => '/'} unless mounts.respond_to?(:each_pair)
mounts = { mounts => '/' } unless mounts.respond_to?(:each_pair)
mounts.each_pair do |app, path|
if app.respond_to?(:inherit_settings)
app.inherit_settings(settings.clone)
app_settings = settings.clone
mount_path = Rack::Mount::Utils.normalize_path([ settings[:mount_path], path ].compact.join("/"))
app_settings.set :mount_path, mount_path
app.inherit_settings(app_settings)
end
endpoints << Grape::Endpoint.new(settings.clone,
endpoints << Grape::Endpoint.new(settings.clone, {
:method => :any,
:path => path,
:app => app
)
})
end
end

Expand Down
12 changes: 9 additions & 3 deletions lib/grape/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ def generate_api_method(method_name, &block)
def initialize(settings, options = {}, &block)
@settings = settings
if block_given?
method_name = "#{options[:method]} #{settings.gather(:namespace).join( "/")} #{Array(options[:path]).join("/")}"
method_name = [
options[:method],
settings.gather(:namespace).join("/"),
settings.gather(:mount_path).join("/"),
Array(options[:path]).join("/")
].join(" ")
@source = block
@block = self.class.generate_api_method(method_name, &block)
end
Expand All @@ -57,7 +62,7 @@ def routes

def mount_in(route_set)
if endpoints
endpoints.each{|e| e.mount_in(route_set)}
endpoints.each { |e| e.mount_in(route_set) }
else
routes.each do |route|
route_set.add_route(self, {
Expand Down Expand Up @@ -106,14 +111,15 @@ def prepare_routes

def prepare_path(path)
parts = []
parts << settings[:mount_path].to_s.split("/") if settings[:mount_path]
parts << settings[:root_prefix].to_s.split("/") if settings[:root_prefix]

uses_path_versioning = settings[:version] && settings[:version_options][:using] == :path
namespace_is_empty = namespace && (namespace.to_s =~ /^\s*$/ || namespace.to_s == '/')
path_is_empty = path && (path.to_s =~ /^\s*$/ || path.to_s == '/')

parts << ':version' if uses_path_versioning
if !uses_path_versioning || (!namespace_is_empty || !path_is_empty)
if ! uses_path_versioning || (! namespace_is_empty || ! path_is_empty)
parts << namespace.to_s if namespace
parts << path.to_s if path
format_suffix = '(.:format)'
Expand Down
28 changes: 28 additions & 0 deletions spec/grape/api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,34 @@ def self.call(object, env)
subject.routes.first.route_path.should =~ /\/cool\/awesome/
subject.routes.last.route_path.should =~ /\/cool\/sauce/
end

it 'mounts on a path' do
subject.namespace :cool do
app = Class.new(Grape::API)
app.get '/awesome' do
"sauce"
end
mount app => '/mounted'
end
get "/mounted/cool/awesome"
last_response.status.should == 200
last_response.body.should == "sauce"
end

it 'mounts on a nested path' do
app1 = Class.new(Grape::API)
app2 = Class.new(Grape::API)
app2.get '/nice' do
"play"
end
# note that the reverse won't work, mount from outside-in
subject.mount app1 => '/app1'
app1.mount app2 => '/app2'
get "/app1/app2/nice"
last_response.status.should == 200
last_response.body.should == "play"
end

end
end

Expand Down

0 comments on commit 70a93a7

Please sign in to comment.