Skip to content
Browse files

gh

  • Loading branch information...
0 parents commit 732265e80f5cb9a3b6acb2f9d238dfef1bc46322 @softprops committed Nov 4, 2009
Showing with 280 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +39 −0 README.md
  3. +70 −0 app.rb
  4. +2 −0 config.ru
  5. +83 −0 lib/doc.rb
  6. +82 −0 test/doc_test.rb
  7. +3 −0 test/test_helper.rb
1 .gitignore
@@ -0,0 +1 @@
+.DS_Store
39 README.md
@@ -0,0 +1,39 @@
+# sinatra-doc
+
+self documentaion for your [sinatra]("http://sinatrarb.com") app's routes
+
+# usage
+
+ > your app.rb
+
+ class App < Sinatra::Base
+ register Sinatra::Doc
+
+ doc "gets a list of foos"
+ get "foos" { ... }
+
+ doc "gets a specific foo", {
+ :id => "identifier for a given foo"
+ }
+ get "foos/:id" { ... }
+ end
+
+ > GET /doc
+
+ sinatra doc
+
+ GET foos gets a list of foos
+
+ GET foos/:id gets a specific foo
+ :id identifier for a given foo
+
+# Props
+
+based on an idea [@bmizerany]("http://twitter.com/bmizerany") proposed in a [heroku]("http://heroku.com/") talk in nyc
+
+# TODO
+
+ * rake sinatra::doc #=> à la rails rake:routes
+ * clean up rendering of docs
+
+2009 softprops (doug tangren)
70 app.rb
@@ -0,0 +1,70 @@
+require 'sinatra/base'
+require File.join(File.dirname(__FILE__), *%w(lib doc))
+
+# a reference usage of Sinatra::Doc
+module Kittens
+ class App < Sinatra::Base
+ register Sinatra::Doc do
+ def title
+ "kittenz api"
+ end
+
+ def header
+ (<<-HEADER)
+ <h1>
+ <pre>
+ kittenz
+ _
+ \`*-.
+ ) _`-.
+ . : `. .
+ : _ ' \
+ ; *` _. `*-._
+ `-.-' `-.
+ ; ` `.
+ :. . \
+ . \ . : .-' .
+ ' `+.; ; ' :
+ : ' | ; ;-.
+ ; ' : :`-: _.`* ;
+ .*' / .*' ; .*`- +' `*'
+ `*-* `*-* `*-*'
+ </pre>
+ </h1>
+ HEADER
+ end
+ end
+
+ doc 'lists all kittens'
+ get '/kittens' do
+ '...'
+ end
+
+ doc 'gets a kitten by name', {
+ :name => "name of kitten"
+ }
+ get '/kittens/:name' do |name|
+ "..."
+ end
+
+ doc 'creates a new kitten'
+ post '/kittens' do
+ '...'
+ end
+
+ doc 'updates a kitten', {
+ :name => 'name of kitten'
+ }
+ put "/kittens/:id" do
+ '...'
+ end
+
+ doc 'deletes a given kitten', {
+ :name => 'name of kitten'
+ }
+ delete '/kittens/:name' do |name|
+ '...'
+ end
+
+ end
+end
2 config.ru
@@ -0,0 +1,2 @@
+require 'app'
+run Kittens::App
83 lib/doc.rb
@@ -0,0 +1,83 @@
+module Sinatra
+ # executable api documentation
+ module Doc
+ class Route
+ attr_accessor :desc, :params, :paths
+
+ def initialize(attrs={})
+ attrs.each_pair { |k,v| send "#{k}=",v if respond_to? "#{k}=" }
+ self.paths = []
+ end
+
+ def <<(path)
+ self.paths << path
+ end
+
+ def to_s
+ self.inspect
+ end
+
+ def inspect
+ "#{@paths.join(', ')} # #{@desc}"
+ end
+ end
+
+ def self.registered(app)
+ app.get '/doc' do
+ app.instance_eval { render_docs_page(@docs) }
+ end
+ end
+
+ def doc(desc, params = {})
+ @last_doc = Route.new(:desc => desc, :params => params)
+ (@docs ||= []) << @last_doc
+ end
+
+ def title
+ "sinatra doc"
+ end
+
+ def header
+ "<h1>%s</h1>" % title
+ end
+
+ def method_added(method)
+ return if method.to_s =~ /(^(GET|HEAD) \/doc\z)/
+ if method.to_s =~ /(GET|POST|PUT|DELETE|UPDATE|HEAD)/ && @last_doc
+ @last_doc << method
+ @last_doc = nil
+ end
+ super
+ end
+
+ def render_docs_list(routes)
+ routes.inject('<dl>') { |markup, route|
+ path = route.paths.join(', ')
+ desc = route.desc
+ params = route.params.inject('') { |li,(k,v)|
+ li << "<dt>:%s</dt><dd>%s</dd>" % [k,v]
+ }
+ markup << "<dt>%s</dt><dd>%s<dl>%s</dl></dd>" % [path, desc, params]
+ } << "</dl>"
+ end
+
+ def render_docs_page(routes)
+ (<<-HTML)
+ <html>
+ <head><title>#{title}</title></head>
+ <style type="text/css">
+ #container{width:960px; margin:1em auto; font-family:monaco, monospace;}
+ dt{ background:#f5f5f5; font-weight:bold; float:left; margin-right:1em; }
+ dd{ margin-left:1em; }
+ </style>
+ <body>
+ <div id="container">
+ #{header}
+ #{render_docs_list(routes)}
+ </div>
+ </body>
+ </html>
+ HTML
+ end
+ end
+end
82 test/doc_test.rb
@@ -0,0 +1,82 @@
+require File.join(File.dirname(__FILE__), *%w(test_helper))
+
+class DocTest < Test::Unit::TestCase
+ class A < Sinatra::Base
+ register Sinatra::Doc
+
+ doc "gets a list of materia"
+ get "/materia" do
+ "..."
+ end
+
+ get "/undocumented" do
+ "..."
+ end
+
+ doc "gets a specific materia", {
+ :kind => "color of the materia [red,green,blue,yellow,purple]"
+ }
+ get "/materia/:kind" do
+ "..."
+ end
+ end
+
+ class B < Sinatra::Base
+ register Sinatra::Doc do
+ def title
+ "Beez Kneez"
+ end
+ end
+
+ doc "get a list of bees"
+ get "/bees" do
+ "..."
+ end
+ end
+
+ def documented_app
+ A
+ end
+
+ def documented_app_with_overrides
+ B
+ end
+
+ context 'a documented sinatra app' do
+ should 'have a documented api' do
+ browser = Rack::Test::Session.new(
+ Rack::MockSession.new(documented_app)
+ )
+ browser.get '/doc'
+ assert browser.last_response.ok?
+ [
+ "GET /materia",
+ "gets a list of materia",
+ "/materia/:kind",
+ "gets a specific materia",
+ "color of the materia [red,green,blue,yellow,purple]"
+ ].each { |phrase|
+ assert browser.last_response.body.include?(phrase)
+ }
+
+ assert !browser.last_response.body.include?("/undocumented")
+ end
+
+ context "with doc overrides" do
+ should "render with overrides" do
+ browser = Rack::Test::Session.new(
+ Rack::MockSession.new(documented_app_with_overrides)
+ )
+ browser.get '/doc'
+ assert browser.last_response.ok?
+ [
+ "Beez Kneez",
+ "GET /bees",
+ "get a list of bees"
+ ].each { |phrase|
+ assert browser.last_response.body.include?(phrase)
+ }
+ end
+ end
+ end
+end
3 test/test_helper.rb
@@ -0,0 +1,3 @@
+require File.join(File.dirname(__FILE__), *%w(.. app))
+require 'shoulda'
+require 'rack/test'

0 comments on commit 732265e

Please sign in to comment.
Something went wrong with that request. Please try again.