views:

32

answers:

2

I'd like to make a node.js function that, when calls, reads a file, and returns the contents. I'm having difficulty doing this because 'fs' is evented. Thus, my function has to look like this:

function render_this() {
    fs.readFile('sourcefile', 'binary', function(e, content) {
        if(e) throw e;
        // I have the content here, but how do I tell people?
    });
    return /* oh no I can't access the contents! */;
};

I know that there might be a way to do this using non-evented IO, but I'd prefer an answer that allows me to wait on evented functions so that I'm not stuck again if I come to a situation where I need to do the same thing, but not with IO. I know that this breaks the "everything is evented" idea, and I don't plan on using it very often. However, sometimes I need a utility function that renders a haml template on the fly or something.

Finally, I know that I can call fs.readFile and cache the results early on, but that won't work because in this situation 'sourcefile' may change on the fly.

A: 
function render_this( cb ) {
    fs.readFile('sourcefile', 'binary', function(e, content) {
        if(e) throw e;
        cb( content );
    });
};


render_this(function( content ) {
  // tell people here
});
Corey Hart
I'd much rather not do this with callbacks. In this case, I'm trying to write a function that renders a haml template. On production servers, it should cache the template at server statup and render it when called, but on development servers it should read the file each call in case it's changed. As such, I'd like a development function that reads the file but pretends that it doesn't. Callbacks change the type signature of my functions, which makes it very difficult to switch to production mode.
Nate
+1  A: 

OK, so you want to make your development version to automatically load and re-render the file each time it changes, right?

You can use fs.watchFile to monitor the file and then re-render the template each time it changed, I suppose you've got some kind of global variable in your which states whether the server is running in dev or production mode:

var fs = require('fs');
var http = require('http');
var DEV_MODE = true;

// Let's encapsulate all the nasty bits!
function cachedRenderer(file, render, refresh) {
    var cachedData = null;
    function cache() {

        fs.readFile(file, function(e, data) {
            if (e) {
                throw e;
            }
            cachedData = render(data);
        });

        // Watch the file if, needed and re-render + cache it whenever it changes
         // you may also move cachedRenderer into a different file and then use a global config option instead of the refresh parameter
        if (refresh) {
            fs.watchFile(file, {'persistent': true, 'interval': 100}, function() {
                cache();
            });
            refresh = false;
        }
    }

    // simple getter
    this.getData = function() {
        return cachedData;
    }

    // initial cache
    cache();
}


var ham = new cachedRenderer('foo.haml',

    // supply your custom render function here
    function(data) {
        return 'RENDER' + data + 'RENDER';
    },
    DEV_MODE
);


// start server
http.createServer(function(req, res) {
    res.writeHead(200);
    res.end(ham.getData());

}).listen(8000);

Create a cachedRenderer and then access it's getData property whenever needed, in case you're in development mod it will automatically re-render the file each time it changes.

Ivo Wetzel