useless-doc 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/useless/doc/rack/transform.rb +8 -2
- data/lib/useless/doc/ui/godel.rb +43 -10
- data/lib/useless/doc/ui/godel/api.mustache +38 -0
- data/lib/useless/doc/ui/godel/{template.mustache → resource.mustache} +0 -0
- data/lib/useless/doc/version.rb +1 -1
- data/spec/documents/api.json +16 -1
- data/spec/useless/doc/rack/application_spec.rb +11 -1
- data/spec/useless/doc/rack/transform_spec.rb +18 -5
- data/spec/useless/doc/ui/godel_spec.rb +125 -0
- data/useless-doc.gemspec +1 -0
- metadata +22 -4
- data/lib/useless/doc/ui.rb +0 -37
@@ -24,8 +24,14 @@ module Useless
|
|
24
24
|
|
25
25
|
if response[0] == 200
|
26
26
|
begin
|
27
|
-
|
28
|
-
|
27
|
+
if env['PATH_INFO'] == '/'
|
28
|
+
api = Serialization::Load.api(response[2].first)
|
29
|
+
html = env['useless.doc.ui'].api(api)
|
30
|
+
else
|
31
|
+
resource = Serialization::Load.resource(response[2].first)
|
32
|
+
html = env['useless.doc.ui'].resource(resource)
|
33
|
+
end
|
34
|
+
|
29
35
|
[200, {'Content-Type' => 'text/html'}, [html]]
|
30
36
|
rescue Oj::ParseError
|
31
37
|
[502, {'Content-Type' => 'text/plain'}, ['Documentation JSON is malformed.']]
|
data/lib/useless/doc/ui/godel.rb
CHANGED
@@ -1,29 +1,55 @@
|
|
1
1
|
require 'mustache'
|
2
2
|
require 'forwardable'
|
3
3
|
|
4
|
-
require 'useless/doc/ui'
|
5
|
-
|
6
4
|
module Useless
|
7
5
|
module Doc
|
8
6
|
module UI
|
9
|
-
class Godel
|
10
|
-
|
7
|
+
class Godel
|
8
|
+
def self.api(api)
|
9
|
+
API.new(api).render
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.resource(resource)
|
13
|
+
Resource.new(resource).render
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.css
|
17
|
+
File.read(asset_path + '/stylesheet.css')
|
18
|
+
end
|
11
19
|
|
12
|
-
def
|
13
|
-
|
20
|
+
def self.asset_path
|
21
|
+
File.dirname(__FILE__) + '/godel'
|
14
22
|
end
|
15
23
|
|
16
|
-
class
|
24
|
+
class API < Mustache
|
25
|
+
extend Forwardable
|
26
|
+
|
27
|
+
def_delegators :@api, :url, :description
|
28
|
+
|
29
|
+
self.template_file = Godel.asset_path + '/api.mustache'
|
30
|
+
|
31
|
+
def initialize(api)
|
32
|
+
@api = api
|
33
|
+
end
|
34
|
+
|
35
|
+
def resources
|
36
|
+
@api.resources.map{ |resource| Godel::Resource.new(resource) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Resource < Mustache
|
17
41
|
extend Forwardable
|
18
42
|
|
19
43
|
def_delegators :@resource, :path, :description
|
20
44
|
|
45
|
+
self.template_file = Godel.asset_path + '/resource.mustache'
|
46
|
+
|
21
47
|
def initialize(resource)
|
22
48
|
@resource = resource
|
23
49
|
end
|
24
50
|
|
25
51
|
def requests
|
26
|
-
@resource.requests.map{ |request| Godel::Request.new(request) }
|
52
|
+
@resource.requests.map{ |request| Godel::Request.new(request, self) }
|
27
53
|
end
|
28
54
|
end
|
29
55
|
|
@@ -32,14 +58,15 @@ module Useless
|
|
32
58
|
|
33
59
|
def_delegators :@request, :method, :description, :parameters, :headers, :body
|
34
60
|
|
35
|
-
def initialize(request)
|
61
|
+
def initialize(request, resource)
|
36
62
|
@request = request
|
63
|
+
@resource = resource
|
37
64
|
end
|
38
65
|
|
39
66
|
def authentication_requirement
|
40
67
|
@request.authentication_required ?
|
41
68
|
'Authentication Required' :
|
42
|
-
'
|
69
|
+
'Authentication Not Required'
|
43
70
|
end
|
44
71
|
|
45
72
|
def parameters?
|
@@ -53,6 +80,12 @@ module Useless
|
|
53
80
|
def responses
|
54
81
|
@request.responses.map { |response| Godel::Response.new(response) }
|
55
82
|
end
|
83
|
+
|
84
|
+
def doc_path
|
85
|
+
if @resource
|
86
|
+
"#{@resource.path}##{method}"
|
87
|
+
end
|
88
|
+
end
|
56
89
|
end
|
57
90
|
|
58
91
|
class Response
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>{{url}} | doc.useless.io</title>
|
5
|
+
<link href="/doc.css" media="screen" rel="stylesheet" type="text/css" />
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
|
9
|
+
<h1>{{url}}</h1>
|
10
|
+
|
11
|
+
<p>{{description}}</p>
|
12
|
+
|
13
|
+
{{#resources}}
|
14
|
+
<section>
|
15
|
+
<h2>{{path}}</h2>
|
16
|
+
|
17
|
+
<p>{{description}}</p>
|
18
|
+
|
19
|
+
<table>
|
20
|
+
<th>
|
21
|
+
<tr>
|
22
|
+
<td>Method</td>
|
23
|
+
<td>Description</td>
|
24
|
+
<tr>
|
25
|
+
</th>
|
26
|
+
<tbody>
|
27
|
+
{{#requests}}
|
28
|
+
<tr>
|
29
|
+
<td><a href="{{doc_path}}">{{method}}</a></td>
|
30
|
+
<td>{{description}}</td>
|
31
|
+
</tr>
|
32
|
+
{{/requests}}
|
33
|
+
</tbody>
|
34
|
+
</table>
|
35
|
+
</section>
|
36
|
+
{{/resources}}
|
37
|
+
|
38
|
+
</body>
|
File without changes
|
data/lib/useless/doc/version.rb
CHANGED
data/spec/documents/api.json
CHANGED
@@ -5,7 +5,22 @@
|
|
5
5
|
{
|
6
6
|
"path": "/twonks/:id",
|
7
7
|
"description": "The most critical aspect.",
|
8
|
-
"requests": [
|
8
|
+
"requests": [
|
9
|
+
{
|
10
|
+
"method": "GET",
|
11
|
+
"description": "Get dat twonk."
|
12
|
+
}
|
13
|
+
]
|
14
|
+
},
|
15
|
+
{
|
16
|
+
"path": "/twonks/:id/werp",
|
17
|
+
"description": "That other shit.",
|
18
|
+
"requests": [
|
19
|
+
{
|
20
|
+
"method": "POST",
|
21
|
+
"description": "Make dem werps."
|
22
|
+
}
|
23
|
+
]
|
9
24
|
}
|
10
25
|
]
|
11
26
|
}
|
@@ -25,7 +25,17 @@ describe Useless::Doc::Rack::Application do
|
|
25
25
|
last_response.should be_not_found
|
26
26
|
end
|
27
27
|
|
28
|
-
it 'should return HTML if proper JSON is retrieved' do
|
28
|
+
it 'should return API HTML if proper JSON is retrieved' do
|
29
|
+
Useless::Doc::Rack::Retriever::Stub.should_receive(:retrieve).
|
30
|
+
with('http://some-api.useless.io/').
|
31
|
+
and_return(load_document('api.json'))
|
32
|
+
|
33
|
+
get 'http://some-api.doc.useless.io/'
|
34
|
+
last_response.should be_ok
|
35
|
+
last_response.body.should =~ /<h1>twonk\.useless\.io<\/h1>/
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should return resource HTML if proper JSON is retrieved' do
|
29
39
|
Useless::Doc::Rack::Retriever::Stub.should_receive(:retrieve).
|
30
40
|
with('http://some-api.useless.io/some/resource').
|
31
41
|
and_return(load_document('twonk.json'))
|
@@ -1,11 +1,18 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
2
|
|
3
|
+
require 'rack'
|
3
4
|
require 'useless/doc/rack/transform'
|
4
5
|
require 'useless/doc/ui/godel'
|
5
6
|
|
6
7
|
describe Useless::Doc::Rack::Transform do
|
7
|
-
def
|
8
|
-
@
|
8
|
+
def api_base
|
9
|
+
@api_base ||= lambda do |env|
|
10
|
+
[200, {'Content-Type' => 'application/json'}, [load_document('api.json')]]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def resource_base
|
15
|
+
@resource_base ||= lambda do |env|
|
9
16
|
[200, {'Content-Type' => 'application/json'}, [load_document('twonk.json')]]
|
10
17
|
end
|
11
18
|
end
|
@@ -18,7 +25,7 @@ describe Useless::Doc::Rack::Transform do
|
|
18
25
|
end
|
19
26
|
|
20
27
|
it 'should raise an error if a UI is not specified' do
|
21
|
-
app = Useless::Doc::Rack::Transform.new(
|
28
|
+
app = Useless::Doc::Rack::Transform.new(resource_base)
|
22
29
|
request = Rack::MockRequest.new(app)
|
23
30
|
lambda { request.get('http://some-api.doc.granmal.com/some/resource') }.should raise_error(RuntimeError, 'No UI specified.')
|
24
31
|
end
|
@@ -36,8 +43,14 @@ describe Useless::Doc::Rack::Transform do
|
|
36
43
|
response.body.should == 'Not Found.'
|
37
44
|
end
|
38
45
|
|
39
|
-
it 'should transform the JSON returned by the downstream app to HTML' do
|
40
|
-
response = Rack::MockRequest.new(app(
|
46
|
+
it 'should transform the API JSON returned by the downstream app to HTML, if the request path is root' do
|
47
|
+
response = Rack::MockRequest.new(app(api_base)).get('http://twonk.useless.io/')
|
48
|
+
response['Content-Type'].should == 'text/html'
|
49
|
+
response.body.should =~ /<h1>twonk\.useless\.io<\/h1>/
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should transform the resource JSON returned by the downstream app to HTML' do
|
53
|
+
response = Rack::MockRequest.new(app(resource_base)).get('http://some-api.doc.granmal.com/some/resource')
|
41
54
|
response['Content-Type'].should == 'text/html'
|
42
55
|
response.body.should =~ /<h1>\/twonks\/:id<\/h1>/
|
43
56
|
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'useless/doc/ui/godel'
|
5
|
+
require 'useless/doc/serialization/load'
|
6
|
+
|
7
|
+
describe Useless::Doc::UI::Godel do
|
8
|
+
describe '.resource' do
|
9
|
+
before(:each) do
|
10
|
+
json = load_document('api.json').read
|
11
|
+
api = Useless::Doc::Serialization::Load.api json
|
12
|
+
result = Useless::Doc::UI::Godel.api(api)
|
13
|
+
@doc = Nokogiri::HTML(result)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should render the URL in the only h1' do
|
17
|
+
h1s = @doc.css('h1')
|
18
|
+
h1s.length.should == 1
|
19
|
+
h1s.first.content.should == 'twonk.useless.io'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should render the API description in a top-level p' do
|
23
|
+
description = @doc.css('body > p')
|
24
|
+
description.length.should == 1
|
25
|
+
description.first.content.should == 'Twonk information. Duh.'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should render resouce paths as h2s' do
|
29
|
+
h2s = @doc.css('h2')
|
30
|
+
h2s.length.should == 2
|
31
|
+
h2s.map { |h2| h2.content }.should match_array(['/twonks/:id', '/twonks/:id/werp'])
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should render resouce descriptions as ps within the section' do
|
35
|
+
ps = @doc.css('section > p')
|
36
|
+
ps.length.should == 2
|
37
|
+
ps.map { |h2| h2.content }.should match_array(['The most critical aspect.', 'That other shit.'])
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should add a link for each method, with an appropriate description' do
|
41
|
+
as = @doc.css('table td a')
|
42
|
+
td_content = @doc.css('table td').map { |td| td.content }
|
43
|
+
as.find { |a| a.content == 'GET' }['href'].should == '/twonks/:id#GET'
|
44
|
+
td_content.should include 'Get dat twonk.'
|
45
|
+
|
46
|
+
as.find { |a| a.content == 'POST' }['href'].should == '/twonks/:id/werp#POST'
|
47
|
+
td_content.should include 'Make dem werps.'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '.resource' do
|
52
|
+
before(:each) do
|
53
|
+
json = load_document('twonk.json').read
|
54
|
+
resource = Useless::Doc::Serialization::Load.resource json
|
55
|
+
result = Useless::Doc::UI::Godel.resource(resource)
|
56
|
+
@doc = Nokogiri::HTML(result)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should render the path within the only h1' do
|
60
|
+
h1s = @doc.css('h1')
|
61
|
+
h1s.length.should == 1
|
62
|
+
h1s.first.content.should == '/twonks/:id'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should render the methods as h2s' do
|
66
|
+
h2s = @doc.css('h2')
|
67
|
+
h2s.length.should == 2
|
68
|
+
h2s.map { |h2| h2.content }.should match_array(['GET', 'PUT'])
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should render response codes as h3s' do
|
72
|
+
h3s = @doc.css('h3')
|
73
|
+
h3s.length.should == 3
|
74
|
+
h3s.map { |h3| h3.content }.should match_array(['404', '200', '201'])
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should contain the method and response code descriptions in ps' do
|
78
|
+
p_content = @doc.css('p').map { |p| p.content }
|
79
|
+
p_content.should include 'The most critical aspect.'
|
80
|
+
p_content.should include 'Retrieve a representation of an individual twonk.'
|
81
|
+
p_content.should include 'A twonk with the specified ID could not be found.'
|
82
|
+
p_content.should include 'The specified twonk was retrieved successfully.'
|
83
|
+
p_content.should include 'The specified twonk was updated successfully.'
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should add parameter information to a table' do
|
87
|
+
[
|
88
|
+
{ key: 'id', description: 'The ID of the desired twonk.' },
|
89
|
+
{ key: 'werp', description: 'Self-explanatory.' },
|
90
|
+
{ key: 'id', description: 'The ID of the twonk to be updated.' }
|
91
|
+
].each do |param|
|
92
|
+
@doc.css('table').find do |table|
|
93
|
+
table.content.match(param[:key]) and
|
94
|
+
table.content.match(param[:description])
|
95
|
+
end.should_not be_nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should add body information to a table' do
|
100
|
+
[
|
101
|
+
{ key: 'name', description: 'The name of the twonk.' },
|
102
|
+
{ key: 'created_by', description: 'The short name of the user who created the twonk.' },
|
103
|
+
{ key: 'hoinked_by', description: 'The ID of the person who hoinked this twonk.' }
|
104
|
+
].each do |attribute|
|
105
|
+
@doc.css('table').find do |table|
|
106
|
+
table.content.match(attribute[:key]) and
|
107
|
+
table.content.match(attribute[:description])
|
108
|
+
end.should_not be_nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should add header information to a table' do
|
113
|
+
@doc.css('table').find do |table|
|
114
|
+
table.content.match('User-Agent') and
|
115
|
+
table.content.match('The thingy you\'re using.')
|
116
|
+
end.should_not be_nil
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should include authentication information in lis' do
|
120
|
+
li_content = @doc.css('li').map { |li| li.content }
|
121
|
+
li_content.should include 'Authentication Required'
|
122
|
+
li_content.should include 'Authentication Not Required'
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/useless-doc.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: useless-doc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-16 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: oj
|
@@ -139,6 +139,22 @@ dependencies:
|
|
139
139
|
- - ! '>='
|
140
140
|
- !ruby/object:Gem::Version
|
141
141
|
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: nokogiri
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
142
158
|
description: For parsing and serving Useless documentation.
|
143
159
|
email: khy@me.com
|
144
160
|
executables: []
|
@@ -168,10 +184,10 @@ files:
|
|
168
184
|
- lib/useless/doc/serialization/dump.rb
|
169
185
|
- lib/useless/doc/serialization/load.rb
|
170
186
|
- lib/useless/doc/sinatra.rb
|
171
|
-
- lib/useless/doc/ui.rb
|
172
187
|
- lib/useless/doc/ui/godel.rb
|
188
|
+
- lib/useless/doc/ui/godel/api.mustache
|
189
|
+
- lib/useless/doc/ui/godel/resource.mustache
|
173
190
|
- lib/useless/doc/ui/godel/stylesheet.css
|
174
|
-
- lib/useless/doc/ui/godel/template.mustache
|
175
191
|
- lib/useless/doc/version.rb
|
176
192
|
- spec/documents/api.json
|
177
193
|
- spec/documents/twonk.json
|
@@ -186,6 +202,7 @@ files:
|
|
186
202
|
- spec/useless/doc/serialization/dump_spec.rb
|
187
203
|
- spec/useless/doc/serialization/load_spec.rb
|
188
204
|
- spec/useless/doc/sinatra_spec.rb
|
205
|
+
- spec/useless/doc/ui/godel_spec.rb
|
189
206
|
- useless-doc.gemspec
|
190
207
|
homepage:
|
191
208
|
licenses: []
|
@@ -225,3 +242,4 @@ test_files:
|
|
225
242
|
- spec/useless/doc/serialization/dump_spec.rb
|
226
243
|
- spec/useless/doc/serialization/load_spec.rb
|
227
244
|
- spec/useless/doc/sinatra_spec.rb
|
245
|
+
- spec/useless/doc/ui/godel_spec.rb
|
data/lib/useless/doc/ui.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'mustache'
|
3
|
-
|
4
|
-
module Useless
|
5
|
-
module Doc
|
6
|
-
module UI
|
7
|
-
def self.included(base)
|
8
|
-
base.instance_eval do
|
9
|
-
extend ClassMethods
|
10
|
-
self.template_file = dir + 'template.mustache'
|
11
|
-
|
12
|
-
extend Forwardable
|
13
|
-
def_delegators :@resource, :path, :description, :requests
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
module ClassMethods
|
18
|
-
def html(resource)
|
19
|
-
new(resource).render
|
20
|
-
end
|
21
|
-
|
22
|
-
def css
|
23
|
-
File.read(dir + 'stylesheet.css')
|
24
|
-
end
|
25
|
-
|
26
|
-
def dir
|
27
|
-
key = Mustache.underscore(name.split('::').last)
|
28
|
-
File.dirname(__FILE__) + "/ui/#{key}/"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def initialize(resource)
|
33
|
-
@resource = resource
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|