useless-doc 0.1.0
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/.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
|