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.
@@ -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