Skip to content

Commit f42b394

Browse files
committed
Merge pull request ruby-grape#115 from jwkoelewijn/frontier-anchored
Added the anchor option to Frontier branch
2 parents 4332cb0 + 13ec78c commit f42b394

File tree

3 files changed

+108
-16
lines changed

3 files changed

+108
-16
lines changed

README.markdown

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
## What is Grape?
66

7-
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.
7+
Grape is a REST-like API micro-framework for Ruby. It is built to complement
8+
existing web application frameworks such as Rails and Sinatra by providing a
9+
simple DSL to easily provide APIs. It has built-in support for common
10+
conventions such as multiple formats, subdomain/prefix restriction, and
11+
versioning.
812

913
## Project Tracking
1014

@@ -19,7 +23,9 @@ Grape is available as a gem, to install it just install the gem:
1923

2024
## Basic Usage
2125

22-
Grape APIs are Rack applications that are created by subclassing `Grape::API`. Below is a simple example showing some of the more common features of Grape in the context of recreating parts of the Twitter API.
26+
Grape APIs are Rack applications that are created by subclassing `Grape::API`.
27+
Below is a simple example showing some of the more common features of Grape in
28+
the context of recreating parts of the Twitter API.
2329

2430
```ruby
2531
class Twitter::API < Grape::API
@@ -68,7 +74,8 @@ class Twitter::API < Grape::API
6874
end
6975
```
7076

71-
This would create a Rack application that could be used like so (in a Rackup config.ru file):
77+
This would create a Rack application that could be used like so (in a Rackup
78+
config.ru file):
7279

7380
```ruby
7481
run Twitter::API
@@ -89,10 +96,14 @@ request:
8996

9097
curl -H Accept=application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
9198

92-
By default, the first matching version is used when no Accept header is supplied. This behavior is similar to routing in Rails.
93-
To circumvent this default behaviour, one could use the `:strict` option. When this option is set to `true`, a `404 Not found` error is returned when no correct Accept header is supplied.
99+
By default, the first matching version is used when no Accept header is
100+
supplied. This behavior is similar to routing in Rails.
101+
To circumvent this default behaviour, one could use the `:strict` option. When
102+
this option is set to `true`, a `404 Not found` error is returned when no
103+
correct Accept header is supplied.
94104

95-
Serialization takes place automatically. For more detailed usage information, please visit the [Grape Wiki](http://github.com/intridea/grape/wiki).
105+
Serialization takes place automatically. For more detailed usage information,
106+
please visit the [Grape Wiki](http://github.com/intridea/grape/wiki).
96107

97108
## Helpers
98109

@@ -132,7 +143,8 @@ You can raise errors explicitly.
132143
error!("Access Denied", 401)
133144
```
134145

135-
You can also return JSON formatted objects explicitly by raising error! and passing a hash instead of a message.
146+
You can also return JSON formatted objects explicitly by raising error! and
147+
passing a hash instead of a message.
136148

137149
```ruby
138150
error!({ "error" => "unexpected error", "detail" => "missing widget" }, 500)
@@ -157,15 +169,17 @@ class Twitter::API < Grape::API
157169
end
158170
```
159171

160-
The error format can be specified using `error_format`. Available formats are `:json` and `:txt` (default).
172+
The error format can be specified using `error_format`. Available formats are
173+
`:json` and `:txt` (default).
161174

162175
```ruby
163176
class Twitter::API < Grape::API
164177
error_format :json
165178
end
166179
```
167180

168-
You can rescue all exceptions with a code block. The `rack_response` wrapper automatically sets the default error code and content-type.
181+
You can rescue all exceptions with a code block. The `rack_response` wrapper
182+
automatically sets the default error code and content-type.
169183

170184
```ruby
171185
class Twitter::API < Grape::API
@@ -175,7 +189,8 @@ class Twitter::API < Grape::API
175189
end
176190
```
177191

178-
You can also rescue specific exceptions with a code block and handle the Rack response at the lowest level.
192+
You can also rescue specific exceptions with a code block and handle the Rack
193+
response at the lowest level.
179194

180195
```ruby
181196
class Twitter::API < Grape::API
@@ -196,7 +211,10 @@ end
196211

197212
## Writing Tests
198213

199-
You can test a Grape API with RSpec. Tests make HTTP requests, therefore they must go into the `spec/request` group. You may want your API code to go into `app/api` - you can match that layout under `spec` by adding the following in `spec/spec_helper.rb`.
214+
You can test a Grape API with RSpec. Tests make HTTP requests, therefore they
215+
must go into the `spec/request` group. You may want your API code to go into
216+
`app/api` - you can match that layout under `spec` by adding the following in
217+
`spec/spec_helper.rb`.
200218

201219
```ruby
202220
RSpec.configure do |config|
@@ -224,7 +242,8 @@ end
224242

225243
## Describing and Inspecting an API
226244

