2

Following the advice here I can get a shell script in JavaScript that runs under node.js and prints a bit of hello world html:

test.cgi
-------
#!/usr/local/bin/node

console.log("Content-Type: text/html; charset=utf-8\n");
console.log("<html><body><h1>hello node.js world</h1></body></html>");
-------

And running it:

$ ./test.cgi
Content-Type: text/html; charset=utf-8

<html><body><h1>hello node.js world</h1></body></html>

It also works as expected in Apache and displays the expected HTML in the browser.

Now on to CoffeeScript (note the lovely triple-quoted here docs, Python-style):

ctest.cgi
-------
#!/usr/bin/env coffee

console.log("Content-Type: text/html; charset=utf-8\n");
console.log('''<html><body><h1>hello CoffeeScript world
</h1></body></html>''');
-------

This works when run locally:

$ ./ctest.cgi
Content-Type: text/html; charset=utf-8

<html><body><h1>hello CoffeeScript world
</h1></body></html>

But not in Apache:

Internal Server Error

Why doesn't this work?

Community
  • 1
  • 1
Jared Updike
  • 6,806
  • 7
  • 42
  • 67

5 Answers5

3

I attempted to fix this by reverse engineering command.js from CoffeeScript and running my coffee script (ctest.cgi above) directly, via the following JavaScript node.js shell script:

drip
-------
#!/usr/local/bin/node

var fs = require('fs');
var cs = require('/usr/local/lib/coffee-script/lib/coffee-script');
var args = process.argv;
var fname = args[2];
if (fname)
{
    fs.readFile(fname, function (err, data) {
      if (err) throw err;
      var text = data.toString(); //console.log(data.toString());
      cs.run(text, {'filename':fname, 'bare':undefined});
    });
}
-------

I put this in /usr/local/bin/drip and now I can run ctest.cgi with a small change to the top line:

#!/usr/bin/env /usr/local/bin/drip

Now I can hack CoffeeScript CGI scripts and simply hit reload in my browser instead of having to manually invoke the CoffeeScript compiler when I change the .coffee files.

Jared Updike
  • 6,806
  • 7
  • 42
  • 67
  • 1
    This is probably the right direction if you're going to use CoffeeScript for CGI in a performance-critical environment, since if you run the script each time with `coffee`, you're incurring the overhead of CoffeeScript compilation every time. Instead, you might want to extend the script above so that it compiles to a .js file if either no such file exists or the timestamp on the .coffee file is more recent, then runs the .js file. – Trevor Burnham Mar 13 '11 at 15:42
  • @Trevor: Compilation is a really great idea. Also, I just saw that I could print some a basic http header in a catch block to make exception traces print in the browser instead of having to tail /var/log/apache2/error_log when I have syntax errors in my scripts. BTW my approach isn't so much performance critical but rather I wanted a chance to try out CoffeeScript as a drop in replacement for my Python CGI scripts as part of a plan to entirely replace Python with offline JS running in the browser -- since "CoffeeScript IS JavaScript" -- just pretty as Python. – Jared Updike Mar 13 '11 at 16:09
  • Actually if I add the compilation approach, it may help solve another problem, which is: allowing coffee CGI scripts to 'require' other .coffee files in the same folder (I would add the same logic to re-compile required .js if .coffee timestamp didn't match). If I get something useful working, I will post it to github. – Jared Updike Mar 13 '11 at 16:25
2

You probably already thought of this, but: Have you tried replacing

#!/usr/bin/env coffee

with an absolute reference to wherever coffee is located? env relies on the PATH environment variable, and your PATH when you run ./ctest.cgi isn't necessarily the same as Apache's.

Trevor Burnham
  • 74,631
  • 30
  • 153
  • 193
  • I tried that but the /usr/local/bin/coffee script uses the same sort of "/usr/bin/env node" bit which is defined for my user but not in the user context in which Apache runs. Now I just tried changing coffee's first line to an absolute reference to node but the path hacks in the coffee script fail to link up to the correct location in /usr/local/lib/coffee-script/lib so even more modifications to the coffee script would be required, which is the essence of my drip script above. – Jared Updike Mar 13 '11 at 16:04
  • Ahh, I see. Well, I'm no Apache expert, but have you tried using [mod_env](http://httpd.apache.org/docs/2.0/mod/mod_env.html) to set the `PATH` variable so that it points to both `coffee` and `node`? – Trevor Burnham Mar 13 '11 at 17:18
1

Here is my setup, for anybody that's interested.

It is very bad for performance!

~/coffee
------
#!/usr/bin/perl 

# this is coffee runner!

print ` PATH="\$PATH:~/www/cgi-bin/bin" ; ~/www/cgi-bin/bin/node_modules/coffee-script/bin/coffee $ARGV[0] 2>&1 `;
------

I don't have the necessaries to alter my server environment, so I get to add my node paths here. However, I can set up a handler in .htaccess:

~/dir/.htaccess
------
AddHandler cgi-script .litcoffee
DirectoryIndex cv.litcoffee
------

This means I can run literate as CGI and serve up coffee for the browser :-) Very inefficient, but no many people are coming to my website anyway.

