Skip to content
Browse files

optional parts

  • Loading branch information...
1 parent 90bbc95 commit 0f1f341ec6c409c20eababe56512d75cf70ac276 @joshbuddy committed Feb 3, 2010
Showing with 163 additions and 20 deletions.
  1. +24 −0 README.rdoc
  2. +96 −20 lib/sherpa.js
  3. +16 −0 spec/spec_generate.js
  4. +27 −0 spec/spec_recognize.js
View
24 README.rdoc
@@ -79,6 +79,30 @@ Would return
}
}
+Optional parts of the path are marked with <tt>(</tt> and <tt>)</tt>. For example:
+
+ sherpa.add('/test(/:variable)').to('testing')
+ sherpa.recognize('/test/world')
+
+Would return
+
+ {
+ "destination": "testing",
+ "params": {
+ "variable": "world"
+ }
+ }
+
+And
+ sherpa.add('/test(/:variable)').to('testing')
+ sherpa.recognize('/test')
+
+Would return only
+ {
+ "destination": "testing",
+ "params": { }
+ }
+
You can also specify other parts of the request to match on using <tt>conditions</tt>.
sherpa.add('/test', {conditions: {method: 'GET'}}).to('testing')
View
116 lib/sherpa.js
@@ -26,13 +26,61 @@ Sherpa = {
Variable: function(name) {
this.name = name;
},
- Route: function(router, finalNode) {
- this.router = router;
+ Route: function(routeSet, finalNode) {
+ this.routeSet = routeSet;
this.finalNode = finalNode;
},
+ RouteSet: function(router) {
+ this.routes = [];
+ this.router = router;
+ },
interfaces: { }
}
+Sherpa.RouteSet.prototype = {
+ to: function(destination) {
+ for (var routeIndex = 0; routeIndex != this.routes.length; routeIndex++) {
+ this.routes[routeIndex].finalNode.destination = destination;
+ }
+ return this;
+ },
+ name: function(name) {
+ this.router.routes[name] = this;
+ return this;
+ },
+ matchingNode: function(params) {
+ if (this.routes.length == 1) {
+ return this.routes[0].finalNode;
+ } else {
+ var maximumMatchedRoute = undefined;
+ var maximumMatchedParams = -1;
+ for (var i = 0; i != this.routes.length; i++) {
+ var route = this.routes[i];
+ var node = route.finalNode;
+ var paramCount = 0;
+ while (node && node.value) {
+ if (node.value.name !== undefined) {
+ if ((params === undefined) || (params[node.value.name] === undefined)) {
+ paramCount = -1;
+ node = undefined;
+ } else {
+ paramCount += 1;
+ node = node.parent;
+ }
+ } else {
+ node = node.parent;
+ }
+ }
+ if (paramCount != -1 && paramCount > maximumMatchedParams) {
+ maximumMatchedParams = paramCount;
+ maximumMatchedNode = route.finalNode;
+ }
+ }
+ return maximumMatchedNode;
+ }
+ }
+};
+
Sherpa.Router.prototype = {
compareRequestKeys: function(k1, k2) {
@@ -46,11 +94,45 @@ Sherpa.Router.prototype = {
return 0;
}
},
-
+
add: function(uri, options) {
+ var paths = [""];
+ var chars = uri.split('');
+
+ var startIndex = 0;
+ var endIndex = 1;
+
+ for (var charIndex in chars) {
+ var c = chars[charIndex];
+ if (c == '(') {
+ // over current working set, double paths
+ for (var pathIndex = startIndex; pathIndex != endIndex; pathIndex++) {
+ paths.push(paths[pathIndex]);
+ }
+ // move working set to newly copied paths
+ startIndex = endIndex;
+ endIndex = paths.length;
+ } else if (c == ')') {
+ // expand working set scope
+ startIndex -= (endIndex - startIndex);
+ } else {
+ for (var i = startIndex; i != endIndex; i++) {
+ paths[i] += c;
+ }
+ }
+ }
+
+ var routeSet = new Sherpa.RouteSet(this);
+ for (var pathIndex = 0; pathIndex != paths.length; pathIndex++) {
+ routeSet.routes.push(this.addPath(routeSet, paths[pathIndex], options));
+ }
+ return routeSet;
+ },
+
+ addPath: function(routeSet, uri, options) {
var splitUri = uri.split(Sherpa.SplitRegex);
var node = this.root;
- for(var i in splitUri) {
+ for (var i in splitUri) {
var part = splitUri[i];
if (part != '') {
var firstChar = part.substring(0,1)
@@ -75,7 +157,7 @@ Sherpa.Router.prototype = {
var preRequestNode = node;
node.destination = undefined;
- for(var requestKeyIndex = 0; requestKeyIndex < this.requestKeys.length; requestKeyIndex++) {
+ for (var requestKeyIndex = 0; requestKeyIndex < this.requestKeys.length; requestKeyIndex++) {
var key = this.requestKeys[requestKeyIndex];
// if it exists, we have to deal with it
@@ -149,7 +231,7 @@ Sherpa.Router.prototype = {
}
}
- var route = new Sherpa.Route(this, node);
+ var route = new Sherpa.Route(routeSet, node);
return route;
},
recognize: function(uri, request) {
@@ -168,7 +250,7 @@ Sherpa.Router.prototype = {
var matched = false;
if (node.shortcut.length != 0) {
- for(var shortcutIndex in node.shortcut) {
+ for (var shortcutIndex in node.shortcut) {
if (match = uri.match(node.shortcut[shortcutIndex][0])) {
uri = uri.substring(match[0].length);
node = node.shortcut[shortcutIndex][1];
@@ -231,7 +313,7 @@ Sherpa.Router.prototype = {
}
case 0:
if (node.requestShortcut.length != 0 && request[this.requestKeys[index]] !== undefined) {
- for(var requestShortcutIndex in node.requestShortcut) {
+ for (var requestShortcutIndex in node.requestShortcut) {
if ((match = request[this.requestKeys[index]].match(node.requestShortcut[requestShortcutIndex][0])) && match[0] == request[this.requestKeys[index]]) {
return node.requestShortcut[requestShortcutIndex][1];
}
@@ -253,8 +335,10 @@ Sherpa.Router.prototype = {
generate: function(name, params) {
var pathParts = [];
- var route = this.routes[name];
- var node = route.finalNode;
+ var routeSet = this.routes[name];
+ var node = routeSet.matchingNode(params);
+
+ if (!node) throw("matching route not found in "+name);
while (node && node.value) {
if (node.value.name !== undefined) {
@@ -275,7 +359,7 @@ Sherpa.Router.prototype = {
node = node.parent;
}
var path = '';
- for(var i in pathParts) {
+ for (var i in pathParts) {
path += pathParts[pathParts.length - i - 1];
}
path = encodeURI(path);
@@ -289,14 +373,6 @@ Sherpa.Router.prototype = {
};
Sherpa.Route.prototype = {
- name: function(name) {
- this.router.routes[name] = this;
- return this;
- },
- to: function(destination) {
- this.finalNode.destination = destination;
- return this;
- }
}
Sherpa.Node.prototype = {
@@ -321,7 +397,7 @@ Sherpa.interfaces.NodeJs.prototype = {
res.finish();
}
- for(var key in this.routes) {
+ for (var key in this.routes) {
if (this.routes[key][0] == 'not found') {
notFound = this.routes[key][1];
} else {
View
16 spec/spec_generate.js
@@ -23,6 +23,22 @@ describe("Sherpa - generate", function() {
assertEqual('/asd', router.generate('with_variable', {test: 'asd'}))
});
+ it("should generate a route with a optionals in it", function() {
+ var router = new Sherpa.Router();
+ router.add('/(:test)').name('with_optional');
+ assertEqual('/', router.generate('with_optional'))
+ assertEqual('/hello', router.generate('with_optional', {test: 'hello'}))
+ });
+
+ it("should generate a route with nested optionals in it", function() {
+ var router = new Sherpa.Router();
+ router.add('/(:test(/:test2))').name('with_optional');
+ assertEqual('/', router.generate('with_optional'))
+ assertEqual('/hello', router.generate('with_optional', {test: 'hello'}))
+ assertEqual('/hello/world', router.generate('with_optional', {test: 'hello', test2: 'world'}))
+ assertEqual('/?test2=hello', router.generate('with_optional', {test2: 'hello'}))
+ });
+
it("should generate extra params as a query string after", function() {
var router = new Sherpa.Router();
router.add('/:test', {matchesWith: {test: /asd|qwe|\d+/}}).name('with_variable');
View
27 spec/spec_recognize.js
@@ -7,6 +7,33 @@ describe("Sherpa - recognize", function() {
assertEqual('recognized', router.recognize('/test').destination);
});
+ it("should recognize a simple route", function() {
+ var router = new Sherpa.Router();
+ router.add('/test').to('recognized');
+ assertEqual('recognized', router.recognize('/test').destination);
+ });
+
+ it("should recognize a simple route with optionals", function() {
+ var router = new Sherpa.Router();
+ router.add('/(test)').to('recognized');
+ assertEqual('recognized', router.recognize('/test').destination);
+ assertEqual('recognized', router.recognize('/').destination);
+ });
+
+ it("should recognize a simple route with nested optionals", function() {
+ var router = new Sherpa.Router();
+ router.add('(/test(/test2(/test3)))(/test4)').to('recognized');
+ assertEqual('recognized', router.recognize('/test').destination);
+ assertEqual('recognized', router.recognize('/test/test2').destination);
+ assertEqual('recognized', router.recognize('/test/test2/test3').destination);
+ assertEqual('recognized', router.recognize('/test/test4').destination);
+ assertEqual('recognized', router.recognize('/test/test2/test4').destination);
+ assertEqual('recognized', router.recognize('/test/test2/test3/test4').destination);
+ assertEqual('recognized', router.recognize('/test4').destination);
+ assertEqual(undefined, router.recognize('/test/test3'));
+ assertEqual(undefined, router.recognize('/test/test3/test4'));
+ });
+
it("should recognize a route with a variable in it", function() {
var router = new Sherpa.Router();
router.add('/:test').to('recognized');

0 comments on commit 0f1f341

Please sign in to comment.
Something went wrong with that request. Please try again.