Skip to content

Commit 948746b

Browse files
author
Michael Bleigh
committed
Adding namespacing and HTTP Basic authentication.
1 parent 6107b97 commit 948746b

File tree

5 files changed

+140
-7
lines changed

5 files changed

+140
-7
lines changed

README.rdoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ Grape is a REST-like API micro-framework for Ruby. It is built to complement exi
3030
end
3131
end
3232

33+
# Rack endpoint
34+
Twitter::API.statuses.timelines.get(:public_timeline)
35+
3336
class Twitter::API::User < Grape::Resource::ActiveRecord
3437
represents ::User
3538

lib/grape/api.rb

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ def settings
2222
@settings.inject({}){|f,h| f.merge!(h); f}
2323
end
2424

25+
def settings_stack
26+
@settings
27+
end
28+
2529
def set(key, value)
2630
@settings.last[key.to_sym] = value
2731
end
@@ -52,6 +56,7 @@ def compile_path(path)
5256
parts = []
5357
parts << prefix if prefix
5458
parts << version if version
59+
parts << namespace if namespace
5560
parts << path
5661
Rack::Mount::Utils.normalize_path(parts.join('/'))
5762
end
@@ -61,18 +66,35 @@ def route(method, path_info, &block)
6166
end
6267

6368
def build_endpoint(&block)
64-
builder = Rack::Builder.new
65-
builder.use Grape::Middleware::Error
66-
builder.use Grape::Middleware::Prefixer, :prefix => prefix if prefix
67-
builder.use Grape::Middleware::Versioner if version
68-
builder.use Grape::Middleware::Formatter, :default_format => default_format || :json
69-
builder.run Grape::Endpoint.new(&block)
70-
builder.to_app
69+
b = Rack::Builder.new
70+
b.use Grape::Middleware::Error
71+
b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix
72+
b.use Grape::Middleware::Versioner if version
73+
b.use Grape::Middleware::Formatter, :default_format => default_format || :json
74+
b.run Grape::Endpoint.new(&block)
75+
b.to_app
7176
end
7277

7378
def get(path_info, &block); route('GET', path_info, &block) end
7479
def post(path_info, &block); route('POST', path_info, &block) end
7580
def put(path_info, &block); route('PUT', path_info, &block) end
81+
def head(path_info, &block); route('HEAD', path_info, &block) end
82+
def delete(path_info, &block); route('DELETE', path_info, &block) end
83+
84+
def namespace(space = nil, &block)
85+
if space
86+
settings_stack << {}
87+
set(:namespace, space.to_s)
88+
instance_eval &block
89+
settings_stack.pop
90+
else
91+
Rack::Mount::Utils.normalize_path(settings_stack.map{|s| s[:namespace]}.join('/'))
92+
end
93+
end
94+
95+
alias_method :group, :namespace
96+
alias_method :resource, :namespace
97+
alias_method :resources, :namespace
7698

7799
def inherited(subclass)
78100
subclass.reset!

lib/grape/middleware/auth/basic.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
require 'rack/auth/basic'
2+
3+
module Grape
4+
module Middleware
5+
module Auth
6+
class Basic < Grape::Middleware::Base
7+
attr_reader :authenticator
8+
9+
def initialize(app, options = {}, &authenticator)
10+
super(app, options)
11+
@authenticator = authenticator
12+
end
13+
14+
def basic_request
15+
Rack::Auth::Basic::Request.new(env)
16+
end
17+
18+
def credentials
19+
basic_request.provided?? basic_request.credentials : [nil, nil]
20+
end
21+
22+
def before
23+
unless authenticator.call(*credentials)
24+
throw :error, :status => 401, :message => "API Authorization Failed."
25+
end
26+
end
27+
end
28+
end
29+
end
30+
end

spec/grape/api_spec.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,47 @@ def app; subject end
4343
last_response.body.should == "Version: v1"
4444
end
4545
end
46+
47+
describe '.namespace' do
48+
it 'should be retrievable and converted to a path' do
49+
subject.namespace :awesome do
50+
namespace.should == '/awesome'
51+
end
52+
end
53+
54+
it 'should come after the prefix and version' do
55+
subject.prefix :rad
56+
subject.version :v1
57+
58+
subject.namespace :awesome do
59+
compile_path('hello').should == '/rad/v1/awesome/hello'
60+
end
61+
end
62+
63+
it 'should cancel itself after the block is over' do
64+
subject.namespace :awesome do
65+
namespace.should == '/awesome'
66+
end
67+
68+
subject.namespace.should == '/'
69+
end
70+
71+
it 'should be stackable' do
72+
subject.namespace :awesome do
73+
namespace :rad do
74+
namespace.should == '/awesome/rad'
75+
end
76+
namespace.should == '/awesome'
77+
end
78+
subject.namespace.should == '/'
79+
end
80+
81+
%w(group resource resources).each do |als|
82+
it "`.#{als}` should be an alias" do
83+
subject.send(als, :awesome) do
84+
namespace.should == "/awesome"
85+
end
86+
end
87+
end
88+
end
4689
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
require 'spec_helper'
2+
3+
require 'base64'
4+
5+
describe Grape::Middleware::Auth::Basic do
6+
def app
7+
Rack::Builder.new do |b|
8+
b.use Grape::Middleware::Error
9+
b.use(Grape::Middleware::Auth::Basic) do |u,p|
10+
u && p && u == p
11+
end
12+
b.run lambda{|env| [200, {}, "Hello there."]}
13+
end
14+
end
15+
16+
def encode(username, password)
17+
"Basic " + Base64.encode64("#{username}:#{password}")
18+
end
19+
20+
it 'should throw a 401 if no auth is given' do
21+
@proc = lambda{ false }
22+
get '/whatever'
23+
last_response.status.should == 401
24+
end
25+
26+
it 'should authenticate if given valid creds' do
27+
get '/whatever', {}, 'HTTP_AUTHORIZATION' => encode('admin','admin')
28+
last_response.status.should == 200
29+
end
30+
31+
it 'should throw a 401 is wrong auth is given' do
32+
get '/whatever', {}, 'HTTP_AUTHORIZATION' => encode('admin','wrong')
33+
last_response.status.should == 401
34+
end
35+
end

0 commit comments

Comments
 (0)