apidoc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +48 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +59 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/apidoc.gemspec +89 -0
- data/bin/apidoc +6 -0
- data/examples/authentication.rb +8 -0
- data/examples/burritos_api.html +190 -0
- data/examples/burritos_api.rb +37 -0
- data/examples/tacos_api.html +177 -0
- data/examples/tacos_api.rb +34 -0
- data/ftags +23 -0
- data/lib/apidoc.rb +255 -0
- data/spec/apidoc_spec.rb +211 -0
- data/spec/spec_helper.rb +12 -0
- data/templates/apidoc_helper.rb +11 -0
- data/templates/layout.mustache +163 -0
- data/templates/resource.mustache +7 -0
- metadata +183 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
3
|
+
require 'apidoc'
|
|
4
|
+
require 'sinatra/base'
|
|
5
|
+
|
|
6
|
+
class BurritoApp < Sinatra::Base
|
|
7
|
+
|
|
8
|
+
get '/burritos.json' do
|
|
9
|
+
JSON.generate [ { meat: 'chicken' }, { meat: 'tofu' } ]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
post '/burritos.json' do
|
|
13
|
+
request.body
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
doc = ApiDoc.new BurritoApp do
|
|
19
|
+
|
|
20
|
+
accept :json
|
|
21
|
+
content_type :json
|
|
22
|
+
|
|
23
|
+
get '/burritos.json' do
|
|
24
|
+
desc "Get all burritos"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
post '/burritos.json' do
|
|
28
|
+
desc "Make a new delicious burrito"
|
|
29
|
+
params do
|
|
30
|
+
JSON.generate({ meat: 'beef', lettuce: true })
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
ApiDoc::HtmlWriter.new(doc).write(STDOUT)
|
|
37
|
+
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<title></title>
|
|
4
|
+
<style>
|
|
5
|
+
|
|
6
|
+
html, body, div, span, applet, object, iframe,
|
|
7
|
+
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
|
8
|
+
a, abbr, acronym, address, big, cite, code,
|
|
9
|
+
del, dfn, em, img, ins, kbd, q, s, samp,
|
|
10
|
+
small, strike, strong, sub, sup, tt, var,
|
|
11
|
+
b, u, i, center,
|
|
12
|
+
dl, dt, dd, ol, ul, li,
|
|
13
|
+
fieldset, form, label, legend,
|
|
14
|
+
table, caption, tbody, tfoot, thead, tr, th, td,
|
|
15
|
+
article, aside, canvas, details, embed,
|
|
16
|
+
figure, figcaption, footer, header, hgroup,
|
|
17
|
+
menu, nav, output, ruby, section, summary,
|
|
18
|
+
time, mark, audio, video {
|
|
19
|
+
margin: 0;
|
|
20
|
+
padding: 0;
|
|
21
|
+
border: 0;
|
|
22
|
+
font-size: 100%;
|
|
23
|
+
font: inherit;
|
|
24
|
+
vertical-align: baseline;
|
|
25
|
+
}
|
|
26
|
+
/* HTML5 display-role reset for older browsers */
|
|
27
|
+
article, aside, details, figcaption, figure,
|
|
28
|
+
footer, header, hgroup, menu, nav, section {
|
|
29
|
+
display: block;
|
|
30
|
+
}
|
|
31
|
+
body {
|
|
32
|
+
line-height: 1;
|
|
33
|
+
}
|
|
34
|
+
ol, ul {
|
|
35
|
+
list-style: none;
|
|
36
|
+
}
|
|
37
|
+
blockquote, q {
|
|
38
|
+
quotes: none;
|
|
39
|
+
}
|
|
40
|
+
blockquote:before, blockquote:after,
|
|
41
|
+
q:before, q:after {
|
|
42
|
+
content: '';
|
|
43
|
+
content: none;
|
|
44
|
+
}
|
|
45
|
+
table {
|
|
46
|
+
border-collapse: collapse;
|
|
47
|
+
border-spacing: 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
body {
|
|
55
|
+
font-family: Helvetica;
|
|
56
|
+
margin: 1em;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.name {
|
|
60
|
+
font-size: 2.0em;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.api-resource {
|
|
64
|
+
margin: 1em 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.api-resource .method {
|
|
68
|
+
font-size: 1em;
|
|
69
|
+
background-color: #999;
|
|
70
|
+
background-image: -webkit-linear-gradient(top,
|
|
71
|
+
transparent 0%,
|
|
72
|
+
rgba(0, 0, 0, 0.296875) 100%);
|
|
73
|
+
background-origin: padding-box;
|
|
74
|
+
border-bottom-color:
|
|
75
|
+
white;
|
|
76
|
+
border-bottom-left-radius: 0px;
|
|
77
|
+
border-bottom-style: none;
|
|
78
|
+
border-bottom-width: 0px;
|
|
79
|
+
border-left-color:
|
|
80
|
+
white;
|
|
81
|
+
border-left-style: none;
|
|
82
|
+
border-left-width: 0px;
|
|
83
|
+
border-right-color:
|
|
84
|
+
white;
|
|
85
|
+
border-right-style: none;
|
|
86
|
+
border-right-width: 0px;
|
|
87
|
+
border-top-color:
|
|
88
|
+
white;
|
|
89
|
+
border-top-left-radius: 3px;
|
|
90
|
+
border-top-style: none;
|
|
91
|
+
border-top-width: 0px;
|
|
92
|
+
color:
|
|
93
|
+
white;
|
|
94
|
+
text-align: center;
|
|
95
|
+
text-shadow:
|
|
96
|
+
rgba(0, 0, 0, 0.296875) 0px -1px 0px;
|
|
97
|
+
text-transform: uppercase;
|
|
98
|
+
vertical-align: baseline;
|
|
99
|
+
padding: 0.5em;
|
|
100
|
+
display: inline-block;
|
|
101
|
+
width: 84px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.api-resource .path {
|
|
105
|
+
display: inline-block;
|
|
106
|
+
font-family: Monaco, fixed;
|
|
107
|
+
color: #0669CD;
|
|
108
|
+
font-size: 1em;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.api-resource .desc {
|
|
112
|
+
margin: 1em 0;
|
|
113
|
+
color:
|
|
114
|
+
#333;
|
|
115
|
+
display: block;
|
|
116
|
+
font-family: Helvetica, sans-serif;
|
|
117
|
+
font-size: 13px;
|
|
118
|
+
font-style: normal;
|
|
119
|
+
font-weight: 100;
|
|
120
|
+
height: 17px;
|
|
121
|
+
line-height: 17px;
|
|
122
|
+
text-shadow:
|
|
123
|
+
rgba(255, 255, 255, 0.597656) 0px 1px 0px;
|
|
124
|
+
vertical-align: baseline;
|
|
125
|
+
}
|
|
126
|
+
.api-resource .params,
|
|
127
|
+
.api-resource .response {
|
|
128
|
+
font-family: Monaco, fixed;
|
|
129
|
+
margin: 1em 0;
|
|
130
|
+
overflow: scroll;
|
|
131
|
+
white-space: pre;
|
|
132
|
+
background-color: #333333;
|
|
133
|
+
color: #8B8B8B;
|
|
134
|
+
padding: 1em;
|
|
135
|
+
border-radius: 2px;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.api-resource .params:before {
|
|
139
|
+
content: "IN";
|
|
140
|
+
color: #C29262;
|
|
141
|
+
display: block;
|
|
142
|
+
font-family: Helvetica;
|
|
143
|
+
margin: 0.5em 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.api-resource .response:before {
|
|
147
|
+
content: "OUT";
|
|
148
|
+
display: block;
|
|
149
|
+
color: #C29262;
|
|
150
|
+
font-family: Helvetica;
|
|
151
|
+
margin: 0.5em 0;
|
|
152
|
+
}
|
|
153
|
+
</style>
|
|
154
|
+
</head>
|
|
155
|
+
|
|
156
|
+
<body>
|
|
157
|
+
<div class="api">
|
|
158
|
+
<div class="name"></div>
|
|
159
|
+
<div class="api-resource">
|
|
160
|
+
<div class="method">GET</div>
|
|
161
|
+
<div class="path">/tacos.json</div>
|
|
162
|
+
<div class="desc">Get all tacos</div>
|
|
163
|
+
<div class="params"></div>
|
|
164
|
+
<div class="response">[{"meat":"chicken"}]</div>
|
|
165
|
+
</div>
|
|
166
|
+
<div class="api-resource">
|
|
167
|
+
<div class="method">POST</div>
|
|
168
|
+
<div class="path">/tacos.json</div>
|
|
169
|
+
<div class="desc">Make a new delicious taco</div>
|
|
170
|
+
<div class="params">{"meat":"beef","lettuce":true}</div>
|
|
171
|
+
<div class="response">{"meat":"beef","lettuce":true}</div>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
</div>
|
|
175
|
+
</body>
|
|
176
|
+
</html>
|
|
177
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
3
|
+
require 'apidoc'
|
|
4
|
+
require 'sinatra/base'
|
|
5
|
+
|
|
6
|
+
class TacoApp < Sinatra::Base
|
|
7
|
+
|
|
8
|
+
get '/tacos.json' do
|
|
9
|
+
JSON.generate [ { meat: 'chicken' } ]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
post '/tacos.json' do
|
|
13
|
+
request.body
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
doc = ApiDoc.new TacoApp do
|
|
19
|
+
|
|
20
|
+
get '/tacos.json' do
|
|
21
|
+
desc "Get all tacos"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
post '/tacos.json' do
|
|
25
|
+
desc "Make a new delicious taco"
|
|
26
|
+
params do
|
|
27
|
+
JSON.generate({ meat: 'beef', lettuce: true })
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
ApiDoc::HtmlWriter.new(doc).write(STDOUT)
|
|
34
|
+
|
data/ftags
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
|
|
2
|
+
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
|
|
3
|
+
!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/
|
|
4
|
+
!_TAG_PROGRAM_NAME Exuberant Ctags //
|
|
5
|
+
!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
|
|
6
|
+
!_TAG_PROGRAM_VERSION 5.7 //
|
|
7
|
+
.document ./.document /^/;" r
|
|
8
|
+
.rspec ./.rspec /^/;" r
|
|
9
|
+
Gemfile ./Gemfile /^/;" r
|
|
10
|
+
Gemfile.lock ./Gemfile.lock /^/;" r
|
|
11
|
+
LICENSE.txt ./LICENSE.txt /^/;" r
|
|
12
|
+
README.rdoc ./README.rdoc /^/;" r
|
|
13
|
+
Rakefile ./Rakefile /^/;" r
|
|
14
|
+
apidoc ./bin/apidoc /^/;" r
|
|
15
|
+
apidoc.rb ./lib/apidoc.rb /^/;" r
|
|
16
|
+
apidoc_helper.rb ./templates/apidoc_helper.rb /^/;" r
|
|
17
|
+
apidoc_spec.rb ./spec/apidoc_spec.rb /^/;" r
|
|
18
|
+
authentication.rb ./examples/authentication.rb /^/;" r
|
|
19
|
+
config ./.bundle/config /^/;" r
|
|
20
|
+
ftags ./ftags /^/;" r
|
|
21
|
+
layout.mustache ./templates/layout.mustache /^/;" r
|
|
22
|
+
resource.mustache ./templates/resource.mustache /^/;" r
|
|
23
|
+
spec_helper.rb ./spec/spec_helper.rb /^/;" r
|
data/lib/apidoc.rb
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
require 'rack/test'
|
|
2
|
+
require 'thor'
|
|
3
|
+
require 'mustache'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module ApiDoc
|
|
7
|
+
VERSION = "0.1.0"
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def new(app, &block)
|
|
11
|
+
@app = app
|
|
12
|
+
Runner.new(&block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def app; @app; end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Runner
|
|
19
|
+
def initialize(&block)
|
|
20
|
+
@resources = []
|
|
21
|
+
@accept = nil
|
|
22
|
+
@content_type = nil
|
|
23
|
+
Blueprint.new self, &block
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_accessor :resources, :accept, :content_type
|
|
27
|
+
|
|
28
|
+
class Blueprint
|
|
29
|
+
def initialize(runner, &block)
|
|
30
|
+
@runner = runner
|
|
31
|
+
instance_eval &block
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def accept(type)
|
|
35
|
+
@runner.accept = type
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def content_type(type)
|
|
39
|
+
@runner.content_type = type
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get(path, &block)
|
|
43
|
+
@runner.resources << GetResource.new(@runner, path, &block)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def post(path, &block)
|
|
47
|
+
@runner.resources << PostResource.new(@runner, path, &block)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def put(path, &block)
|
|
51
|
+
@runner.resources << PutResource.new(@runner, path, &block)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def delete(path, &block)
|
|
55
|
+
@runner.resources << DeleteResource.new(@runner, path, &block)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def options(path, &block)
|
|
59
|
+
@runner.resources << OptionsResource.new(@runner, path, &block)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def run
|
|
64
|
+
@resources.each(&:run)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
class HtmlWriter
|
|
69
|
+
def initialize(runner)
|
|
70
|
+
@runner = runner
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def write(stream)
|
|
74
|
+
stream.write(
|
|
75
|
+
Mustache.to_html(File.read(File.dirname(__FILE__) + '/../templates/layout.mustache'), {
|
|
76
|
+
content: @runner.resources.map {|r| resource_html(r) }.join
|
|
77
|
+
})
|
|
78
|
+
)
|
|
79
|
+
stream.rewind
|
|
80
|
+
stream
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def resource_html(resource)
|
|
84
|
+
resource.run
|
|
85
|
+
|
|
86
|
+
Mustache.to_html(File.read(File.dirname(__FILE__) + '/../templates/resource.mustache'), {
|
|
87
|
+
method: resource.method,
|
|
88
|
+
path: resource.path,
|
|
89
|
+
desc: resource.desc,
|
|
90
|
+
params: formatted_params(resource),
|
|
91
|
+
response: formatted_response(resource)
|
|
92
|
+
})
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def formatted_params(resource)
|
|
96
|
+
return '' unless resource.params
|
|
97
|
+
return resource.params unless resource.accept
|
|
98
|
+
|
|
99
|
+
case resource.accept.intern
|
|
100
|
+
when :json
|
|
101
|
+
JSON.pretty_generate(JSON.parse(resource.params))
|
|
102
|
+
else
|
|
103
|
+
resource.params
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def formatted_response(resource)
|
|
108
|
+
return resource.response_body unless resource.content_type
|
|
109
|
+
|
|
110
|
+
case resource.content_type.intern
|
|
111
|
+
when :json
|
|
112
|
+
JSON.pretty_generate(JSON.parse(resource.response_body))
|
|
113
|
+
else
|
|
114
|
+
resource.response_body
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
class Resource
|
|
120
|
+
def initialize(runner, path, &block)
|
|
121
|
+
@runner = runner
|
|
122
|
+
@path = path
|
|
123
|
+
@params = nil
|
|
124
|
+
Blueprint.new self, &block
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
attr_accessor :path, :method, :setup, :params, :desc
|
|
128
|
+
attr_writer :accept, :content_type
|
|
129
|
+
|
|
130
|
+
class Blueprint
|
|
131
|
+
def initialize(resource, &block)
|
|
132
|
+
@resource = resource
|
|
133
|
+
instance_eval &block
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def desc(val)
|
|
137
|
+
@resource.desc = val
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def accept(format)
|
|
141
|
+
@resource.accept = format
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def content_type(format)
|
|
145
|
+
@resource.content_type = format
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def params(&block)
|
|
149
|
+
@resource.params = block
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def setup(&block)
|
|
153
|
+
@resource.setup = block
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
include Rack::Test::Methods
|
|
158
|
+
|
|
159
|
+
def content_type
|
|
160
|
+
@content_type || @runner.content_type
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def accept
|
|
164
|
+
@accept || @runner.accept
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def app
|
|
168
|
+
ApiDoc.app
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def run
|
|
172
|
+
if @setup
|
|
173
|
+
instance_eval &@setup
|
|
174
|
+
end
|
|
175
|
+
@params = @params.call if @params.is_a?(Proc)
|
|
176
|
+
@response = make_request(@path, @params)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def response_body
|
|
180
|
+
@response.body
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
class PostResource < Resource
|
|
186
|
+
|
|
187
|
+
def make_request(path, params)
|
|
188
|
+
post path, params
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def initialize(runner, path, &block)
|
|
192
|
+
super runner, path, &block
|
|
193
|
+
@method = 'POST'
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
class GetResource < Resource
|
|
198
|
+
|
|
199
|
+
def make_request(path, params)
|
|
200
|
+
get path, params
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def initialize(runner, path, &block)
|
|
204
|
+
super runner, path, &block
|
|
205
|
+
@method = 'GET'
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
class PutResource < Resource
|
|
210
|
+
|
|
211
|
+
def make_request(path, params)
|
|
212
|
+
put path, params
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def initialize(runner, path, &block)
|
|
216
|
+
super runner, path, &block
|
|
217
|
+
@method = 'PUT'
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
class DeleteResource < Resource
|
|
222
|
+
|
|
223
|
+
def make_request(path, params)
|
|
224
|
+
delete path, params
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def initialize(runner, path, &block)
|
|
228
|
+
super runner, path, &block
|
|
229
|
+
@method = 'DELETE'
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
class OptionsResource < Resource
|
|
234
|
+
|
|
235
|
+
def make_request(path, params)
|
|
236
|
+
options path, params
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def initialize(runner, path, &block)
|
|
240
|
+
super runner, path, &block
|
|
241
|
+
@method = 'OPTIONS'
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
class Application < Thor
|
|
246
|
+
map '-v' => :version
|
|
247
|
+
|
|
248
|
+
desc "version", "print version information"
|
|
249
|
+
def version
|
|
250
|
+
puts ApiDoc::VERSION
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
end
|
|
255
|
+
|