Skip to content

Commit

Permalink
added nested routes
Browse files Browse the repository at this point in the history
added inflection dependency
inflecting keys like resource_id to singular when nested
version bump
  • Loading branch information
kieran committed Sep 6, 2012
1 parent f2004ae commit c1095af
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 28 deletions.
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tests
7 changes: 0 additions & 7 deletions lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@
// return ret;
// }

// snake_case
exports.snakeize = function(str){
return str.replace( /[A-Z]|\d+/g, function(str){
return '_'+str.toLowerCase();
}).replace(/^[^a-zA-Z0-9]|[^a-zA-Z0-9]$/,'');
}

// deep object mixer
exports.mixin = function(){
var args = Array.prototype.slice.call(arguments)
Expand Down
23 changes: 14 additions & 9 deletions lib/resource.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,41 @@
var snakeize = require('./helpers').snakeize
, mixin = require('./helpers').mixin
var mixin = require('./helpers').mixin
, inflection = require('inflection')

var Resource = exports.Resource = function( base, controller ){

var controller_slug = this.name = snakeize(controller)
var controller_slug = inflection.underscore(inflection.pluralize(controller))

this.routes = []

// set up the actual routes for the resource
this.routes.push(
this.collection_route =
base.get('/'+controller_slug+'(.:format)', 'GET').to(controller+'.index')
, base.post('/'+controller_slug+'(.:format)', 'POST').to(controller+'.create')
, base.get('/'+controller_slug+'/add(.:format)', 'GET').to(controller+'.add')
, this.member_route =
base.get('/'+controller_slug+'/:id(.:format)', 'GET').to(controller+'.show')
, base.get('/'+controller_slug+'/:id/edit(.:format)', 'GET').to(controller+'.edit')
, base.post('/'+controller_slug+'(.:format)', 'POST').to(controller+'.create')
, base.put('/'+controller_slug+'/:id(.:format)', 'PUT').to(controller+'.update')
, base.del('/'+controller_slug+'/:id(.:format)', 'DELETE').to(controller+'.destroy')
)

this.collection_route.collection = true
this.member_route.member = true
return this
}

Resource.prototype.collection = function(cb){
if ( typeof cb != 'function' ) throw 'resource.collection() requires a callback function'
cb.call( this.collection_route )
this.collection_route.nest(cb)
return this // for chaining
}

Resource.prototype.member = function(cb){
if ( typeof cb != 'function' ) throw 'resource.member() requires a callback function'
cb.call( this.member_route )
this.member_route.nest(cb)
return this // for chaining
}

Resource.prototype.nest = function(cb){
this.member_route.nest(cb)
this.collection_route.nest(cb)
return this // for chaining
}
16 changes: 12 additions & 4 deletions lib/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ var Key = require('./key').Key
, regExpEscape = require('./helpers').regExpEscape
, mixin = require('./helpers').mixin
, kindof = require('./helpers').kindof
, snakeize = require('./helpers').snakeize
, inflection = require('inflection')

// new Route( router, path [, method] )
// =================
Expand Down Expand Up @@ -47,14 +47,15 @@ Route.prototype.match = function( router, path, method, optional ){
var prefix = this.path

// get a list of key names in the new segment
var new_keys = []
var new_keys = (this.collection || this.member) ? [':id'] : [] // force id to be renamed for resources
Array.prototype.push.apply(new_keys, path.match(RegExp(this.KEY.source,'g')) ) // find all

// rename earlier keys
for (var i=0; i<new_keys.length; i++){
var replKey = new RegExp(':('+new_keys[i].substring(1)+'/?)')
prefix = prefix.replace(replKey,":"+snakeize(this.params['controller'])+"_$1")
prefix = prefix.replace(replKey,":"+inflection.underscore(inflection.singularize(this.params['controller']))+"_$1")
}

// return the new awesomeness
return (new Route( router, prefix+path, method ))
}
Expand Down Expand Up @@ -434,4 +435,11 @@ Route.prototype.parse = function( urlParam, method ) {
if ( HEAD ) params.method = 'HEAD'

return params
};
}


Route.prototype.nest = function(cb){
if ( typeof cb != 'function' ) throw 'route.nest() requires a callback function'
cb.call( this )
return this // for chaining
}
6 changes: 5 additions & 1 deletion lib/router.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
var Route = require('./route').Route
, Resource = require('./resource').Resource
, qstring = require('querystring')
// , snakeize = require('./helpers').snakeize


