Skip to content

Commit 3598d9a

Browse files
author
Robert Yawn
committed
Initial commit.
0 parents  commit 3598d9a

21 files changed

+678
-0
lines changed

.editorconfig

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# http://editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true
11+
12+
[*.md]
13+
trim_trailing_whitespace = false

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules
2+
npm-debug.log
3+
.env
4+
.env*
5+
coverage/
6+
atlassian-ide-plugin.xml
7+
.idea

.npmignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
*.sw*
2+
node_modules/
3+
.DS_Store
4+
coverage
5+
test/
6+
atlassian-ide-plugin.xml
7+
.idea

LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
(The MIT License)
2+
3+
Copyright (c) 2014 Pebble Technology
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
'Software'), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Makefile

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
test:
3+
@./node_modules/.bin/mocha -A --harmony-generators test/*Spec.js
4+
5+
test-cov:
6+
@NODE_ENV=test node --harmony-generators \
7+
node_modules/.bin/istanbul cover \
8+
./node_modules/.bin/_mocha \
9+
-- -u exports \
10+
-A test/*Spec.js
11+
12+
open-cov:
13+
open coverage/lcov-report/index.html
14+
15+
.PHONY: test test-cov open-cov

README.md

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#koa-resourcer-docs
2+
3+
## Introduction
4+
A simple documentation generator for [koa-resourcer](https://github.com/pebble/koa-resourcer).
5+
6+
App resources that have exposed routes will be parsed and documented.
7+
8+
## Use
9+
In your app:
10+
```js
11+
var koa = require('koa');
12+
var join = require('path').join;
13+
var resource = require('koa-resourcer');
14+
var docs = require('koa-resourcer-docs');
15+
16+
var app = koa();
17+
resource(app, join(__dirname, 'resources'), docs.addResource);
18+
app.listen();
19+
```
20+
21+
In each resource app:
22+
```js
23+
var koa = require('koa');
24+
var Router = require('koa-joi-router');
25+
26+
var router = Router();
27+
var app = module.exports = koa();
28+
29+
// Expose routes to documentation generator
30+
app.routes = router.routes;
31+
32+
// Define some routes...
33+
34+
app.use(router.middleware());
35+
```
36+
37+
## Configuration
38+
Add a description to the route config:
39+
```js
40+
router.get('/', {description: 'Home page'}, function* () {
41+
this.body = "Home page under construction since 2009";
42+
});
43+
```
44+
45+
Hide a resource by not exposing routes:
46+
```js
47+
// Expose routes to documentation generator
48+
//app.routes = router.routes;
49+
```
50+
51+
Hide individual routes in a resource app from documentation by adding `hide: true` to route metadata:
52+
```js
53+
// Documented route:
54+
router.get('/', {description: 'Main route'}, function* () {
55+
this.body = 'Hello world';
56+
});
57+
58+
// Hidden route:
59+
router.get('/secretRoute', {description: 'Nobody here but us chickens.', hide: true}, function* () {
60+
this.body = 'This is a hidden world';
61+
});
62+
```
63+
64+
## Installation
65+
```
66+
npm install koa-resourcer-docs --save
67+
```
68+
69+
## Sponsored by
70+
71+
[Pebble Technology!](https://getpebble.com)
72+
73+
## LICENSE
74+
75+
[MIT](/LICENSE)

docs.js

+170
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
'use strict';
2+
3+
var fs = require('fs');
4+
var join = require('path').join;
5+
var dot = require('dot');
6+
var koa = require('koa');
7+
var router = require('koa-joi-router');
8+
9+
var docs = module.exports = koa();
10+
var r = router();
11+
12+
// holds the routes as they are added by koa-resourcer
13+
var routes = [];
14+
15+
// expose routes to docs generator
16+
docs.routes = r.routes;
17+
18+
var templatePath = join(__dirname, 'template.dot');
19+
var template = loadTemplate(templatePath);
20+
21+
// holds the routes description object
22+
var objCache = null;
23+
24+
// holds the compiled html routes description string data
25+
var htmlCache = null;
26+
27+
/**
28+
* API documentation in JSON format
29+
*/
30+
31+
r.get('/json'
32+
, {
33+
description: 'API documentation in JSON format.'
34+
}
35+
, function* () {
36+
if (objCache) {
37+
this.body = objCache;
38+
return;
39+
}
40+
41+
this.body = objCache = { docs: describeRoutes(routes) };
42+
}
43+
);
44+
45+
/**
46+
* API documentation in human-readable HTML format
47+
*/
48+
49+
r.get('/'
50+
, {
51+
description: 'API documentation in human-readable HTML format.'
52+
}
53+
, function* () {
54+
if (htmlCache) {
55+
this.body = htmlCache;
56+
return;
57+
}
58+
59+
if (objCache) {
60+
this.body = htmlCache = template(objCache);
61+
return;
62+
}
63+
64+
objCache = { docs: describeRoutes(routes) };
65+
this.body = htmlCache = template(objCache);
66+
}
67+
);
68+
69+
docs.use(r.middleware());
70+
71+
/**
72+
* Handles the route object passed to the resourcer callback
73+
* @api public
74+
*/
75+
76+
docs.addRoute = function addRoute(o) {
77+
routes.push(o)
78+
};
79+
80+
/**
81+
* Clear documentation cache
82+
* @api public
83+
*/
84+
85+
docs.clearCache = function clearCache() {
86+
htmlCache = null;
87+
objCache = null;
88+
};
89+
90+
/**
91+
* Load the documentation template.
92+
* @api private
93+
*/
94+
95+
function loadTemplate(path) {
96+
try {
97+
return dot.compile(fs.readFileSync(path));
98+
} catch (e) {
99+
// Ignoring this line for code coverage until custom templates are supported.
100+
/* istanbul ignore next */
101+
return dot.compile("Failed to load documentation template from path: " + path);
102+
}
103+
}
104+
105+
/**
106+
* Produce the routes description object.
107+
* @api private
108+
*/
109+
110+
function describeRoutes(routes) {
111+
return routes.filter(function (route) {
112+
return isObject(route.resource.routes);
113+
}).map(function (route) {
114+
return {
115+
path: route.path
116+
, routes: route.resource.routes.filter(function (route) {
117+
return !route.hide;
118+
}).map(describeRoute)
119+
};
120+
});
121+
}
122+
123+
/**
124+
* Produces a human readable description of the route.
125+
* @api private
126+
*/
127+
128+
function describeRoute(route) {
129+
var ret = {};
130+
131+
Object.keys(route).forEach(function(key){
132+
if ('handler' == key) return;
133+
if ('validate' == key) return;
134+
ret[key] = route[key];
135+
});
136+
137+
if (route.validate) {
138+
// this is the validation object being used by the routes themselves,
139+
// do not change this object.
140+
ret.validate = describeObject(route.validate);
141+
}
142+
143+
return ret;
144+
}
145+
146+
/**
147+
* Produces a human readable description of the route validators.
148+
* @api private
149+
*/
150+
151+
function describeObject(o) {
152+
if ('function' == typeof o.describe) return o.describe();
153+
154+
var ret = {};
155+
if (!isObject(o)) return o;
156+
157+
Object.keys(o).forEach(function(key) {
158+
ret[key] = describeObject(o[key]);
159+
});
160+
161+
return ret;
162+
}
163+
164+
/**
165+
* @api private
166+
*/
167+
168+
function isObject(o) {
169+
return 'object' == typeof o && null !== o;
170+
}