227-
Grape lets you add a description to an API along with any other optional elements that can also be inspected at runtime.
245+
Grape lets you add a description to an API along with any other optional
246+
elements that can also be inspected at runtime.
228247
This can be useful for generating documentation.
229248

230249
```ruby
@@ -246,7 +265,11 @@ class TwitterAPI < Grape::API
246265
end
247266
```
248267

249-
Grape then exposes arrays of API versions and compiled routes. Each route contains a `route_prefix`, `route_version`, `route_namespace`, `route_method`, `route_path` and `route_params`. The description and the optional hash that follows the API path may contain any number of keys and its values are also accessible via dynamically-generated `route_[name]` functions.
268+
Grape then exposes arrays of API versions and compiled routes. Each route
269+
contains a `route_prefix`, `route_version`, `route_namespace`, `route_method`,
270+
`route_path` and `route_params`. The description and the optional hash that
271+
follows the API path may contain any number of keys and its values are also
272+
accessible via dynamically-generated `route_[name]` functions.
250273

251274
```ruby
252275
TwitterAPI::versions # yields [ 'v1', 'v2' ]
@@ -268,12 +291,46 @@ StringAPI::routes[0].route_params # yields an array [ "string", "token" ]
268291
StringAPI::routes[0].route_optional_params # yields an array [ "limit" ]
269292
```
270293

294+
## Anchoring
295+
296+
Grape by default anchors all request paths, which means that the request URL
297+
should match from start to end to match, otherwise a `404 Not Found` is
298+
returned.
299+
However, this is sometimes not what you want, because it is not always known up
300+
front what can be expected from the call.
301+
This is because Rack-mount by default anchors requests to match from the start
302+
to the end, or not at all. Rails solves this problem by using a `:anchor =>
303+
false` option in your routes.
304+
In Grape this option can be used as well when a method is defined.
305+
306+
For instance when you're API needs to get part of an URL, for instance:
307+
308+
```ruby
309+
class UrlAPI < Grape::API
310+
namespace :urls do
311+
get '/(*:url)', :anchor => false do
312+
some_data
313+
end
314+
end
315+
end
316+
```
317+
318+
This will match all paths starting with '/urls/'. There is one caveat though:
319+
the `params[:url]` parameter only holds the first part of the request url.
320+
Luckily this can be circumvented by using the described above syntax for path
321+
specification and using the `PATH_INFO` Rack environment variable, using
322+
`env["PATH_INFO"]`. This will hold everyting that comes after the '/urls/'
323+
part.
324+
271325
## Note on Patches/Pull Requests
272326

273327
* Fork the project.
274328
* Make your feature addition or bug fix.
275-
* Add tests for it. This is important so I don't break it in a future version unintentionally.
276-
* Commit, do not mess with Rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
329+
* Add tests for it. This is important so I don't break it in a future version
330+
unintentionally.
331+
* Commit, do not mess with Rakefile, version, or history. (if you want to have
332+
your own version, that is fine but bump version in a commit by itself I can
333+
ignore when I pull)
277334
* Send me a pull request. Bonus points for topic branches.
278335

279336
## Copyright

lib/grape/endpoint.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ def prepare_routes
4848
options[:method].each do |method|
4949
options[:path].each do |path|
5050
prepared_path = prepare_path(path)
51-
path = compile_path(prepared_path, !options[:app])
51+
52+
anchor = options[:route_options][:anchor]
53+
anchor = anchor.nil? ? true : anchor
54+
55+
path = compile_path(prepared_path, anchor && !options[:app])
5256
regex = Rack::Mount::RegexpWithNamedGroups.new(path)
5357
path_params = {}
5458
# named parameters in the api path

spec/grape/endpoint_spec.rb

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,4 +215,35 @@ def memoized
215215
end
216216
end
217217
end
218+
219+
context 'anchoring' do
220+
verbs = %w(post get head delete put options)
221+
222+
verbs.each do |verb|
223+
it "should allow for the anchoring option with a #{verb.upcase} method" do
224+
subject.send(verb, '/example', :anchor => true) do
225+
verb
226+
end
227+
send(verb, '/example/and/some/more')
228+
last_response.status.should eql 404
229+
end
230+
231+
it "should anchor paths by default for the #{verb.upcase} method" do
232+
subject.send(verb, '/example') do
233+
verb
234+
end
235+
send(verb, '/example/and/some/more')
236+
last_response.status.should eql 404
237+
end
238+
239+
it "should respond to /example/and/some/more for the non-anchored #{verb.upcase} method" do
240+
subject.send(verb, '/example', :anchor => false) do
241+
verb
242+
end
243+
send(verb, '/example/and/some/more')
244+
last_response.status.should eql (verb == "post" ? 201 : 200)
245+
last_response.body.should eql verb
246+
end
247+
end
248+
end
218249
end

0 commit comments

Comments
 (0)