Skip to content

Commit 90d4278

Browse files
author
Michael Bleigh
committed
Added versioner middleware.
1 parent 1cd901f commit 90d4278

File tree

6 files changed

+96
-11
lines changed

6 files changed

+96
-11
lines changed

README.rdoc

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,28 @@
33
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.
44

55
class Twitter < Grape::Base
6-
# Set this API to work only on a specified subdomain
76
subdomain 'api'
7+
version '1'
8+
formats :xml, :json
9+
authorization :oauth, User
810

9-
# Use a path prefix.
10-
path_prefix 'api'
11-
12-
version 'v2'
13-
14-
version 'v1' do
11+
resource :statuses do
12+
group :timelines do
13+
formats :rss, :atom
14+
15+
get :public_timeline do
16+
optional :trim_user, Boolean
17+
optional :include_entities, Boolean
18+
19+
Tweet.limit(20)
20+
end
1521

22+
get :home_timeline do
23+
authorized
24+
25+
user.home_timeline
26+
end
27+
end
1628
end
1729
end
1830

lib/grape.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
require 'rack/builder'
33

44
require 'grape/middleware/base'
5-
require 'grape/middleware/prefixer'
5+
require 'grape/middleware/prefixer'
6+
require 'grape/middleware/versioner'

lib/grape/middleware/base.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ class Base
55

66
def initialize(app, options = {})
77
@app = app
8-
@options = options
8+
@options = default_options.merge(options)
99
end
1010

11+
def default_options; {} end
12+
1113
def call(env)
1214
dup.call!(env)
1315
end

lib/grape/middleware/versioner.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
require 'grape/middleware/base'
2+
3+
module Grape
4+
module Middleware
5+
class Versioner < Base
6+
def default_options
7+
{
8+
:pattern => /.*/i
9+
}
10+
end
11+
12+
def before
13+
pieces = env['PATH_INFO'].split('/')
14+
potential_version = pieces[1]
15+
if potential_version =~ options[:pattern]
16+
truncated_path = "/#{pieces[2..-1].join('/')}"
17+
env['api.version'] = potential_version
18+
env['PATH_INFO'] = truncated_path
19+
end
20+
end
21+
end
22+
end
23+
end

spec/grape/middleware/base_spec.rb

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,25 @@
3939
subject.response.should be_kind_of(Rack::Response)
4040
end
4141

42-
it 'should persist options passed at initialization' do
43-
Grape::Middleware::Base.new(blank_app, {:abc => true}).options[:abc].should be_true
42+
context 'options' do
43+
it 'should persist options passed at initialization' do
44+
Grape::Middleware::Base.new(blank_app, {:abc => true}).options[:abc].should be_true
45+
end
46+
47+
context 'defaults' do
48+
class ExampleWare < Grape::Middleware::Base
49+
def default_options
50+
{:monkey => true}
51+
end
52+
end
53+
54+
it 'should persist the default options' do
55+
ExampleWare.new(blank_app).options[:monkey].should be_true
56+
end
57+
58+
it 'should override default options when provided' do
59+
ExampleWare.new(blank_app, :monkey => false).options[:monkey].should be_false
60+
end
61+
end
4462
end
4563
end
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
require 'spec_helper'
2+
3+
describe Grape::Middleware::Versioner do
4+
let(:app) { lambda{|env| [200, env, env['api.version']]} }
5+
subject { Grape::Middleware::Versioner.new(app, @options || {}) }
6+
7+
it 'should set the API version based on the first path' do
8+
subject.call('PATH_INFO' => '/v1/awesome').last.should == 'v1'
9+
end
10+
11+
it 'should cut the version out of the path' do
12+
subject.call('PATH_INFO' => '/v1/awesome')[1]['PATH_INFO'].should == '/awesome'
13+
end
14+
15+
it 'should provide a nil version if no path is given' do
16+
subject.call('PATH_INFO' => '/').last.should be_nil
17+
end
18+
19+
context 'with a pattern' do
20+
before{ @options = {:pattern => /v./i} }
21+
it 'should set the version if it matches' do
22+
subject.call('PATH_INFO' => '/v1/awesome').last.should == 'v1'
23+
end
24+
25+
it 'should ignore the version if it fails to match' do
26+
subject.call('PATH_INFO' => '/awesome/radical').last.should be_nil
27+
end
28+
end
29+
end

0 commit comments

Comments
 (0)