Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added tests, read me and travis conf

  • Loading branch information...
commit 80f1c1d4a3a7b14c0ec14ccff2420795d4c2150d 1 parent 835904b
@ramitos authored
View
1  .gitignore
@@ -0,0 +1 @@
+/node_modules/
View
3  .travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+ - 0.8
View
28 package.json
@@ -1,19 +1,27 @@
{
- "name": "turnout",
- "version": "0.0.1",
+ "author": "Sérgio Ramos <mail@sergioramos.me>",
"description": "http router",
- "main": "turnout.js",
- "scripts": {
- "test": "node test/runner.js"
- },
+ "main": "src/turnout.js",
+ "version": "0.0.1",
+ "name": "turnout",
+ "license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/ramitos/turnout.git"
},
+ "devDependencies": {
+ "blage": "0.0.x",
+ "specced": "0.0.x",
+ "request": "2.11.x"
+ },
+ "scripts": {
+ "test": "node test/runner.js"
+ },
+ "directories": {
+ "test": "test"
+ },
"keywords": [
"http",
"router"
- ],
- "author": "Sérgio Ramos <mail@sergioramos.me>",
- "license": "FreeBSD"
-}
+ ]
+}
View
97 readme.md
@@ -0,0 +1,97 @@
+# turnout
+
+## usage
+
+ var router = require('../')()
+
+ require('http').createServer(function (req, res) {
+ router(req, res, function () {
+ res.statusCode = 404
+ res.end()
+ })
+ }).listen()
+
+ router.post('/todo/:id', function (req, res, params, query) {})
+
+ router.put('/todo/:id/:state', function (req, res, params, query) {})
+
+ router.get('/user/:id', function (req, res, params, query) {})
+
+ router.del('/user/:id', function (req, res, params, query) {})
+
+## install
+ npm install turnout
+
+## test
+ npm test
+
+## api
+
+### methods
+
+###### get
+
+ router.get('/', callback)
+
+###### post
+
+ router.post('/', callback)
+
+###### put
+
+ router.put('/', callback)
+
+###### delete
+
+ router.del('/', callback)
+
+### path
+
+###### path
+
+ router.get('/todo/:id', callback)
+
+###### regexp
+
+ router.get(/\/todo\/(\d*?)/, callback)
+
+### params
+
+ router.get('/todo/:id', function (req, res, params) {
+ assert(params.id === '5')
+ })
+
+ request.get('http://address:port/todo/5')
+
+### query
+
+ router.get('/todos/:state', function (req, res, params, query) {
+ assert(query.priority === 'urgent')
+ })
+
+ request.get('http://address:port/todo/completed?priority=urgent')
+
+## credits
+ * The API is inspired by [TJ Holowaychuk](https://github.com/visionmedia)'s [express](https://github.com/visionmedia/express).
+ * The path regular expression function comes from [TJ Holowaychuk](https://github.com/visionmedia)'s [express](https://github.com/visionmedia/express). The copyright is present.
+
+## license
+ Copyright (C) 2012 Sérgio Ramos <mail@sergioramos.me>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
View
38 src/regex.js
@@ -0,0 +1,38 @@
+/** Copyright (c) 2009-2011 TJ Holowaychuk <tj@vision-media.ca>
+ *
+ * Normalize the given path string,
+ * returning a regular expression.
+ *
+ * An empty array should be passed,
+ * which will contain the placeholder
+ * key names. For example "/user/:id" will
+ * then contain ["id"].
+ *
+ * @param {String|RegExp|Array} path
+ * @param {Array} keys
+ * @param {Boolean} sensitive
+ * @param {Boolean} strict
+ * @return {RegExp}
+ * @api private
+ */
+module.exports = function(path, keys, sensitive, strict) {
+ if (path instanceof RegExp) return path;
+ if (Array.isArray(path)) path = '(' + path.join('|') + ')';
+ path = path
+ .concat(strict ? '' : '/?')
+ .replace(/\/\(/g, '(?:/')
+ .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){
+ keys.push({ name: key, optional: !! optional });
+ slash = slash || '';
+ return ''
+ + (optional ? '' : slash)
+ + '(?:'
+ + (optional ? slash : '')
+ + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
+ + (optional || '')
+ + (star ? '(/*)?' : '');
+ })
+ .replace(/([\/.])/g, '\\$1')
+ .replace(/\*/g, '(.*)');
+ return new RegExp('^' + path + '$', sensitive ? '' : 'i');
+}
View
64 src/turnout.js
@@ -0,0 +1,64 @@
+var qs = require('querystring'),
+ regex = require('./regex'),
+ url = require('url')
+
+var build = function () {
+ var routes = {};
+ new Array('get', 'post', 'put', 'delete').forEach(function (method) {
+ routes[method] = {}
+ })
+ return routes
+}
+
+var route = function (req, res, routes, fallback) {
+ var method = req.method.toLowerCase()
+ var parsedurl = url.parse(req.url)
+ var querystring = parsedurl.query
+ var query = qs.parse(querystring)
+ var pathname = parsedurl.pathname
+ var callback = null
+ var params = {}
+
+ Object.keys(routes[method]).forEach(function (route) {
+ var keys = []
+ var rexp = regex(route, keys, false, false)
+ var match = pathname.match(rexp)
+ if(!match) return
+
+ match.shift()
+ callback = routes[method][route]
+
+ match.forEach(function (param, index) {
+ params[keys[index].name] = param
+ })
+ })
+
+ if(!callback) fallback()
+ else callback(req, res, params, query)
+}
+
+module.exports = function () {
+ var routes = build()
+
+ var turnout = function (req, res, next) {
+ route(req, res, routes, next)
+ }
+
+ turnout.get = function (path, callback) {
+ routes.get[path] = callback
+ }
+
+ turnout.post = function (path, callback) {
+ routes.post[path] = callback
+ }
+
+ turnout.put = function (path, callback) {
+ routes.put[path] = callback
+ }
+
+ turnout.del = function (path, callback) {
+ routes.delete[path] = callback
+ }
+
+ return turnout
+}
View
29 test/runner.js
@@ -0,0 +1,29 @@
+var interpolate = require('util').format,
+ test = require('specced')(),
+ join = require('path').join,
+ router = require('../')(),
+ blage = require('blage')
+
+test.timeout = 10000
+
+test.specs = {
+ routing: require('./specs/routing'),
+ params: require('./specs/params'),
+ query: require('./specs/query')
+}
+
+test.before = function (helpers, callback) {
+ var onRequest = blage(router)
+ var address = require('http').createServer(onRequest).listen().address()
+ helpers.address = interpolate('http://%s:%s', address.address, address.port)
+ helpers.router = router
+ callback()
+}
+
+test.run(function (e) {
+ console.log('tests not passed')
+ process.exit()
+}, function () {
+ console.log('all passed')
+ process.exit()
+})
View
57 test/specs/params.js
@@ -0,0 +1,57 @@
+var interpolate = require('util').format,
+ request = require('request'),
+ assert = require('assert')
+
+module.exports = function (helpers, callback) {
+ var state = {}
+ var on = {}
+
+ var check = function (method) {
+ state[method] = true
+ if(state.post && state.del && state.get && state.put) callback()
+ }
+
+ on.post = function (req, res, params) {
+ assert(req.method.toLowerCase() === 'post')
+ assert(req.url === '/user/5/21')
+ assert(params.age === '21')
+ assert(params.id === '5')
+ res.end()
+ check('post')
+ }
+
+ on.del = function (req, res, params) {
+ assert(req.method.toLowerCase() === 'delete')
+ assert(req.url === '/user/5')
+ assert(params.id === '5')
+ res.end()
+ check('del')
+ }
+
+ on.get = function (req, res, params) {
+ assert(req.method.toLowerCase() === 'get')
+ assert(req.url === '/user/5/age')
+ assert(params.id === '5')
+ res.end()
+ check('get')
+ }
+
+ on.put = function (req, res, params) {
+ assert(req.method.toLowerCase() === 'put')
+ assert(req.url === '/user/admin')
+ assert(params.subtype === 'admin')
+ assert(params.type === 'user')
+ res.end()
+ check('put')
+ }
+
+ helpers.router.post('/user/:id/:age', on.post)
+ helpers.router.put('/:type/:subtype', on.put)
+ helpers.router.get('/user/:id/age', on.get)
+ helpers.router.del('/user/:id', on.del)
+
+ request.post(interpolate('%s/user/5/21', helpers.address))
+ request.put(interpolate('%s/user/admin', helpers.address))
+ request.get(interpolate('%s/user/5/age', helpers.address))
+ request.del(interpolate('%s/user/5', helpers.address))
+}
View
59 test/specs/query.js
@@ -0,0 +1,59 @@
+var interpolate = require('util').format,
+ request = require('request'),
+ assert = require('assert')
+
+module.exports = function (helpers, callback) {
+ var state = {}
+ var on = {}
+
+ var check = function (method) {
+ state[method] = true
+ if(state.post && state.del && state.get && state.put) callback()
+ }
+
+ on.post = function (req, res, params, query) {
+ assert(req.method.toLowerCase() === 'post')
+ assert(req.url === '/user/5/21?q1=2&q2=t')
+ assert(query.q1 === '2')
+ assert(query.q2 === 't')
+ res.end()
+ check('post')
+ }
+
+ on.del = function (req, res, params, query) {
+ assert(req.method.toLowerCase() === 'delete')
+ assert(req.url === '/user/5?q1=3&q2=t')
+ assert(query.q1 === '3')
+ assert(query.q2 === 't')
+ res.end()
+ check('del')
+ }
+
+ on.get = function (req, res, params, query) {
+ assert(req.method.toLowerCase() === 'get')
+ assert(req.url === '/user/5/age?q1=3&q2=t')
+ assert(query.q1 === '3')
+ assert(query.q2 === 't')
+ res.end()
+ check('get')
+ }
+
+ on.put = function (req, res, params, query) {
+ assert(req.method.toLowerCase() === 'put')
+ assert(req.url === '/user/admin?q1=3&q2=t')
+ assert(query.q1 === '3')
+ assert(query.q2 === 't')
+ res.end()
+ check('put')
+ }
+
+ helpers.router.post('/user/:id/:age', on.post)
+ helpers.router.get('/user/:id/age', on.get)
+ helpers.router.del('/user/:id', on.del)
+ helpers.router.put('/:type/:subtype', on.put)
+
+ request.post(interpolate('%s/user/5/21?q1=2&q2=t', helpers.address))
+ request.get(interpolate('%s/user/5/age?q1=3&q2=t', helpers.address))
+ request.put(interpolate('%s/user/admin?q1=3&q2=t', helpers.address))
+ request.del(interpolate('%s/user/5?q1=3&q2=t', helpers.address))
+}
View
51 test/specs/routing.js
@@ -0,0 +1,51 @@
+var interpolate = require('util').format,
+ request = require('request'),
+ assert = require('assert')
+
+module.exports = function (helpers, callback) {
+ var state = {}
+ var on = {}
+
+ var check = function (method) {
+ state[method] = true
+ if(state.post && state.del && state.get && state.put) callback()
+ }
+
+ on.post = function (req, res, params, query) {
+ assert(req.method.toLowerCase() === 'post')
+ assert(req.url === '/')
+ res.end()
+ check('post')
+ }
+
+ on.del = function (req, res, params, query) {
+ assert(req.method.toLowerCase() === 'delete')
+ assert(req.url === '/')
+ res.end()
+ check('del')
+ }
+
+ on.get = function (req, res, params, query) {
+ assert(req.method.toLowerCase() === 'get')
+ assert(req.url === '/')
+ res.end()
+ check('get')
+ }
+
+ on.put = function (req, res, params, query) {
+ assert(req.method.toLowerCase() === 'put')
+ assert(req.url === '/')
+ res.end()
+ check('put')
+ }
+
+ helpers.router.post('/', on.post)
+ helpers.router.get('/', on.get)
+ helpers.router.del('/', on.del)
+ helpers.router.put('/', on.put)
+
+ request.post(helpers.address)
+ request.get(helpers.address)
+ request.put(helpers.address)
+ request.del(helpers.address)
+}
View
0  turnout.js
No changes.
Please sign in to comment.
Something went wrong with that request. Please try again.