useless-doc 0.2.2 → 0.2.3
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/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
|