sinatra-doc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ .DS_Store
@@ -0,0 +1,41 @@
1
+ # sinatra-doc
2
+
3
+ self documentaion for your [sinatra]("http://sinatrarb.com") app's routes
4
+
5
+ # usage
6
+
7
+ see the reference implementation [app.rb](http://github.com/softprops/sinatra-doc/blob/master/app.rb)
8
+
9
+ > your app.rb
10
+
11
+ class App < Sinatra::Base
12
+ register Sinatra::Doc
13
+
14
+ doc "gets a list of foos"
15
+ get "foos" { ... }
16
+
17
+ doc "gets a specific foo", {
18
+ :id => "identifier for a given foo"
19
+ }
20
+ get "foos/:id" { ... }
21
+ end
22
+
23
+ > GET /doc
24
+
25
+ sinatra doc
26
+
27
+ GET foos gets a list of foos
28
+
29
+ GET foos/:id gets a specific foo
30
+ :id identifier for a given foo
31
+
32
+ # Props
33
+
34
+ based on an idea [@bmizerany]("http://twitter.com/bmizerany") proposed in a [heroku]("http://heroku.com/") talk in nyc
35
+
36
+ # TODO
37
+
38
+ * rake sinatra::doc #=> à la rails rake:routes
39
+ * clean up rendering of docs
40
+
41
+ 2009 softprops (doug tangren)
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/app.rb ADDED
@@ -0,0 +1,70 @@
1
+ require 'sinatra/base'
2
+ require File.join(File.dirname(__FILE__), *%w(lib doc))
3
+
4
+ # a reference usage of Sinatra::Doc
5
+ module Kittens
6
+ class App < Sinatra::Base
7
+ register Sinatra::Doc do
8
+ def title
9
+ "kittenz api"
10
+ end
11
+
12
+ def header
13
+ (<<-HEADER)
14
+ <h1>
15
+ <pre>
16
+ kittenz
17
+ _
18
+ \`*-.
19
+ ) _`-.
20
+ . : `. .
21
+ : _ ' \
22
+ ; *` _. `*-._
23
+ `-.-' `-.
24
+ ; ` `.
25
+ :. . \
26
+ . \ . : .-' .
27
+ ' `+.; ; ' :
28
+ : ' | ; ;-.
29
+ ; ' : :`-: _.`* ;
30
+ .*' / .*' ; .*`- +' `*'
31
+ `*-* `*-* `*-*'
32
+ </pre>
33
+ </h1>
34
+ HEADER
35
+ end
36
+ end
37
+
38
+ doc 'lists all kittens'
39
+ get '/kittens' do
40
+ '...'
41
+ end
42
+
43
+ doc 'gets a kitten by name', {
44
+ :name => "name of kitten"
45
+ }
46
+ get '/kittens/:name' do |name|
47
+ "..."
48
+ end
49
+
50
+ doc 'creates a new kitten'
51
+ post '/kittens' do
52
+ '...'
53
+ end
54
+
55
+ doc 'updates a kitten', {
56
+ :name => 'name of kitten'
57
+ }
58
+ put "/kittens/:id" do
59
+ '...'
60
+ end
61
+
62
+ doc 'deletes a given kitten', {
63
+ :name => 'name of kitten'
64
+ }
65
+ delete '/kittens/:name' do |name|
66
+ '...'
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,2 @@
1
+ require 'app'
2
+ run Kittens::App
@@ -0,0 +1,83 @@
1
+ module Sinatra
2
+ # executable api documentation
3
+ module Doc
4
+ class Route
5
+ attr_accessor :desc, :params, :paths
6
+
7
+ def initialize(attrs={})
8
+ attrs.each_pair { |k,v| send "#{k}=",v if respond_to? "#{k}=" }
9
+ self.paths = []
10
+ end
11
+
12
+ def <<(path)
13
+ self.paths << path
14
+ end
15
+
16
+ def to_s
17
+ self.inspect
18
+ end
19
+
20
+ def inspect
21
+ "#{@paths.join(', ')} # #{@desc}"
22
+ end
23
+ end
24
+
25
+ def self.registered(app)
26
+ app.get '/doc' do
27
+ app.instance_eval { render_docs_page(@docs) }
28
+ end
29
+ end
30
+
31
+ def doc(desc, params = {})
32
+ @last_doc = Route.new(:desc => desc, :params => params)
33
+ (@docs ||= []) << @last_doc
34
+ end
35
+
36
+ def title
37
+ "sinatra doc"
38
+ end
39
+
40
+ def header
41
+ "<h1>%s</h1>" % title
42
+ end
43
+
44
+ def method_added(method)
45
+ return if method.to_s =~ /(^(GET|HEAD) \/doc\z)/
46
+ if method.to_s =~ /(GET|POST|PUT|DELETE|UPDATE|HEAD)/ && @last_doc
47
+ @last_doc << method
48
+ @last_doc = nil
49
+ end
50
+ super
51
+ end
52
+
53
+ def render_docs_list(routes)
54
+ routes.inject('<dl>') { |markup, route|
55
+ path = route.paths.join(', ')
56
+ desc = route.desc
57
+ params = route.params.inject('') { |li,(k,v)|
58
+ li << "<dt>:%s</dt><dd>%s</dd>" % [k,v]
59
+ }
60
+ markup << "<dt>%s</dt><dd>%s<dl>%s</dl></dd>" % [path, desc, params]
61
+ } << "</dl>"
62
+ end
63
+
64
+ def render_docs_page(routes)
65
+ (<<-HTML)
66
+ <html>
67
+ <head><title>#{title}</title></head>
68
+ <style type="text/css">
69
+ #container{width:960px; margin:1em auto; font-family:monaco, monospace;}
70
+ dt{ background:#f5f5f5; font-weight:bold; float:left; margin-right:1em; }
71
+ dd{ margin-left:1em; }
72
+ </style>
73
+ <body>
74
+ <div id="container">
75
+ #{header}
76
+ #{render_docs_list(routes)}
77
+ </div>
78
+ </body>
79
+ </html>
80
+ HTML
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,82 @@
1
+ require File.join(File.dirname(__FILE__), *%w(test_helper))
2
+
3
+ class DocTest < Test::Unit::TestCase
4
+ class A < Sinatra::Base
5
+ register Sinatra::Doc
6
+
7
+ doc "gets a list of materia"
8
+ get "/materia" do
9
+ "..."
10
+ end
11
+
12
+ get "/undocumented" do
13
+ "..."
14
+ end
15
+
16
+ doc "gets a specific materia", {
17
+ :kind => "color of the materia [red,green,blue,yellow,purple]"
18
+ }
19
+ get "/materia/:kind" do
20
+ "..."
21
+ end
22
+ end
23
+
24
+ class B < Sinatra::Base
25
+ register Sinatra::Doc do
26
+ def title
27
+ "Beez Kneez"
28
+ end
29
+ end
30
+
31
+ doc "get a list of bees"
32
+ get "/bees" do
33
+ "..."
34
+ end
35
+ end
36
+
37
+ def documented_app
38
+ A
39
+ end
40
+
41
+ def documented_app_with_overrides
42
+ B
43
+ end
44
+
45
+ context 'a documented sinatra app' do
46
+ should 'have a documented api' do
47
+ browser = Rack::Test::Session.new(
48
+ Rack::MockSession.new(documented_app)
49
+ )
50
+ browser.get '/doc'
51
+ assert browser.last_response.ok?
52
+ [
53
+ "GET /materia",
54
+ "gets a list of materia",
55
+ "/materia/:kind",
56
+ "gets a specific materia",
57
+ "color of the materia [red,green,blue,yellow,purple]"
58
+ ].each { |phrase|
59
+ assert browser.last_response.body.include?(phrase)
60
+ }
61
+
62
+ assert !browser.last_response.body.include?("/undocumented")
63
+ end
64
+
65
+ context "with doc overrides" do
66
+ should "render with overrides" do
67
+ browser = Rack::Test::Session.new(
68
+ Rack::MockSession.new(documented_app_with_overrides)
69
+ )
70
+ browser.get '/doc'
71
+ assert browser.last_response.ok?
72
+ [
73
+ "Beez Kneez",
74
+ "GET /bees",
75
+ "get a list of bees"
76
+ ].each { |phrase|
77
+ assert browser.last_response.body.include?(phrase)
78
+ }
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,3 @@
1
+ require File.join(File.dirname(__FILE__), *%w(.. app))
2
+ require 'shoulda'
3
+ require 'rack/test'
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sinatra-doc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Doug Tangren
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-04 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: self documentaion for your sinatra app's routes
17
+ email: d.tangren@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.md
24
+ files:
25
+ - .gitignore
26
+ - README.md
27
+ - VERSION
28
+ - app.rb
29
+ - config.ru
30
+ - lib/doc.rb
31
+ - test/doc_test.rb
32
+ - test/test_helper.rb
33
+ has_rdoc: true
34
+ homepage: http://github.com/softprops/sinatra-doc
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options:
39
+ - --charset=UTF-8
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.3.5
58
+ signing_key:
59
+ specification_version: 3
60
+ summary: self documentaion for your sinatra app's routes
61
+ test_files:
62
+ - test/doc_test.rb
63
+ - test/test_helper.rb