Skip to content

Commit 63fdf74

Browse files
author
Michael Bleigh
committed
Add a little more documentation to the API DSL, properly protect some methods.
1 parent e690f37 commit 63fdf74

File tree

5 files changed

+144
-49
lines changed

5 files changed

+144
-49
lines changed

Gemfile

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
source 'http://rubygems.org'
2+
13
gem 'rack'
24
gem 'rack-mount', '~> 0.6.13'
35
gem 'rack-jsonp'
@@ -9,6 +11,7 @@ gem 'multi_xml'
911
group :development do
1012
gem 'rake'
1113
gem 'jeweler'
14+
# gem 'yard'
1215
end
1316

1417
group :test do

Gemfile.lock

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
GEM
2+
remote: http://rubygems.org/
23
specs:
34
builder (2.1.2)
45
cucumber (0.8.5)

Rakefile

+35-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ require 'bundler'
33

44
Bundler.setup :default, :test, :development
55

6+
def version
7+
@version ||= open('VERSION').read.trim
8+
end
9+
610
begin
711
require 'jeweler'
812
Jeweler::Tasks.new do |gem|
@@ -33,12 +37,34 @@ end
3337
task :spec => :check_dependencies
3438
task :default => :spec
3539

36-
require 'rake/rdoctask'
37-
Rake::RDocTask.new do |rdoc|
38-
version = File.exist?('VERSION') ? File.read('VERSION') : ""
39-
40-
rdoc.rdoc_dir = 'rdoc'
41-
rdoc.title = "grape #{version}"
42-
rdoc.rdoc_files.include('README*')
43-
rdoc.rdoc_files.include('lib/**/*.rb')
44-
end
40+
begin
41+
require 'yard'
42+
YARD_OPTS = ['-m', 'markdown', '-M', 'maruku']
43+
DOC_FILES = ['lib/**/*.rb', 'README.markdown']
44+
45+
YARD::Rake::YardocTask.new(:doc) do |t|
46+
t.files = DOC_FILES
47+
t.options = YARD_OPTS
48+
end
49+
50+
namespace :doc do
51+
YARD::Rake::YardocTask.new(:pages) do |t|
52+
t.files = DOC_FILES
53+
t.options = YARD_OPTS + ['-o', '../grape.doc']
54+
end
55+
56+
namespace :pages do
57+
desc 'Generate and publish YARD docs to GitHub pages.'
58+
task :publish => ['doc:pages'] do
59+
Dir.chdir(File.dirname(__FILE__) + '/../grape.doc') do
60+
system("git add .")
61+
system("git add -u")
62+
system("git commit -m 'Generating docs for version #{version}.'")
63+
system("git push origin gh-pages")
64+
end
65+
end
66+
end
67+
end
68+
rescue LoadError
69+
puts "You need to install YARD."
70+
end

lib/grape/api.rb

+71-39
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require 'rack/mount'
22
require 'rack/auth/basic'
3+
require 'logger'
34

45
module Grape
56
class API
@@ -8,14 +9,18 @@ module Helpers; end
89
class << self
910
attr_reader :route_set
1011

12+
def logger
13+
@logger ||= Logger.new($STDOUT)
14+
end
15+
1116
def reset!
1217
@settings = [{}]
1318
@route_set = Rack::Mount::RouteSet.new
1419
@prototype = nil
1520
end
1621

1722
def call(env)
18-
puts "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
23+
logger.info "#{env['REQUEST_METHOD']} #{env['PATH_INFO']}"
1924
route_set.freeze.call(env)
2025
end
2126

@@ -30,6 +35,11 @@ def settings_stack
3035
@settings
3136
end
3237

38+
# Set a configuration value for this
39+
# namespace.
40+
#
41+
# @param key [Symbol] The key of the configuration variable.
42+
# @param value [Object] The value to which to set the configuration variable.
3343
def set(key, value)
3444
@settings.last[key.to_sym] = value
3545
end
@@ -48,6 +58,9 @@ def version(*new_versions, &block)
4858
new_versions.any? ? nest(block){ set(:version, new_versions) } : settings[:version]
4959
end
5060

61+
# Specify the default format for the API's
62+
# serializers. Currently only `:json` is
63+
# supported.
5164
def default_format(new_format = nil)
5265
new_format ? set(:default_format, new_format.to_sym) : settings[:default_format]
5366
end
@@ -76,6 +89,8 @@ def helpers(&block)
7689
end
7790
end
7891

92+
# Add an authentication type to the API. Currently
93+
# only `:http_basic` is supported.
7994
def auth(type = nil, options = {}, &block)
8095
if type
8196
set(:auth, {:type => type.to_sym, :proc => block}.merge(options))
@@ -93,47 +108,34 @@ def http_basic(options = {}, &block)
93108
auth :http_basic, options, &block
94109
end
95110