package.json

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "koa-resourcer-docs",
3+
"version": "0.1.0",
4+
"private": true,
5+
"description": "Simple route documentation for koa-resourcer.",
6+
"main": "docs.js",
7+
"license": "MIT",
8+
"readmeFilename": "README.md",
9+
"keywords": [
10+
"koa",
11+
"koa-joi-router",
12+
"koa-resourcer",
13+
"resource",
14+
"router",
15+
"documentation"
16+
],
17+
"homepage": "https://github.com/pebble/koa-resourcer-docs",
18+
"repository": {
19+
"type": "git",
20+
"url": "https://github.com/pebble/koa-resourcer-docs.git"
21+
},
22+
"bugs": {
23+
"url": "https://github.com/pebble/koa-resourcer-docs/issues"
24+
},
25+
"engines": {
26+
"node": ">= 0.11.4"
27+
},
28+
"dependencies": {
29+
"dot": "1.0.2",
30+
"koa": "0.10.0",
31+
"koa-joi-router": "1.3.0"
32+
},
33+
"devDependencies": {
34+
"koa-resourcer": "1.0.0",
35+
"istanbul-harmony": "^0.3.0",
36+
"co-mocha": "1.0.1",
37+
"co-supertest": "^0.0.7",
38+
"mocha": "^1.21.4",
39+
"supertest": "^0.13.0"
40+
},
41+
"scripts": {
42+
"test": "make test",
43+
"test-cov": "make test-cov"
44+
}
45+
}

0 commit comments

Comments
 (0)