useless-doc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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