Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 072d3d8
Showing
5 changed files
with
303 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1 @@ | |||
node_modules |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,49 @@ | |||
#!/usr/bin/env node | |||
|
|||
var rhizome = require('..'); | |||
var pegjs = require('pegjs'); | |||
var fs = require('fs'); | |||
|
|||
var usage = 'Usage: rhizome routes.pegjs [/uri/path/to/test]'; | |||
|
|||
var filename = process.argv[2]; | |||
var uri = process.argv[3]; | |||
|
|||
if (process.argv.length < 3) { | |||
console.log(usage); | |||
process.exit(1); | |||
} | |||
|
|||
|
|||
var grammar = fs.readFileSync(filename, 'utf8'); | |||
|
|||
try { | |||
var parser = pegjs.buildParser(grammar); | |||
} | |||
catch (e) { | |||
console.log(e); | |||
process.exit(2); | |||
} | |||
|
|||
var config = { | |||
parser: parser, | |||
debug: true | |||
}; | |||
|
|||
// var router = rhizome(config); | |||
|
|||
if (uri) { | |||
var parsetree = rhizome.parse(config, uri + '#'); | |||
var leaf = rhizome.resolve(parsetree).node; | |||
console.log(leaf); | |||
} | |||
|
|||
// var rhizome = require('rhizome'); | |||
// var resources = require('./resources'); | |||
// var grammar = 'a valid peg.js grammar'; | |||
// | |||
// | |||
// app.use(rhizome({ | |||
// parser: parser, | |||
// resources: resources | |||
// })); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,227 @@ | |||
var util = require('util'); | |||
var url = require('url'); | |||
|
|||
// | |||
// Rhizome | |||
// | |||
// A connect/express middleware for dispatching requests to a | |||
// hierarchy of resources, using a grammar to describe the | |||
// structure of the url hierarchy. | |||
// | |||
// Synopsis: | |||
// | |||
// var rhizome = require('rhizome'); | |||
// var pegjs = require('pegjs'); | |||
// var resources = require('./resources'); | |||
// var grammar = 'a valid peg.js grammar'; | |||
// | |||
// var parser = pegjs.buildParser(grammar); | |||
// | |||
// app.use(rhizome({ | |||
// parser: parser, | |||
// resources: resources | |||
// })); | |||
// | |||
// You construct a pegjs parser from the given grammar. | |||
// Then for each request passed to this middleware, the parser | |||
// is given req.url as input, and the resulting parse tree is | |||
// traversed to resolve the request to a resource. See also the | |||
// comments for rhizome.resolve, below. | |||
// | |||
// config fields: | |||
// | |||
// parser A pegjs-compatible parser object, | |||
// having parser.parse(input, [startRule]) | |||
// | |||
// resources An object holding resource constructors. | |||
// tree resolves a leaf resource. | |||
// | |||
// [makeinput] Function to extract from a request the input | |||
// to be parsed. Defaults to rhizome.makeinput | |||
// | |||
// [startRule] The name of the start rule in the grammar. | |||
// Defaults to the first declared rule. | |||
// | |||
// | |||
// [parse] Function to parse a request into a parse tree. | |||
// Defaults to rhizome.parse. | |||
// | |||
// [dispatch] Function to dispatch a request against | |||
// a resource. Defaults to rhizome.dispatch. | |||
// | |||
// [debug] True to log parse errors. Defaults to false. | |||
// | |||
var rhizome = module.exports = function rhizome(config) { | |||
var parse = config.parse || rhizome.parse; | |||
var dispatch = config.dispatch || rhizome.dispatch; | |||
var makeinput = config.makeinput || rhizome.makeinput; | |||
function getResource(base, selector) { | |||
var input = url.resolve(base, selector); | |||
var tree = parse(config, input); | |||
if (!tree) { | |||
return null; | |||
} | |||
var result = rhizome.resolve(tree); | |||
if (!result.node) { | |||
return null; | |||
} | |||
var path = result.suffix ? | |||
input.slice(0, -result.suffix.length) : | |||
input; | |||
var type = result.node.type; | |||
var Constructor = config.resources[type]; | |||
if (!(Constructor && Constructor instanceof Function)) { | |||
throw new TypeError('Unknown resource type ' + | |||
util.inspect(type)); | |||
} | |||
function select(relpath) { | |||
var result = getResource(path + '/', relpath); | |||
return result ? result.resource : null; | |||
} | |||
result.resource = new Constructor(path, result.node, select); | |||
return result; | |||
} | |||
return function (req, res, next) { | |||
var result = getResource('', makeinput(req), req); | |||
if (result && result.resource) { | |||
dispatch(config, result, req, res, next); | |||
} else { | |||
next(); | |||
} | |||
|
|||
}; | |||
}; | |||
|
|||
// | |||
// rhizome.makeinput | |||
// | |||
// The user may replace this implementation by providing config.makeinput, | |||
// for example to construct input based on req.hostname and req.url. | |||
// | |||
rhizome.makeinput = function makeinput(req) { | |||
return req.url + '#'; | |||
}; | |||
|
|||
// | |||
// rhizome.parse | |||
// | |||
// Parse a url and return a parse tree. | |||
// | |||
// This default implementation is suitable for pegjs parsers. | |||
// If a parser syntax error occurs, it returns null to indicate | |||
// that the url did not match the grammar. | |||
// | |||
rhizome.parse = function parse(config, input) { | |||
var parser = config.parser; | |||
var tree; | |||
try { | |||
tree = parser.parse(input, config.startRule); | |||
} | |||
catch (e) { | |||
// pegjs provides parser.SyntaxError, but we try | |||
// to be compatible with other parsers that might not. | |||
if (parser.SyntaxError && (e instanceof parser.SyntaxError)) { | |||
if (config.debug) { | |||
var marker = ''; | |||
if (e.column) { | |||
for (var i = e.column; i > 1; i--) { | |||
marker += ' '; | |||
} | |||
marker += '^ '; | |||
} | |||
console.log(input + '\n' + marker); | |||
console.log(e); | |||
} | |||
return null; | |||
} | |||
throw e; | |||
} | |||
return tree; | |||
}; | |||
|
|||
// | |||
// rhizome.dispatch | |||
// | |||
// Dispatch a request against the resolved resource object. | |||
// | |||
// It looks for a method on the resource object having the same name as the | |||
// http verb of the request, i.e. resource.get, resource.post etc, or | |||
// resource.all as a fallback. If that method exists it is called, e.g: | |||
// | |||
// resource.get(req, res, next, suffix); | |||
// | |||
// Arguments: | |||
// | |||
// config The config object that was passed to rhizome. | |||
// | |||
// result The result of resolving against the parse tree. | |||
// result.resource is the resource object. | |||
// result.suffix is the url portion following the resource's path. | |||
// | |||
// req,res,next The request and response objects and the next function passed | |||
// to this middleware for this request. Each call to dispatch | |||
// should normally result eventually in a call to res.end (via | |||
// res.send, res.render etc) or next(err). | |||
// | |||
// | |||
rhizome.dispatch = function (config, result, req, res, next) { | |||
var resource = result.resource; | |||
var suffix = result.suffix; | |||
if (!resource) { | |||
// No error but resolved to a null resource - fall through silently. | |||
if (config.debug) { | |||
console.log(req.url + ' resolved to null resource'); | |||
} | |||
return next(); | |||
} | |||
if ('object' !== typeof resource) { | |||
return next(new TypeError('resource is not an object')); | |||
} | |||
var fn = resource[req.method.toLowerCase()] || resource.all; | |||
if ('function' === typeof fn) { | |||
fn.call(resource, req, res, next, suffix); | |||
} else { | |||
res.send('Resource at ' + req.url + | |||
' does not implement ' + req.method + '\r\n', 404); | |||
} | |||
}; | |||
|
|||
// | |||
// rhizome.resolve | |||
// | |||
// Resolve from resourceRoot to a resource selected by the parse tree. | |||
// | |||
// The parser returns a parse tree representing the url. This is | |||
// a tree of nested arrays. | |||
// | |||
// Returns: | |||
// | |||
// { | |||
// node: // the last object in the tree, or null if none. | |||
// suffix: // string component of the url following node | |||
// } | |||
// | |||
rhizome.resolve = function resolve(tree) { | |||
if (Array.isArray(tree)) { | |||
var node; | |||
var suffix = ''; | |||
for (var i = tree.length - 1; !node && i >= 0; i--) { | |||
var branch = resolve(tree[i]); | |||
node = branch.node; | |||
suffix = branch.suffix + suffix; | |||
} | |||
return { | |||
node: node, | |||
suffix: suffix | |||
}; | |||
} else if ('string' === typeof tree) { | |||
return { | |||
suffix: tree | |||
}; | |||
} else { | |||
return { | |||
node: tree, | |||
suffix: '' | |||
}; | |||
} | |||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,26 @@ | |||
{ | |||
"name": "rhizome", | |||
"version": "0.1.0", | |||
"description": "Use a peg.js grammar to route url requests", | |||
"author": { | |||
"name": "Ben Williamson", | |||
"email": "benw@pobox.com" | |||
}, | |||
"license": "MIT", | |||
"main": "index.js", | |||
"scripts": { | |||
"test": "node_modules/mocha/bin/mocha" | |||
}, | |||
"repository": { | |||
"type": "git", | |||
"url": "git://github.com/benw/rhizome.git" | |||
}, | |||
"dependencies": { | |||
"pegjs": "0.7.x" | |||
}, | |||
"devDependencies": { | |||
"mocha": "1.4.x", | |||
"supertest": "0.2.x", | |||
"chai": "1.2.x" | |||
} | |||
} |