96-
def route_set
97-
@route_set ||= Rack::Mount::RouteSet.new
98-
end
99-
100-
def compile_path(path)
101-
parts = []
102-
parts << prefix if prefix
103-
parts << ':version' if version
104-
parts << namespace if namespace
105-
parts << path
106-
Rack::Mount::Utils.normalize_path(parts.join('/'))
107-
end
108-
109-
def route(method, path_info, &block)
110-
route_set.add_route(build_endpoint(&block),
111-
:path_info => Rack::Mount::Strexp.compile(compile_path(path_info)),
112-
:request_method => method
113-
)
114-
end
115-
116-
def build_endpoint(&block)
117-
118-
b = Rack::Builder.new
119-
b.use Grape::Middleware::Error
120-
b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
121-
b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix
122-
b.use Grape::Middleware::Versioner, :versions => (version if version.is_a?(Array)) if version
123-
b.use Grape::Middleware::Formatter, :default_format => default_format || :json
124-
125-
endpoint = Grape::Endpoint.generate(&block)
126-
endpoint.send :include, helpers
127-
b.run endpoint
111+
# Defines a route that will be recognized
112+
# by the Grape API.
113+
#
114+
# @param methods [HTTP Verb(s)] One or more HTTP verbs that are accepted by this route. Set to `:any` if you want any verb to be accepted.
115+
# @param paths [String(s)] One or more strings representing the URL segment(s) for this route.
116+
# @param block [Proc] The code to be executed
117+
def route(methods, paths, &block)
118+
methods = Array(methods)
119+
paths = ['/'] if paths == []
120+
paths = Array(paths)
121+
endpoint = build_endpoint(&block)
128122

129-
b.to_app
123+
methods.each do |method|
124+
paths.each do |path|
125+
path = Rack::Mount::Strexp.compile(compile_path(path))
126+
route_set.add_route(endpoint,
127+
:path_info => path,
128+
:request_method => (method.to_s.upcase unless method == :any)
129+
)
130+
end
131+
end
130132
end
131133

132-
def get(path_info = '', &block); route('GET', path_info, &block) end
133-
def post(path_info = '', &block); route('POST', path_info, &block) end
134-
def put(path_info = '', &block); route('PUT', path_info, &block) end
135-
def head(path_info = '', &block); route('HEAD', path_info, &block) end
136-
def delete(path_info = '', &block); route('DELETE', path_info, &block) end
134+
def get(*paths, &block); route('GET', paths, &block) end
135+
def post(*paths, &block); route('POST', paths, &block) end
136+
def put(*paths, &block); route('PUT', paths, &block) end
137+
def head(*paths, &block); route('HEAD', paths, &block) end
138+
def delete(*paths, &block); route('DELETE', paths, &block) end
137139

138140
def namespace(space = nil, &block)
139141
if space || block_given?
@@ -164,9 +166,39 @@ def nest(*blocks, &block)
164166
alias_method :resource, :namespace
165167
alias_method :resources, :namespace
166168

169+
protected
170+
171+
def build_endpoint(&block)
172+
b = Rack::Builder.new
173+
b.use Grape::Middleware::Error
174+
b.use Rack::Auth::Basic, settings[:auth][:realm], &settings[:auth][:proc] if settings[:auth] && settings[:auth][:type] == :http_basic
175+
b.use Grape::Middleware::Prefixer, :prefix => prefix if prefix
176+
b.use Grape::Middleware::Versioner, :versions => (version if version.is_a?(Array)) if version
177+
b.use Grape::Middleware::Formatter, :default_format => default_format || :json
178+
179+
endpoint = Grape::Endpoint.generate(&block)
180+
endpoint.send :include, helpers
181+
b.run endpoint
182+
183+
b.to_app
184+
end
185+
167186
def inherited(subclass)
168187
subclass.reset!
169188
end
189+
190+
def route_set
191+
@route_set ||= Rack::Mount::RouteSet.new
192+
end
193+
194+
def compile_path(path)
195+
parts = []
196+
parts << prefix if prefix
197+
parts << ':version' if version
198+
parts << namespace if namespace
199+
parts << path
200+
Rack::Mount::Utils.normalize_path(parts.join('/'))
201+
end
170202
end
171203

172204
reset!

spec/grape/api_spec.rb

+34-1
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def app; subject end
119119
version 'v2'
120120
compile_path('hello').should == '/:version/hello'
121121
end
122-
subject.compile_path('hello').should == '/hello'
122+
subject.send(:compile_path, 'hello').should == '/hello'
123123
end
124124

125125
%w(group resource resources).each do |als|
@@ -149,6 +149,39 @@ def app; subject end
149149
last_response.body.should == 'Created a Vote'
150150
end
151151

152+
it 'should allow for multiple paths' do
153+
subject.get("/abc", "/def") do
154+
"foo"
155+
end
156+
157+
get '/abc'
158+
last_response.body.should == 'foo'
159+
get '/def'
160+
last_response.body.should == 'foo'
161+
end
162+
163+
it 'should allow for multiple verbs' do
164+
subject.route([:get, :post], '/abc') do
165+
"hiya"
166+
end
167+
168+
get '/abc'
169+
last_response.body.should == 'hiya'
170+
post '/abc'
171+
last_response.body.should == 'hiya'
172+
end
173+
174+
it 'should allow for :any as a verb' do
175+
subject.route(:any, '/abc') do
176+
"lol"
177+
end
178+
179+
%w(get post put delete).each do |m|
180+
send(m, '/abc')
181+
last_response.body.should == 'lol'
182+
end
183+
end
184+
152185
verbs = %w(post get head delete put)
153186
verbs.each do |verb|
154187
it "should allow and properly constrain a #{verb.upcase} method" do

0 commit comments

Comments
 (0)