Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Add barista #184

merged 4 commits into from

4 participants


Switched to Barista, added 405 error, fixed a static file bug, updated router template

@mde mde merged commit 03b3aac into geddy:master
mde commented

This is fantastic. :)


Woah awesome!


This patch fixes @kr1zmo's problem above. But it does raise an issue with barista. The barista Router is not fully compatible with the old geddy router. It wasn't caught because the tests weren't updated to reference barista. Apply this patch and you'll see.

diff --git a/test/routers/regexp_router.js b/test/routers/regexp_router.js
index 325bc9e..7ee705d 100644
--- a/test/routers/regexp_router.js
+++ b/test/routers/regexp_router.js
@@ -1,7 +1,7 @@
 // Load the basic Geddy toolkit

-var Router = require('../../lib/routers/regexp_router').RegExpRouter
+var Router = require('barista').Router
   , assert = require('assert')
   , tests;

These compatibility problems seem fairly shallow and can be worked around. It's just a question of how we want to do that. If we don't want to break the geddy api, I suppose we'll have to have a light shim over barista.


Ah, of course, we're just relying Barista's tests, and there's nothing to ensure backward-compat. I guess we need a shim, with our own tests for the specific API calls we care about. I don't have a strong objection to deprecating the old API calls over time either, at a major release point.


Okay, I've done a little bit of cleanup that fixes issues with controller-instantiation and adding routes to the config in an app's router.js. Things should be working as they were before.

A few items of note:

  1. As @kr1zmo noted, controller-names returned by Barista are in snake-case, but Geddy expects them in "constructor" style -- camel-case with initial cap. Since we're in JavaScript land, and variables are never expected to be in snake-case, I think the Geddy way makes more sense inside the app. We'll continue to do things that way in Geddy, and simply rewrite params.controller to be what Geddy expects.

  2. There were no real regressions in the tests, other than the casing-change in the controller names. I'm just going to remove the old router tests, and as Marco notes, when we begin pulling apart the app-loading code in app.js, we should be adding integration tests there.

  3. HEAD requests in Barista looks to be implicitly handled for all GET routes, which is probably a good thing -- but it is a change. The only place I can imagine this might have an impact is in the list of acceptable methods returned in a 405, but I'm not really sure how much I care about that.

  4. Barista is using the NPM 'inflections' module for inflections, and we're using our own version in the 'utilities' lib. We should probably converge on using the NPM module, if it does everything we want, just to make sure we don't run into subtle inconsistencies between the two.


Yeah, we really need some integration tests, but I think the current router tests just muddy the waters. app.js is kind of a nightmare. I've done some work on the init stuff over in the remove-model branch (which is sort of the de facto dev branch right now), but the request handling stuff is really fucking out of control.


I'm finding lots more differences here. What I'd like to do is take a look at the barista tests and the current geddy routes tests with a critical eye. We need to settle on an interface for geddy, and then decide how Barista fits in. Obviously I won't be able to devote time to this for a few weeks. But right now things are pretty broken, even if you're trying to follow the geddy tutorials. I don't know how the rest of you feel, but I recommend rolling this back for now. This is no slight to barista. It's just that we need to rethink this a bit.

I'll leave it up to @mde though.


IIRC, Barista returns the actual controller name un-modified, it simply snake cases (and pluralizes?) the URL portion when generating resources.

i.e. resource('BlogPost') will map to the BlogPost controller, but will generate urls in the form /blog_posts/:id

HEAD is an alias for GET, but HEAD is the method passed in with the params. As far as I can tell, this seems to be the desired behavior since HEAD requests are analogous in every way except that we don't send actual body content. I could be wrong though, it's happened at least once before ;-)

Are there any other inconsistencies? I'm all for fixing Barista if it's a router issue!


I totally agree that having a solid API is necessary.

Not sure how the API is different now than it was before, though I have added a few features like nested routes & resources. Do you happen to remember any of the differences off-hand?

Maybe I should start adding features to a 0.1.x branch, then stabilize them in a 0.2.x branch with bug fixes. That way Geddy can safely include 0.0.x, then update to the 0.2.x line when it's ready.

re: validating params, do you mean this? It's been there (and un-touched) since the early days. Maybe it got dropped from the Geddy docs?

I really do have to finish updating the docs for Barista, especially the sections on new features. It's open in a text editor as I write, but I've been on vacation this week. Also: I'm a little lazy when it comes to docs. I'll slap my hands when I get back next week & push to gh.


@kieran Right, it just returns the thing unmodified. I want to depend on using it as the constructor name, so I'm just rewriting params.controller to make sure it's constructor-correct. This was the only real incompatibility as far as I see, and did actually affect the normal workflow of creating resource-routes -- the "frang_zooby" resource should yield a "FrangZoobies" controller.

