Tuesday, August 31, 2010

Express.js Flash

‹prev | My Chain | next›

Bleh. I had planned on playing about with mustache.js in express, but then I remembered, I hate writing HTML. I blame Haml. Instead, I will stick with trying to play with sessions in express.

I do not really need sessions in my (fab) game, but I will try to use flash session messages when the player gets logged out after idling too long. First up, in the jade template, I redirect the player to the /idle_timeout resource when the backend signals that the player is no longer welcome in the game:
      |     var goodbye = function() {
| window.location = window.location.protocol + '//' + window.location.host + '/idle_timeout';
| };
I need to define that resource to set a flash message and then redirect back to the main player board:
app.get('/idle_timeout', function(req, res) {
req.flash('info', 'You were logged out because you weren\'t doing anything.');
res.redirect('/board');
});
When I access this resource, however, I get a sad little stacktrace:
TypeError: Cannot read property 'flash' of undefined
at IncomingMessage.flash (/home/cstrom/.node_libraries/.npm/express/1.0.0rc2/package/lib/express/request.js:152:49)
at Object.<anonymous> (/home/cstrom/repos/my_fab_game/game.js:39:7)
at pass (/home/cstrom/.node_libraries/.npm/connect/0.2.4/package/lib/connect/middleware/router.js:65:27)
at Object.router [as handle] (/home/cstrom/.node_libraries/.npm/connect/0.2.4/package/lib/connect/middleware/router.js:80:10)
at next (/home/cstrom/.node_libraries/.npm/connect/0.2.4/package/lib/connect/index.js:264:23)
at /home/cstrom/.node_libraries/.npm/connect/0.2.4/package/lib/connect/middleware/staticProvider.js:87:21
at node.js:764:9
Hrm... That error is not coming from my (fab) game, it is coming from express. Looking at that method, it seems as though the session is not present:
http.IncomingMessage.prototype.flash = function(type, msg){
var msgs = this.session.flash = this.session.flash || {};
// ....
};
I start acking through the express code for session, when I come across an example app for express sessions. Just what I need. It suggests that I need to add two lines to the createServer call:
// Create the Express server
var app = express.createServer(
express.cookieDecoder(),
express.session()

);
I try adding just the express.session() line, but get warnings about needing the cookieDecoder. Sure enough, once I add that in, flash works. Well, at least it is not crashing.

To get this working, I add an #info div to my jade template:
#info= flash.info
...
But I always see 'undefined' in that div. I think that I am populating it correctly as a local, template variable, but I inspect the flash just to be sure:
app.get('/board', function(req, res) {
puts(inspect(req.flash));
res.render('board', {locals: {flash: req.flash}});
});
In the server output, I see:
[INFO] timeout foo!
[INFO] players.drop_player foo
[Function]
Ah! req.flash() is a function, not an attribute. Easy enough:
app.get('/board', function(req, res) {
puts(inspect(req.flash()));
res.render('board', {locals: {flash: req.flash()}});
});
And now I see the flash message in the server output:
[INFO] timeout foo!
[INFO] players.drop_player foo
{ info: [ 'You were logged out because you weren\'t doing anything.' ] }
Except, in the browser, I still see 'undefined' where the info message should be. Am I invoking the render() method wrong or is something else breaking? I will check the latter first. What is the value of the info attribute?
app.get('/board', function(req, res) {
puts(inspect(req.flash()));
var info = req.flash().info;
puts(inspect(info));
res.render('board', {locals: {flash: flash()}});
});
Now, when my player times out:
[INFO] players.update_player_status: foo
[INFO] timeout foo!
[INFO] players.drop_player foo
{ info: [ 'You were logged out because you weren\'t doing anything.' ] }
undefined
Ah, damn! I've got a Heisenberg thing going on here. Looking at the flash in the first place destroyed it. To get around this, I squirrel away the flash immediately:
app.get('/board', function(req, res) {
var flash = req.flash();
puts(inspect(flash));
var info = flash.info;
puts(inspect(info));
res.render('board', {locals: {flash: flash}});
});
Now, I get the info attribute that I have been expecting:
[INFO] timeout foo!
[INFO] players.drop_player foo
{ info: [ 'You were logged out because you weren\'t doing anything.' ] }
[ 'You were logged out because you weren\'t doing anything.' ]
With that, I finally get the flash warning to show up on the page.

One last thing I need to add is a jade conditional so that the #info div is not displayed unless flash.info is present (it displays undefined otherwise). The conditional:
- if (flash.info)
#info= flash.info

Simple enough.

Well there were certainly a few gotchas in there, at least for this beginner, but I finally got sessions working in express. Tomorrow I plan to head back to faye land to see if I can gracefully handle writes to blocked browsers.


Day #212

1 comment:

  1. Thanks for the post, I just went through the exact same confusion and steps.

    ReplyDelete