// new Router
Expand Down Expand Up @@ -249,4 +248,9 @@ Router.prototype.defer = function( fn ) {
delete route.stringify //= function(){ throw 'Deferred routes are NOT generatable'};
this.routes.push(route)
return route
}


Router.prototype.list = function(){
console.log(this.routes.map(function(rt){return rt.path}))
}
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
{
"name": "barista",
"description": "URL router & generator, similar to Rails / merb",
"version": "0.0.6",
"version": "0.0.7",
"author": "Kieran Huggins <kieran@kieran.ca>",
"repository": "git://github.com/kieran/barista",
"main": "./index.js",
"engines": { "node": ">= 0.3.0" }
"engines": { "node": ">= 0.4.0" },
"dependencies": {
"inflection": "*"
}
}
67 changes: 62 additions & 5 deletions tests/barista.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -592,8 +592,10 @@ RouterTests = {
this.get('/print').to('Posts.print')
})

, params = router.first('/posts/5/print','GET')
, expectedParams = { method:'GET', controller:'Posts', action:'print', id:5 }
var params = router.first('/posts/5/print','GET')

var expectedParams = { method:'GET', controller:'Posts', action:'print', post_id:5 }


// tests both parsing & generation
assert.equal( router.url( params ), '/posts/5/print', this.fail);
Expand All @@ -619,7 +621,7 @@ RouterTests = {
})

, params = router.first('/posts/5/print.pdf','GET')
, expectedParams = { method:'GET', controller:'Posts', action:'printAll', format: 'pdf', id:5 }
, expectedParams = { method:'GET', controller:'Posts', action:'printAll', format: 'pdf', post_id:5 }

// tests both parsing & generation
assert.equal( router.url( params ), '/posts/5/print.pdf', this.fail);
Expand All @@ -633,7 +635,7 @@ RouterTests = {
})

, params = router.first('/posts/5/comments/3','GET')
, expectedParams = { method:'GET', controller:'Comments', action:'show', posts_id:5, id:3 }
, expectedParams = { method:'GET', controller:'Comments', action:'show', post_id:5, id:3 }

// tests both parsing & generation
assert.equal( router.url( params ), '/posts/5/comments/3', this.fail);
Expand Down Expand Up @@ -671,8 +673,63 @@ RouterTests = {
var route = router.post('/something').to('App.index')
, params = router.first('/something','HEAD')

assert.equal( params, false, this.fail);
assert.equal( params, false, this.fail );
},

'test Nesting: Route -> Route' : function() {
var route = router.post('/something').to('App.index').nest(function(){
this.get('/something_else').to('App.somewhere')
})
, params = router.first('/something/something_else','GET')
, expectedParams = { method:'GET', controller:'App', action:'somewhere' }

assert.equal( router.url( params ), '/something/something_else', this.fail);
assert.equal( router.url( expectedParams ), '/something/something_else', this.fail);
},

'test Nesting: Resource -> Route' : function() {
var route = router.resource('Posts').nest(function(){
this.get('/print(.:format)').to('Posts.print')
})

var url = '/posts/5/print.pdf'
, params = router.first(url,'GET')
, expectedParams = { method:'GET', controller:'Posts', action:'print', format:'pdf', post_id:5 }

assert.equal( router.url( params ), url, this.fail);
assert.equal( router.url( expectedParams ), url, this.fail);
},

'test Nesting: Resource -> Resource' : function() {
var res = router.resource('Posts').nest(function(){
this.resource('Comments')
})

var url = '/posts/5/comments'
, params = router.first(url,'GET')
, expectedParams = { method:'GET', controller:'Comments', action:'index', post_id:'5' }

assert.equal( router.url( params ), url, this.fail);
assert.equal( router.url( expectedParams ), url, this.fail);
},//
//
// 'test Nesting: Resource -> Route -> Resource -> Route' : function() {
// var res = router.resource('Ones').collection(function(){
// this.get('/twos').to('Twos.index').nest(function(){
// this.resource('Threes').collection(function(){
// this.get('/fours').to('Fours.index')
// })
// })
// })
// router.list()
// console.log(router.first('/ones/twos/threes/fours'))
// var url = '/ones/twos/threes/fours'
// , params = router.first(url,'GET')
// , expectedParams = { method:'GET', controller:'Fours', action:'index' }
//
// assert.equal( router.url( params ), url, this.fail);
// assert.equal( router.url( expectedParams ), url, this.fail);
// },
}

function bench(fn){
Expand Down

0 comments on commit c1095af

Please sign in to comment.