(There was also a small issue with the changes to the router template, because the ugly code that has to open it up and add the routes depends on the "module.exports" line to be at the bottom, but that was a simple fix.)

I seriously doubt anyone was using any of the more advanced features of the router yet, especially since none of them were documented or publicized. We do need to put our big-boy pants on, and protect backward-compat more going forward, but I genuinely don't think this will be a major issue at this point. I saw we rip this bandaid off.

Here's what docs we have:

@kieran, I guess you wrote a lot of that. Would it be possible to make sure it's up to date with current reality? Of course, a link pointing to the (soon to be, cough) copious docs for Barista would be awesome for people want to learn the fancier features.

@polotek has a really valid point about interfaces. What do people think about specifying an exact version-number of Barista in package.json? Or do we basically expect a guarantee from @kieran that APIs never change within a major version?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 48 additions and 19 deletions.
  1. +31 −7 lib/app.js
  2. +2 −1  package.json
  3. +15 −11 templates/base/router.js
38 lib/app.js
@@ -30,7 +30,7 @@ var fs = require('fs')
, helpers = require('./template/helpers')
, actionHelpers = require('./template/helpers/action')
, FunctionRouter = require('./routers/function_router').FunctionRouter
- , RegExpRouter = require('./routers/regexp_router').RegExpRouter
+ , RegExpRouter = require('barista').Router
, BaseController = require('./base_controller').BaseController
, sessions = require('./sessions')
, CookieCollection = require('./cookies').CookieCollection
@@ -621,7 +621,7 @@ var App = function () {
- // Either static or 404
+ // Either static, 404, or 405
else {
// Get the path to the file
staticPath = geddy.config.staticFilePath + reqUrl;
@@ -630,15 +630,39 @@ var App = function () {
// Decode path (e.g. %20)
staticPath = decodeURIComponent(staticPath);
- if (utils.file.existsSync(staticPath)) {
+ if ( utils.file.existsSync(staticPath) && fs.statSync(staticPath).isFile() ) {
staticResp = new response.Response(resp);
else {
- err = new errors.NotFoundError(reqUrl + ' not found.');
- errResp = new response.Response(resp);
- errResp.send(err.message, err.statusCode,
- {'Content-Type': 'text/html'});
+ // 405?
+ var non_method_routes = router.all(reqUrl);
+ if ( non_method_routes.length > 0 ){
+ // build a unique list of acceptable methods for this resource
+ var acceptable_methods = {};
+ acceptable_methods[params.method] = true;
+ });
+ acceptable_methods = Object.keys(acceptable_methods);
+ // send a friendly error response
+ err = new errors.MethodNotAllowedError(
+ method + ' method not allowed. Please consider ' +
+ acceptable_methods.join(', ').replace(/,\s(\w+)$/," or $1") +
+ ' instead.');
+ errResp = new response.Response(resp);
+ errResp.send( err.message, err.statusCode, {'Content-Type': 'text/html'} );
+ // 404
+ }else{
+ err = new errors.NotFoundError(reqUrl + ' not found.');
+ errResp = new response.Response(resp);
+ errResp.send(err.message, err.statusCode,
+ {'Content-Type': 'text/html'});
+ }
3  package.json
@@ -11,7 +11,8 @@
"author": "Matthew Eernisse <> (",
"dependencies": {
"jake": "0.3.x",
- "utilities": "0.0.x"
+ "utilities": "0.0.x",
+ "barista": "0.0.x"
"bin": {
"geddy": "./bin/cli.js"
26 templates/base/router.js
@@ -17,20 +17,24 @@
-var router = new geddy.RegExpRouter();
-router.match('/').to({controller: 'Main', action: 'index'});
+var router = exports.router = new geddy.RegExpRouter();
// Basic routes
-// router.match('/moving/pictures/:id').to(
-// {controller: 'Moving', action: 'pictures'});
-// router.match('/farewells/:farewelltype/kings/:kingid').to(
-// {controller: 'Farewells', action: 'kings'});
+// router.match('/moving/pictures/:id', 'GET').to('');
+// router.match('/farewells/:farewelltype/kings/:kingid', 'GET').to('Farewells.kings');
// Can also match specific HTTP methods only
-// router.match('/xandadu', 'get').to(
-// {controller: 'Xandadu', action: 'specialHandler'});
+// router.get('/xandadu').to('Xanadu.specialHandler');
+// router.del('/xandadu/:id').to('Xanadu.killItWithFire');
// Resource-based routes
// router.resource('hemispheres');
-exports.router = router;
+// Nested Resource-based routes
+// router.resource('hemispheres', function(){
+// this.resource('countries');
+// this.get('/print(.:format)').to('Hemispheres.print');
+// });
Something went wrong with that request. Please try again.