Then each of my scripts looks something like this...

~/web/ascript.litcoffee
------
#!/usr/bin/perl /home/jimi/coffee

This is literate coffeescript!

    module.paths.push "/home/jimi/www/cgi-bin/bin/node_modules"
    require "coffee-script"

This is a wee module I wrote for spewing tags, with content and attributes

    global[k.toUpperCase()] = v for k,v of require './html.litcoffee'

It also provides a header function, but I'm going to move that to a CGI module when I get around to it.

    console.log CGI_HEADER()

Now we can put something to the browser.

    console.log HTML [
        HEAD [
            META {charset:"utf-8"}
            SCRIPT [],
                src : "https://raw.github.com/jashkenas/coffee-script/master/extras/coffee-script.js"
            SCRIPT [],
                src : "runsonclient.coffee"
                type    : "text/coffeescript"
            LINK
                rel : "stylesheet"
                href    : "mystyles.css"
            TITLE "A page title"
        ]
        BODY [
            H1 "a page title"
            INPUT 
                id     : "myinput"
                type   : "text"
            SVG
                id     : "mysvg"
                width  : "80%"
                height : "20"
            DIV
                id     : "mydiv"
        ]
    ]
------

I know it's not pretty, but it works. And running from a script (although admittedly it needn't be perl!) allows 2>&1 so all my errors get to the screen, unless my header isn't printed.... but Jared Updike already solved that with a try block.

0

I tried to update my answer, but it got rejected and some advice said to add this as a separate answer...

I just updated that to a shell script with compile if newer functionality:

#!/bin/sh
CS=$1        # the coffeescript to be run
# the following works because (my) apache runs in the script's directory and passes just the filename (no path)
# if would be safer to test that this is the case, or make sure the dot is added to the filename part
JS=.$CS.cjs  # the compiled javascript
CE=.$CS.cerr # compiler errors
PATH="$PATH:/home/jimi/www/cgi-bin/bin:/home/jimi/www/cgi-bin/bin/node_modules/coffee-script/bin"
if [ ! -f $JS ] || [ $CS -nt $JS ] ; then
    coffee -c -p $1>$JS 2>$CE
fi
node $JS 2>&1   # runtime errors to browser
0

I have no idea why coffee fails, but a possible (and very simple) solution is to put your code in a separate file (test.coffee) and do a require:

#!/usr/local/bin/node

require('coffee-script')
require('./test')

(after requiring coffee-script the extension is automatically registered)

Ricardo Tomasi
  • 31,690
  • 2
  • 52
  • 65
  • I tested this (pasted this into rtest.cgi) and it works wonderfully from the command line under my user, but it fails when loaded as a CGI script under Apache (other .cgi scripts in the same folder work fine). The error log shows an exception: Cannot find module 'coffee-script'. Overall this seems like the right/best approach if we can get node to know about coffee-script through some other means than env variables, or by getting the env vars right. – Jared Updike Mar 14 '11 at 14:54
  • 1
    modules are installed to the users' path (~/.node_modules). try setting the NODE_PATH environment var. – Ricardo Tomasi Mar 15 '11 at 06:53