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.
Files changed (39) hide show
  1. data/.gitignore +18 -0
  2. data/.rbenv-version +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +3 -0
  6. data/Rakefile +1 -0
  7. data/config.ru +4 -0
  8. data/lib/useless/doc/action.rb +50 -0
  9. data/lib/useless/doc/body.rb +58 -0
  10. data/lib/useless/doc/dsl.rb +208 -0
  11. data/lib/useless/doc/header.rb +24 -0
  12. data/lib/useless/doc/rack/application.rb +31 -0
  13. data/lib/useless/doc/rack/proxy.rb +58 -0
  14. data/lib/useless/doc/rack/stylesheet.rb +24 -0
  15. data/lib/useless/doc/rack/transform.rb +35 -0
  16. data/lib/useless/doc/rack/ui.rb +35 -0
  17. data/lib/useless/doc/request/parameter.rb +47 -0
  18. data/lib/useless/doc/request.rb +29 -0
  19. data/lib/useless/doc/resource.rb +29 -0
  20. data/lib/useless/doc/response/status.rb +27 -0
  21. data/lib/useless/doc/response.rb +29 -0
  22. data/lib/useless/doc/serialization/dump.rb +122 -0
  23. data/lib/useless/doc/serialization/load.rb +171 -0
  24. data/lib/useless/doc/sinatra.rb +48 -0
  25. data/lib/useless/doc/ui/godel/stylesheet.css +1 -0
  26. data/lib/useless/doc/ui/godel/template.mustache +199 -0
  27. data/lib/useless/doc/ui/godel.rb +92 -0
  28. data/lib/useless/doc/ui.rb +37 -0
  29. data/lib/useless/doc/version.rb +5 -0
  30. data/lib/useless/doc.rb +4 -0
  31. data/spec/documents/twonk.json +106 -0
  32. data/spec/spec_helper.rb +10 -0
  33. data/spec/useless/doc/dsl_spec.rb +71 -0
  34. data/spec/useless/doc/proxy_spec.rb +48 -0
  35. data/spec/useless/doc/serialization/dump_spec.rb +116 -0
  36. data/spec/useless/doc/serialization/load_spec.rb +99 -0
  37. data/spec/useless/doc/sinatra_spec.rb +64 -0
  38. data/useless-doc.gemspec +26 -0
  39. 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
@@ -0,0 +1,5 @@
1
+ module Useless
2
+ module Doc
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module Useless
2
+ module Doc
3
+ end
4
+ end
@@ -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
+ }
@@ -0,0 +1,10 @@
1
+ require File.dirname(__FILE__) + '/../lib/useless/doc'
2
+
3
+ RSpec.configure do |config|
4
+ config.order = :rand
5
+ end
6
+
7
+ def load_document(name)
8
+ filename = File.dirname(__FILE__) + "/documents/#{name}"
9
+ File.new(filename)
10
+ end
@@ -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
@@ -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