Skip to content

Commit

Permalink
Fix #18, fix and add some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ruslankerimov committed Oct 12, 2014
1 parent fad161c commit 40b04c8
Show file tree
Hide file tree
Showing 16 changed files with 249 additions and 156 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ unittests: nodejsunittests browsersunittests

.PHONY: unittests_in_nodejs
nodejsunittests: $(MOCHA)
$(MOCHA) -u bdd -R spec -r chai $(PRJ_DIR)test/lib/nodejs.js $(PRJ_DIR)test/*.js
$(MOCHA) -u bdd -R spec -r chai $(PRJ_DIR)test/nodejs $(PRJ_DIR)test

.PHONY: unittests_in_browsers
browsersunittests: $(KARMA) build
Expand All @@ -41,7 +41,7 @@ jscs: $(JSCS)

.PHONY: coverage
coverage: $(ISTANBUL) $(_MOCHA)
$(ISTANBUL) cover $(_MOCHA) -- -u exports $(PRJ_DIR)test
$(ISTANBUL) cover $(_MOCHA) -- -u bdd $(PRJ_DIR)test/nodejs $(PRJ_DIR)test

$(JSHINT) $(MOCHA) $(_MOCHA) $(ISTANBUL) $(JSCS) $(BORSCHIK) $(KARMA):
npm install
Expand Down
139 changes: 68 additions & 71 deletions dist/susanin.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,6 @@ function Route(options) {

options.conditions && typeof options.conditions === 'object' || (options.conditions = {});
options.defaults && typeof options.defaults === 'object' || (options.defaults = {});
options.data && typeof options.data === 'object' || (options.data = {});
typeof options.name === 'string' && (options.data.name = options.name);

if (options.isTrailingSlashOptional !== false) {
options.pattern += GROUP_OPENED_CHAR + PARAM_OPENED_CHAR +
Expand Down Expand Up @@ -340,7 +338,7 @@ Route.prototype._parseParams = function(pattern, parts) {
* @private
*/
Route.prototype._buildParseRegExp = function() {
this._paramsMap = [];
this._reqExpParamsMap = [];
this._parseRegExpSource = '^' + this._buildParseRegExpParts(this._parts) + '$';
this._parseRegExp = new RegExp(this._parseRegExpSource);
};
Expand All @@ -360,10 +358,10 @@ Route.prototype._buildParseRegExpParts = function(parts) {

if (typeof part === 'string') {
ret += escape(part);
} else if (part && part.what === 'param') {
this._paramsMap.push(part.name);
} else if (part.what === 'param') {
this._reqExpParamsMap.push(part.name);
ret += '(' + this._buildParamValueRegExpSource(part.name) + ')';
} else if (part && part.what === 'optional') {
} else {
ret += '(?:' + this._buildParseRegExpParts(part.parts) + ')?';
}
}
Expand Down Expand Up @@ -397,6 +395,7 @@ Route.prototype._buildParamValueRegExpSource = function(paramName) {
* @private
*/
Route.prototype._buildBuildFn = function() {
this._mainParamsMap = {};
this._buildFnSource = 'var h=({}).hasOwnProperty;return ' + this._buildBuildFnParts(this._parts) + ';';
/*jshint evil:true */
this._buildFn = new Function('p', this._buildFnSource);
Expand All @@ -418,14 +417,15 @@ Route.prototype._buildBuildFnParts = function(parts) {

if (typeof part === 'string') {
ret += '+"' + escape(part) + '"' ;
} else if (part && part.what === 'param') {
} else if (part.what === 'param') {
this._mainParamsMap[part.name] = true;
ret += '+(h.call(p,"' + escape(part.name) + '")?' +
'p["' + escape(part.name) + '"]:' +
(has(defaults, part.name) ?
'"' + escape(defaults[part.name]) + '"' :
'""') +
')';
} else if (part && part.what === 'optional') {
} else {
ret += '+((false';

for (j = 0, sizeJ = part.dependOnParams.length; j < sizeJ; ++j) {
Expand All @@ -447,11 +447,36 @@ Route.prototype._buildBuildFnParts = function(parts) {
};

/**
* Matches object with route
* @param {Object|String} matchObject
* @param {Object|Function} data
* @returns {Boolean}
* @private
*/
Route.prototype._isDataMatched = function(data) {
var routeData = this._options.data,
key;

if (typeof data === 'function') {
return Boolean(data(routeData));
} else if (data && typeof data === 'object') {
for (key in data) {
if (has(data, key)) {
if ( ! routeData || typeof routeData !== 'object' || routeData[key] !== data[key]) {
return false;
}
}
}
}

return true;
};

/**
* Matches path with route
* @param {String} path
* @param {Function|Object} [data]
* @returns {Object|null}
*/
Route.prototype.match = function(matchObject) {
Route.prototype.match = function(path, data) {
var ret = null,
paramName,
matches,
Expand All @@ -462,55 +487,39 @@ Route.prototype.match = function(matchObject) {
filter = options.postMatch,
defaults = options.defaults;

if (typeof matchObject === 'string') {
matchObject = { path : matchObject };
} else if ( ! matchObject) {
if (typeof path !== 'string' || (data && ! this._isDataMatched(data))) {
return ret;
}

for (key in matchObject) {
if (has(matchObject, key) && key !== 'path') {
if (options.data[key] !== matchObject[key]) {
return ret;
}
}
}
matches = path.match(this._parseRegExp);

if (typeof matchObject.path === 'string') {
matches = matchObject.path.match(this._parseRegExp);

if (matches) {
ret = {};

for (i = 1, size = matches.length; i < size; ++i) {
if (typeof matches[i] !== 'undefined' && /* for IE lt 9*/ matches[i] !== '') {
paramName = this._paramsMap[i - 1];
if (paramName !== TRAILING_SLASH_PARAM_NAME) {
ret[paramName] = matches[i];
} else if (
matchObject.path.charAt(matchObject.path.length - 2) === TRAILING_SLASH_PARAM_VALUE
) {
return null;
}
if (matches) {
ret = {};

for (i = 1, size = matches.length; i < size; ++i) {
if (typeof matches[i] !== 'undefined' && /* for IE lt 9*/ matches[i] !== '') {
paramName = this._reqExpParamsMap[i - 1];
if (paramName !== TRAILING_SLASH_PARAM_NAME) {
ret[paramName] = matches[i];
} else if (path.charAt(path.length - 2) === TRAILING_SLASH_PARAM_VALUE) {
return null;
}
}
}

queryParams = querystring.parse(ret[QUERY_STRING_PARAM_NAME]);
for (key in queryParams) {
if (has(queryParams, key) && ! has(ret, key)) {
ret[key] = queryParams[key];
}
queryParams = querystring.parse(ret[QUERY_STRING_PARAM_NAME]);
for (key in queryParams) {
if (has(queryParams, key) && ! has(ret, key)) {
ret[key] = queryParams[key];
}
delete ret[QUERY_STRING_PARAM_NAME];
}
delete ret[QUERY_STRING_PARAM_NAME];

for (key in defaults) {
if (has(defaults, key) && ! has(ret, key)) {
ret[key] = defaults[key];
}
for (key in defaults) {
if (has(defaults, key) && ! has(ret, key)) {
ret[key] = defaults[key];
}
}
} else {
ret = {};
}

if (ret && typeof filter === 'function') {
Expand All @@ -533,8 +542,6 @@ Route.prototype.build = function(params) {
queryParams = {},
queryString,
key,
isMainParam,
i, size,
filter = this._options.preBuild;

if (typeof filter === 'function') {
Expand All @@ -547,15 +554,7 @@ Route.prototype.build = function(params) {
params[key] !== null &&
typeof params[key] !== 'undefined'
) {
isMainParam = false;
for (i = 0, size = this._paramsMap.length; i < size; ++i) {
if (this._paramsMap[i] === key) {
isMainParam = true;
break;
}
}

if (isMainParam) {
if (this._mainParamsMap[key]) {
newParams[key] = params[key];
} else {
queryParams[key] = params[key];
Expand All @@ -579,10 +578,10 @@ Route.prototype.getData = function() {

/**
* Returns name of the route
* @returns {?String}
* @returns {*}
*/
Route.prototype.getName = function() {
return this._options.data.name;
return this._options.name;
};

module.exports = Route;
Expand Down Expand Up @@ -613,7 +612,7 @@ function Router() {

/**
* Add route
* @param {Object|String} options
* @param {RouteOptions} options
* @returns {Route}
*/
Router.prototype.addRoute = function(options) {
Expand All @@ -631,17 +630,16 @@ Router.prototype.addRoute = function(options) {

/**
* Returns all successfully matched routes
* @param {Object|String} matchObject
* @returns {Array|null}
* @returns {[ Route, Object ][]}
*/
Router.prototype.find = function(matchObject) {
Router.prototype.find = function() {
var ret = [],
parsed,
i, size,
routes = this._routes;

for (i = 0, size = routes.length; i < size; ++i) {
parsed = routes[i].match(matchObject);
parsed = routes[i].match.apply(routes[i], arguments);
if (parsed !== null) {
ret.push([ routes[i], parsed ]);
}
Expand All @@ -652,16 +650,15 @@ Router.prototype.find = function(matchObject) {

/**
* Returns first successfully matched route
* @param {Object|String} matchObject
* @returns {Array|null}
* @returns {[ Route, Object ]|null}
*/
Router.prototype.findFirst = function(matchObject) {
Router.prototype.findFirst = function() {
var parsed,
i, size,
routes = this._routes;

for (i = 0, size = routes.length; i < size; ++i) {
parsed = routes[i].match(matchObject);
parsed = routes[i].match.apply(routes[i], arguments);
if (parsed !== null) {
return [ routes[i], parsed ];
}
Expand Down
Loading

0 comments on commit 40b04c8

Please sign in to comment.