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.
@@ -24,8 +24,14 @@ module Useless
24
24
 
25
25
  if response[0] == 200
26
26
  begin
27
- resource = Serialization::Load.resource(response[2].first)
28
- html = env['useless.doc.ui'].html(resource)
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.']]
@@ -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 < ::Mustache
10
- include UI
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 initialize(resource)
13
- @resource = Godel::Resource.new(resource)
20
+ def self.asset_path
21
+ File.dirname(__FILE__) + '/godel'
14
22
  end
15
23
 
16
- class Resource
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
- 'Authenication Not Required'
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>
@@ -1,5 +1,5 @@
1
1
  module Useless
2
2
  module Doc
3
- VERSION = '0.2.2'
3
+ VERSION = '0.2.3'
4
4
  end
5
5
  end
@@ -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 good_base
8
- @good_base ||= lambda do |env|
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(good_base)
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(good_base)).get('http://some-api.doc.granmal.com/some/resource')
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
@@ -23,4 +23,5 @@ Gem::Specification.new do |gem|
23
23
 
24
24
  gem.add_development_dependency 'rspec'
25
25
  gem.add_development_dependency 'rack-test'
26
+ gem.add_development_dependency 'nokogiri'
26
27
  end
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.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-15 00:00:00.000000000 Z
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
@@ -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