useless-doc 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +18 -0
- data/.rbenv-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +3 -0
- data/Rakefile +1 -0
- data/config.ru +4 -0
- data/lib/useless/doc/action.rb +50 -0
- data/lib/useless/doc/body.rb +58 -0
- data/lib/useless/doc/dsl.rb +208 -0
- data/lib/useless/doc/header.rb +24 -0
- data/lib/useless/doc/rack/application.rb +31 -0
- data/lib/useless/doc/rack/proxy.rb +58 -0
- data/lib/useless/doc/rack/stylesheet.rb +24 -0
- data/lib/useless/doc/rack/transform.rb +35 -0
- data/lib/useless/doc/rack/ui.rb +35 -0
- data/lib/useless/doc/request/parameter.rb +47 -0
- data/lib/useless/doc/request.rb +29 -0
- data/lib/useless/doc/resource.rb +29 -0
- data/lib/useless/doc/response/status.rb +27 -0
- data/lib/useless/doc/response.rb +29 -0
- data/lib/useless/doc/serialization/dump.rb +122 -0
- data/lib/useless/doc/serialization/load.rb +171 -0
- data/lib/useless/doc/sinatra.rb +48 -0
- data/lib/useless/doc/ui/godel/stylesheet.css +1 -0
- data/lib/useless/doc/ui/godel/template.mustache +199 -0
- data/lib/useless/doc/ui/godel.rb +92 -0
- data/lib/useless/doc/ui.rb +37 -0
- data/lib/useless/doc/version.rb +5 -0
- data/lib/useless/doc.rb +4 -0
- data/spec/documents/twonk.json +106 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/useless/doc/dsl_spec.rb +71 -0
- data/spec/useless/doc/proxy_spec.rb +48 -0
- data/spec/useless/doc/serialization/dump_spec.rb +116 -0
- data/spec/useless/doc/serialization/load_spec.rb +99 -0
- data/spec/useless/doc/sinatra_spec.rb +64 -0
- data/useless-doc.gemspec +26 -0
- metadata +217 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'mustache'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
require 'useless/doc/ui'
|
5
|
+
|
6
|
+
module Useless
|
7
|
+
module Doc
|
8
|
+
module UI
|
9
|
+
class Godel < ::Mustache
|
10
|
+
include UI
|
11
|
+
|
12
|
+
def initialize(resource)
|
13
|
+
@resource = Godel::Resource.new(resource)
|
14
|
+
end
|
15
|
+
|
16
|
+
class Resource
|
17
|
+
extend Forwardable
|
18
|
+
|
19
|
+
def_delegators :@resource, :path, :description
|
20
|
+
|
21
|
+
def initialize(resource)
|
22
|
+
@resource = resource
|
23
|
+
end
|
24
|
+
|
25
|
+
def actions
|
26
|
+
@resource.actions.map{ |action| Godel::Action.new(action) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Action
|
31
|
+
extend Forwardable
|
32
|
+
|
33
|
+
def_delegators :@action, :description, :method
|
34
|
+
|
35
|
+
def initialize(action)
|
36
|
+
@action = action
|
37
|
+
end
|
38
|
+
|
39
|
+
def authentication_requirement
|
40
|
+
@action.authentication_required ?
|
41
|
+
'Authentication Required' :
|
42
|
+
'Authenication Not Required'
|
43
|
+
end
|
44
|
+
|
45
|
+
def request
|
46
|
+
Godel::Request.new(@action.request)
|
47
|
+
end
|
48
|
+
|
49
|
+
def response
|
50
|
+
Godel::Response.new(@action.response)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Request
|
55
|
+
extend Forwardable
|
56
|
+
|
57
|
+
def_delegators :@request, :parameters, :headers, :body
|
58
|
+
|
59
|
+
def initialize(request)
|
60
|
+
@request = request
|
61
|
+
end
|
62
|
+
|
63
|
+
def parameters?
|
64
|
+
parameters.any?
|
65
|
+
end
|
66
|
+
|
67
|
+
def headers?
|
68
|
+
headers.any?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Response
|
73
|
+
extend Forwardable
|
74
|
+
|
75
|
+
def_delegators :@response, :statuses, :headers, :body
|
76
|
+
|
77
|
+
def initialize(response)
|
78
|
+
@response = response
|
79
|
+
end
|
80
|
+
|
81
|
+
def statuses?
|
82
|
+
statuses.any?
|
83
|
+
end
|
84
|
+
|
85
|
+
def headers?
|
86
|
+
headers.any?
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,37 @@
|
|
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, :actions
|
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
|
data/lib/useless/doc.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
{
|
2
|
+
"path": "/twonks/:id",
|
3
|
+
"description": "The most critical aspect.",
|
4
|
+
"actions": [
|
5
|
+
{
|
6
|
+
"description": "Retrieve a representation of an individual twonk.",
|
7
|
+
"method": "GET",
|
8
|
+
"authentication_required": false,
|
9
|
+
"request": {
|
10
|
+
"parameters": [
|
11
|
+
{
|
12
|
+
"type": "path",
|
13
|
+
"key": "id",
|
14
|
+
"required": true,
|
15
|
+
"description": "The ID of the desired twonk."
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"type": "query",
|
19
|
+
"key": "werp",
|
20
|
+
"required": false,
|
21
|
+
"default": 12,
|
22
|
+
"description": "Self-explanatory."
|
23
|
+
}
|
24
|
+
],
|
25
|
+
"headers": [
|
26
|
+
{
|
27
|
+
"key": "User-Agent",
|
28
|
+
"description": "The thingy you're using."
|
29
|
+
}
|
30
|
+
]
|
31
|
+
},
|
32
|
+
"response": {
|
33
|
+
"statuses": [
|
34
|
+
{
|
35
|
+
"code": 200,
|
36
|
+
"description": "The specified twonk was retrieved successfully."
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"code": 404,
|
40
|
+
"description": "A twonk with the specified ID could not be found."
|
41
|
+
}
|
42
|
+
],
|
43
|
+
"body": {
|
44
|
+
"content_type": "application/json",
|
45
|
+
"attributes": [
|
46
|
+
{
|
47
|
+
"key": "name",
|
48
|
+
"type": "string",
|
49
|
+
"required": true,
|
50
|
+
"description": "The name of the twonk."
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"key": "created_by",
|
54
|
+
"type": "object",
|
55
|
+
"required": true,
|
56
|
+
"description": "The short name of the user who created the twonk."
|
57
|
+
}
|
58
|
+
]
|
59
|
+
}
|
60
|
+
|
61
|
+
}
|
62
|
+
},
|
63
|
+
|
64
|
+
{
|
65
|
+
"description": "Update a twonk.",
|
66
|
+
"method": "PUT",
|
67
|
+
"authentication_required": true,
|
68
|
+
"request": {
|
69
|
+
"parameters": [
|
70
|
+
{
|
71
|
+
"type": "path",
|
72
|
+
"key": "id",
|
73
|
+
"required": true,
|
74
|
+
"description": "The ID of the twonk to be updated."
|
75
|
+
}
|
76
|
+
],
|
77
|
+
"body": {
|
78
|
+
"content_type": "application/x-www-form-urlencoded",
|
79
|
+
"attributes": [
|
80
|
+
{
|
81
|
+
"key": "name",
|
82
|
+
"type": "string",
|
83
|
+
"required": true,
|
84
|
+
"description": "The name of the twonk."
|
85
|
+
},
|
86
|
+
{
|
87
|
+
"key": "hoinked_by",
|
88
|
+
"type": "number",
|
89
|
+
"required": false,
|
90
|
+
"default": 3,
|
91
|
+
"description": "The ID of the person who hoinked this twonk."
|
92
|
+
}
|
93
|
+
]
|
94
|
+
}
|
95
|
+
},
|
96
|
+
"response": {
|
97
|
+
"statuses": [
|
98
|
+
{
|
99
|
+
"code": 201,
|
100
|
+
"description": "The specified twonk was updated successfully."
|
101
|
+
}
|
102
|
+
]
|
103
|
+
}
|
104
|
+
}
|
105
|
+
]
|
106
|
+
}
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
require 'rack/test'
|
4
|
+
require 'useless/doc/dsl'
|
5
|
+
|
6
|
+
describe Useless::Doc::DSL do
|
7
|
+
describe '.build' do
|
8
|
+
it 'should provide a terse DSL for building resource documentation' do
|
9
|
+
resource = Useless::Doc::DSL::Resource.build do
|
10
|
+
path '/widgets'
|
11
|
+
description 'The entire collection of widgets'
|
12
|
+
|
13
|
+
get 'Returns a full listing of the collection' do
|
14
|
+
authentication_required false
|
15
|
+
|
16
|
+
request do
|
17
|
+
parameter 'page', 'The page of widgets that you\'d like',
|
18
|
+
type: :query, required: false, default: 1
|
19
|
+
|
20
|
+
header 'X-Twiddle', 'The twiddle threshold.'
|
21
|
+
end
|
22
|
+
|
23
|
+
response do
|
24
|
+
status 200, 'The widgets were appropriately returned'
|
25
|
+
status 404, 'All the widgets are gone!!!!!'
|
26
|
+
|
27
|
+
header 'X-Twonk', 'The twonk coefficient.'
|
28
|
+
|
29
|
+
body do
|
30
|
+
content_type 'application/json'
|
31
|
+
|
32
|
+
attribute 'name', 'The name of the widget', required: true
|
33
|
+
attribute 'age', 'The age of the widget. If missing, the widget was never born',
|
34
|
+
type: :number, required: false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
post do
|
40
|
+
authentication_required true
|
41
|
+
|
42
|
+
description 'Creates a new widget'
|
43
|
+
|
44
|
+
request do
|
45
|
+
body do
|
46
|
+
content_type 'application/json'
|
47
|
+
|
48
|
+
attribute 'name', 'The name of the widget', required: true
|
49
|
+
attribute 'age', 'The age of the widget', type: :number, required: false, default: 42
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
response do
|
54
|
+
status 201, 'The widget was successfully created'
|
55
|
+
status 422, 'The widget couldn\'t be created because name was missing'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
resource.description.should == 'The entire collection of widgets'
|
61
|
+
resource.actions[0].method.should == 'GET'
|
62
|
+
resource.actions[0].request.headers[0].description.should == 'The twiddle threshold.'
|
63
|
+
resource.actions[0].response.statuses[0].code.should == 200
|
64
|
+
resource.actions[1].method.should == 'POST'
|
65
|
+
resource.actions[1].description.should == 'Creates a new widget'
|
66
|
+
resource.actions[1].request.body.content_type.should == 'application/json'
|
67
|
+
resource.actions[1].request.body.attributes[0].key.should == 'name'
|
68
|
+
resource.actions[1].response.statuses[1].description.should == 'The widget couldn\'t be created because name was missing'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
require 'rack/test'
|
4
|
+
require 'useless/doc/proxy'
|
5
|
+
require 'useless/doc/ui/godel'
|
6
|
+
|
7
|
+
describe Useless::Doc::Proxy do
|
8
|
+
include Rack::Test::Methods
|
9
|
+
|
10
|
+
def app
|
11
|
+
@app ||= begin
|
12
|
+
ui = Useless::Doc::UI::Godel
|
13
|
+
Useless::Doc::Proxy.new(ui)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'as a Rack application' do
|
18
|
+
it 'should proxy GET requests as an OPTIONS request to the corresponding API resource.' do
|
19
|
+
app.should_receive(:retrieve_resource).
|
20
|
+
with('http://some-api.granmal.com/some/resource').
|
21
|
+
and_return('{ "some": "response" }')
|
22
|
+
|
23
|
+
get 'http://some-api.doc.granmal.com/some/resource'
|
24
|
+
last_response.should be_ok
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should return a 404 if the proxy call returns nothing' do
|
28
|
+
app.should_receive(:retrieve_resource).
|
29
|
+
with('http://some-api.granmal.com/some/non-existant/resource').
|
30
|
+
and_return(nil)
|
31
|
+
|
32
|
+
get 'http://some-api.doc.granmal.com/some/non-existant/resource'
|
33
|
+
last_response.should be_not_found
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '.transform_url' do
|
38
|
+
it 'should drop the \'doc\' subdomain from the url for the \'io\' TLD' do
|
39
|
+
url = Useless::Doc::Proxy.transform_url 'http://some-api.doc.useless.io/some/resource'
|
40
|
+
url.should == 'http://some-api.useless.io/some/resource'
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should drop the \'doc\' subdomain from the url for the \'dev\' TLD' do
|
44
|
+
url = Useless::Doc::Proxy.transform_url 'http://some-api.doc.useless.dev/some/resource'
|
45
|
+
url.should == 'http://some-api.useless.dev/some/resource'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
|
3
|
+
require 'useless/doc/serialization/dump'
|
4
|
+
|
5
|
+
describe Useless::Doc::Serialization::Dump do
|
6
|
+
describe '.hash_to_json' do
|
7
|
+
it 'should convert a hash to a JSON document' do
|
8
|
+
hash = Useless::Doc::Serialization::Dump.hash_to_json({'abc' => 123})
|
9
|
+
hash.should == '{"abc":123}'
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should act as an identity if a JSON document is specified' do
|
13
|
+
json = '{"abc":123}'
|
14
|
+
Useless::Doc::Serialization::Dump.hash_to_json(json).should == json
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '.resource' do
|
19
|
+
it 'should convert the specified Doc::Resource instance into JSON' do
|
20
|
+
get_parameter = Useless::Doc::Request::Parameter.new \
|
21
|
+
type: Useless::Doc::Request::Parameter::Type::PATH,
|
22
|
+
key: 'page',
|
23
|
+
required: false,
|
24
|
+
default: 1,
|
25
|
+
description: 'The number of the page of twiddles.'
|
26
|
+
|
27
|
+
get_request_header = Useless::Doc::Header.new \
|
28
|
+
key: 'Content',
|
29
|
+
description: 'The type of content.'
|
30
|
+
|
31
|
+
get_request = Useless::Doc::Request.new \
|
32
|
+
parameters: [get_parameter],
|
33
|
+
headers: [get_request_header],
|
34
|
+
body: nil
|
35
|
+
|
36
|
+
get_status = Useless::Doc::Response::Status.new \
|
37
|
+
code: 200,
|
38
|
+
description: 'The list was successfully returned.'
|
39
|
+
|
40
|
+
get_response_header = Useless::Doc::Header.new \
|
41
|
+
key: 'Type',
|
42
|
+
description: 'The response type.'
|
43
|
+
|
44
|
+
get_response_body_attribute = Useless::Doc::Body::Attribute.new \
|
45
|
+
key: 'name',
|
46
|
+
type: 'string',
|
47
|
+
required: true,
|
48
|
+
default: nil,
|
49
|
+
description: 'The name of the twiddle.'
|
50
|
+
|
51
|
+
get_response_body = Useless::Doc::Body.new \
|
52
|
+
content_type: 'application/json',
|
53
|
+
attributes: [get_response_body_attribute]
|
54
|
+
|
55
|
+
get_response = Useless::Doc::Response.new \
|
56
|
+
statuses: [get_status],
|
57
|
+
headers: [get_response_header],
|
58
|
+
body: get_response_body
|
59
|
+
|
60
|
+
get = Useless::Doc::Action.new \
|
61
|
+
description: 'See a whole list of twiddles.',
|
62
|
+
method: Useless::Doc::Action::Method::GET,
|
63
|
+
authentication_required: false,
|
64
|
+
request: get_request,
|
65
|
+
response: get_response
|
66
|
+
|
67
|
+
resource = Useless::Doc::Resource.new \
|
68
|
+
path: '/twiddles',
|
69
|
+
description: 'The full lot of twiddles.',
|
70
|
+
actions: [get]
|
71
|
+
|
72
|
+
json = Useless::Doc::Serialization::Dump.resource(resource)
|
73
|
+
hash = Useless::Doc::Serialization::Load.json_to_hash(json)
|
74
|
+
|
75
|
+
hash['path'].should == '/twiddles'
|
76
|
+
hash['description'].should == 'The full lot of twiddles.'
|
77
|
+
|
78
|
+
get_hash = Useless::Doc::Serialization::Load.json_to_hash(hash['actions'][0])
|
79
|
+
get_hash['description'].should == 'See a whole list of twiddles.'
|
80
|
+
get_hash['method'].should == 'GET'
|
81
|
+
get_hash['authentication_required'].should be_false
|
82
|
+
|
83
|
+
get_request_hash = Useless::Doc::Serialization::Load.json_to_hash(get_hash['request'])
|
84
|
+
get_request_paramter_hash = Useless::Doc::Serialization::Load.json_to_hash(get_request_hash['parameters'][0])
|
85
|
+
get_request_paramter_hash['type'].should == 'path'
|
86
|
+
get_request_paramter_hash['key'].should == 'page'
|
87
|
+
get_request_paramter_hash['required'].should == false
|
88
|
+
get_request_paramter_hash['default'].should == 1
|
89
|
+
get_request_paramter_hash['description'].should == 'The number of the page of twiddles.'
|
90
|
+
|
91
|
+
get_request_header_hash = Useless::Doc::Serialization::Load.json_to_hash(get_request_hash['headers'][0])
|
92
|
+
get_request_header_hash['key'].should == 'Content'
|
93
|
+
get_request_header_hash['description'].should == 'The type of content.'
|
94
|
+
|
95
|
+
get_response_hash = Useless::Doc::Serialization::Load.json_to_hash(get_hash['response'])
|
96
|
+
get_response_status_hash = Useless::Doc::Serialization::Load.json_to_hash(get_response_hash['statuses'][0])
|
97
|
+
get_response_status_hash['code'].should == 200
|
98
|
+
get_response_status_hash['description'].should == 'The list was successfully returned.'
|
99
|
+
|
100
|
+
get_response_header_hash = Useless::Doc::Serialization::Load.json_to_hash(get_response_hash['headers'][0])
|
101
|
+
get_response_header_hash['key'].should == 'Type'
|
102
|
+
get_response_header_hash['description'].should == 'The response type.'
|
103
|
+
|
104
|
+
get_response_body_hash = Useless::Doc::Serialization::Load.json_to_hash(get_response_hash['body'])
|
105
|
+
get_response_body_hash['content_type'].should == 'application/json'
|
106
|
+
|
107
|
+
get_request_body_attribute_hash = Useless::Doc::Serialization::Load.json_to_hash(get_response_body_hash['attributes'][0])
|
108
|
+
get_request_body_attribute_hash['key'].should == 'name'
|
109
|
+
get_request_body_attribute_hash['type'].should == 'string'
|
110
|
+
get_request_body_attribute_hash['required'].should == true
|
111
|
+
get_request_body_attribute_hash['default'].should be_nil
|
112
|
+
get_request_body_attribute_hash['description'].should == 'The name of the twiddle.'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
|
3
|
+
require 'useless/doc/serialization/load'
|
4
|
+
|
5
|
+
describe Useless::Doc::Serialization::Load do
|
6
|
+
describe '.json_to_hash' do
|
7
|
+
it 'should convert a JSON document to a hash' do
|
8
|
+
hash = Useless::Doc::Serialization::Load.json_to_hash('{ "abc": 123 }')
|
9
|
+
hash.should be_a Hash
|
10
|
+
hash['abc'].should == 123
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should act as an identity if a hash is specified' do
|
14
|
+
hash = { abc: 123 }
|
15
|
+
Useless::Doc::Serialization::Load.json_to_hash(hash).should == hash
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.resource' do
|
20
|
+
it 'should parse the specified JSON into a model hierarchy' do
|
21
|
+
document = load_document('twonk.json')
|
22
|
+
resource = Useless::Doc::Serialization::Load.resource document.read
|
23
|
+
|
24
|
+
resource.should be_a Useless::Doc::Resource
|
25
|
+
resource.path.should == '/twonks/:id'
|
26
|
+
resource.description.should == 'The most critical aspect.'
|
27
|
+
|
28
|
+
get = resource.actions.first
|
29
|
+
get.should be_a Useless::Doc::Action
|
30
|
+
get.description.should == 'Retrieve a representation of an individual twonk.'
|
31
|
+
get.method.should == 'GET'
|
32
|
+
get.authentication_required.should == false
|
33
|
+
|
34
|
+
get.request.parameters[0].type.should == 'path'
|
35
|
+
get.request.parameters[0].key.should == 'id'
|
36
|
+
get.request.parameters[0].required.should == true
|
37
|
+
get.request.parameters[0].default.should be_nil
|
38
|
+
get.request.parameters[0].description.should == 'The ID of the desired twonk.'
|
39
|
+
|
40
|
+
get.request.parameters[1].type.should == 'query'
|
41
|
+
get.request.parameters[1].key.should == 'werp'
|
42
|
+
get.request.parameters[1].required.should == false
|
43
|
+
get.request.parameters[1].default.should == 12
|
44
|
+
get.request.parameters[1].description.should == 'Self-explanatory.'
|
45
|
+
|
46
|
+
get.request.headers[0].key.should == 'User-Agent'
|
47
|
+
get.request.headers[0].description.should == 'The thingy you\'re using.'
|
48
|
+
|
49
|
+
get.response.statuses[0].code.should == 200
|
50
|
+
get.response.statuses[0].description.should == 'The specified twonk was retrieved successfully.'
|
51
|
+
|
52
|
+
get.response.statuses[1].code.should == 404
|
53
|
+
get.response.statuses[1].description.should == 'A twonk with the specified ID could not be found.'
|
54
|
+
|
55
|
+
get.response.body.content_type.should == 'application/json'
|
56
|
+
|
57
|
+
get.response.body.attributes[0].key.should == 'name'
|
58
|
+
get.response.body.attributes[0].type.should == 'string'
|
59
|
+
get.response.body.attributes[0].required.should be_true
|
60
|
+
get.response.body.attributes[0].default.should be_nil
|
61
|
+
get.response.body.attributes[0].description.should == 'The name of the twonk.'
|
62
|
+
|
63
|
+
get.response.body.attributes[1].key.should == 'created_by'
|
64
|
+
get.response.body.attributes[1].type.should == 'object'
|
65
|
+
get.response.body.attributes[1].required.should be_true
|
66
|
+
get.response.body.attributes[1].default.should be_nil
|
67
|
+
get.response.body.attributes[1].description.should == 'The short name of the user who created the twonk.'
|
68
|
+
|
69
|
+
put = resource.actions.last
|
70
|
+
put.should be_a Useless::Doc::Action
|
71
|
+
put.description.should =='Update a twonk.'
|
72
|
+
put.method.should == 'PUT'
|
73
|
+
put.authentication_required.should == true
|
74
|
+
|
75
|
+
put.request.parameters[0].type.should == 'path'
|
76
|
+
put.request.parameters[0].key.should == 'id'
|
77
|
+
put.request.parameters[0].required.should == true
|
78
|
+
put.request.parameters[0].default.should be_nil
|
79
|
+
put.request.parameters[0].description.should == 'The ID of the twonk to be updated.'
|
80
|
+
|
81
|
+
put.request.body.content_type.should == 'application/x-www-form-urlencoded'
|
82
|
+
|
83
|
+
put.request.body.attributes[0].key.should == 'name'
|
84
|
+
put.request.body.attributes[0].type.should == 'string'
|
85
|
+
put.request.body.attributes[0].required.should be_true
|
86
|
+
put.request.body.attributes[0].default.should be_nil
|
87
|
+
put.request.body.attributes[0].description.should == 'The name of the twonk.'
|
88
|
+
|
89
|
+
put.request.body.attributes[1].key.should == 'hoinked_by'
|
90
|
+
put.request.body.attributes[1].type.should == 'number'
|
91
|
+
put.request.body.attributes[1].required.should be_false
|
92
|
+
put.request.body.attributes[1].default.should == 3
|
93
|
+
put.request.body.attributes[1].description.should == 'The ID of the person who hoinked this twonk.'
|
94
|
+
|
95
|
+
put.response.statuses[0].code.should == 201
|
96
|
+
put.response.statuses[0].description.should == 'The specified twonk was updated successfully.'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
require 'sinatra/base'
|
4
|
+
require 'useless/doc/sinatra'
|
5
|
+
|
6
|
+
describe Useless::Doc::Sinatra do
|
7
|
+
class DocApp < Sinatra::Base
|
8
|
+
register Useless::Doc::Sinatra
|
9
|
+
|
10
|
+
doc '/some-resources' do
|
11
|
+
get 'Get all of these resources' do
|
12
|
+
request do
|
13
|
+
parameter 'since', 'Only resources after this date.'
|
14
|
+
end
|
15
|
+
|
16
|
+
response do
|
17
|
+
body do
|
18
|
+
attribute 'name', 'The name of the resource.'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
post 'Make a new resource' do
|
24
|
+
request do
|
25
|
+
body do
|
26
|
+
attribute 'name', 'The name of the resource.'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
response do
|
31
|
+
status 201, 'The resource was successfully created.'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
get '/some-resources' do
|
37
|
+
[]
|
38
|
+
end
|
39
|
+
|
40
|
+
post '/some-resources' do
|
41
|
+
201
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
include Rack::Test::Methods
|
46
|
+
|
47
|
+
def app
|
48
|
+
DocApp
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should serve documentation for the documented resource' do
|
52
|
+
options 'http://some-api.granmal.com/some-resources'
|
53
|
+
resource = Useless::Doc::Serialization::Load.resource(last_response.body)
|
54
|
+
|
55
|
+
get = resource.actions.find { |action| action.method == Useless::Doc::Action::Method::GET }
|
56
|
+
get.description.should == 'Get all of these resources'
|
57
|
+
get.request.parameters.first.key.should == 'since'
|
58
|
+
|
59
|
+
post = resource.actions.find { |action| action.method == Useless::Doc::Action::Method::POST }
|
60
|
+
post.description.should == 'Make a new resource'
|
61
|
+
post.response.statuses.first.code.should == 201
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
data/useless-doc.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'useless/doc/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'useless-doc'
|
8
|
+
gem.version = Useless::Doc::VERSION
|
9
|
+
gem.authors = 'Kevin Hyland'
|
10
|
+
gem.email = 'khy@me.com'
|
11
|
+
gem.description = 'For parsing and serving Useless documentation.'
|
12
|
+
gem.summary = 'For parsing and serving Useless documentation.'
|
13
|
+
|
14
|
+
gem.files = `git ls-files`.split($/)
|
15
|
+
gem.test_files = gem.files.grep(%r{^spec/})
|
16
|
+
|
17
|
+
gem.add_dependency 'oj'
|
18
|
+
gem.add_dependency 'rack'
|
19
|
+
gem.add_dependency 'typhoeus'
|
20
|
+
gem.add_dependency 'low'
|
21
|
+
gem.add_dependency 'sinatra'
|
22
|
+
gem.add_dependency 'mustache'
|
23
|
+
|
24
|
+
gem.add_development_dependency 'rspec'
|
25
|
+
gem.add_development_dependency 'rack-test'
|
26
|
+
end
|