coffeescript

Immediately Invoked Function Expression in CoffeeScript

CoffeeScript only has function expression, there are no named functions nor function declaration, as almost everything in CoffeeScript is expression.

Anonymous function can be written as just a dash plus an arrow:

1
->

This will be compiled to JavaScript:

1
(function () {});

This anonymous function can be invoked immediately:

1
2
// (->)()
(function () {})();

The immediately invoked function expression (IIFE) syntax is different from the preferred format in JavaScript. But this is more likely due to the syntax and compilation of CoffeeScript.

Here is one useful example to show the syntax:

1
2
3
4
5
6
// (square = (n) -> return n * n)()
var square;
(square = function(n) {
return n * n;
})();

And a few more without compiled Javascript:

1
2
3
(->)()
(square = (n) -> return n * n)()
console.log (square = (n) -> return n * n)(10) # 100

Defining Correct Environment for CoffeeScript in Cron

I have written a bunch of CoffeeScript scripts that I would like to run them regularly. Cron is certainly the right choice in many situation.

The default executable path recognized by cron is limited:

* * * * * echo $PATH > /tmp/log/cron.log

This cron job will recognize the following path:

/usr/bin:/bin

However the default executable path for Node.js is usually at:

$ which node
/usr/local/bin/node

Therefore, if you attempt to run a cron job:

* * * * * node -v > /tmp/log/cron.log 2> /tmp/log/cron.err.log

It will return an error, where the executable is not found:

bin/sh: 1: node: not found

The easiest way to solve it is by specifying the fully qualified path:

* * * * * /usr/local/bin/node -v > /tmp/log/cron.log

To ensure the process is installed, we can check the file existence and permission before launching it:

* * * * * test -x /usr/local/bin/node && /usr/local/bin/node -v

But when working with CoffeeScript, the same method does not working:

* * * * * /usr/local/bin/coffee -v 2> /tmp/log/cron.err.log

Because CoffeeScript binary (coffee) use the following format:

#!/usr/bin/env node

Which does not specify fully qualified path on the shebang line, and will result in:

/usr/bin/env: node: No such file or directory

We can compile CoffeeScript into JavaScript and use fully qualified node executable. But this is not necessary. Since we are using Debian based system, which uses Vixie cron. It allows environment variables to be defined. We can make Cron aware of the custom path in environment variable:

* * * * * PATH=$PATH:/usr/local/bin coffee -v > /tmp/log/cron.log

Now the executable path include the one where both node and coffee commands reside.

We can also move it to its own line. But Cron is not shell, it does not expand the variable, so you have to specify all paths:

# Error: `PATH=$PATH:/usr/local/bin`
PATH=/usr/bin:/bin:/usr/local/bin
* * * * * coffee -v > /tmp/log/cron.log

The good practice is to set environment variables in a script:

1
2
3
4
5
6
#!/usr/bin/env bash
#
# This script is intended to be run as a cron job.
PATH=$PATH:/usr/local/bin
coffee -v

and run the script as a cron job:

* * * * * /path/to/script > /tmp/log/cron.log 2>> /tmp/log/cron.err.log

Written in CoffeeScript, Required in JavaScript

If you are writing in CoffeeScript, then requiring another module written in CoffeeScript works the same as as both scripts are in JavaScript:

1
cm = require 'coffee-module'

But if you are writing in JavaScript, and the dependent module is in CoffeeScript, you have to include CoffeeScript as a dependency with require statement:

1
2
require('coffee-script');
var cm = require('coffee-module');

For better compatibility, source codes written in CoffeeScript should be compiled into JavaScript. But what is the best practice? You don’t want to maintain two languages. When to compile? Here is two options:

  1. Compile before publish the module
  2. Compile after module install

The advantage of the first approach is that there is no dependency on CoffeeScript. The module has been compiled into JavaScript prior to submitting to module registry. However, this approach requires two repositories, one is source code repository, and another one is the published module repository. If you are working on a private project, it is less likely that you will publish your module to an public NPM registry or running your own private one. It is more likely to have a single source code repository. Therefore, the second approach might be better in this situation. However, coffee-script must be added in dependency, or it must be installed globally with coffee command available during preinstall phase. Although this approach is not recommended in npm-scripts, before setting up a private NPM registry, this is the way to go.

Here is the required fields in package.json:

1
2
3
4
5
{
"scripts": {
"preinstall": "coffee --compile --bare --output lib/ src/"
}
}

Preserve command history in Node REPL

Node REPL (Read-Eval-Print-Loop) is a standalone JavaScript interpreter for Node. To invoke it, simply type:

$ node

However, after exit and re-enter the REPL, all the previous commands are lost. This behaves very much different from what we expect from shell.

To maintain a persistent history, we can use rlwrap as suggested in this answer.

Install rlwrap:

$ sudo apt-get install rlwrap

Create an alias:

alias node='env NODE_NO_READLINE=1 rlwrap -s 1000 -S "node> " node'

Persistent history file is saved in:

~/.node_history

However, if by perserving the command history, Node REPL loses familiarity of color output and tab completion. This is a trade-off. To get around this problem, we can roll our own REPL or use an alternative. The best alternative I have found so far is also the one I am already using: CoffeeScript (see 1.6.3 change log). There is a history command built-in:

$ coffee
coffee> .help
.break  Sometimes you get stuck, this gets you out
.clear  Break, and also clear the local context
.exit   Exit the repl
.help   Show repl options
.history        Show command history
.load   Load JS from a file into the REPL session
.save   Save all evaluated commands in this REPL session to a file

CoffeeScript Inline Block Rule Toggling

Temporarily remove CoffeeLint restriction via the inline code:

1
2
3
4
5
6
# coffeelint: disable=max_line_length,no_implicit_parens
#
# A long comment that exceeds 80 chars per line. Lorem ipsum dolor sit amet, consectetur
# adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
#
# coffeelint: enable=max_line_length,no_implicit_parens

CoffeeScript Array of Objects

We frequently find ourselves need to define array of objects. In CoffeeScript we can write in the following way:

1
2
3
4
5
6
7
8
9
10
arr = [
{
name: 'foo'
pass: 'oof'
}
{
name: 'bar'
pass: 'rab'
}
]

Or another format is to insert a comma:

1
2
3
4
5
6
7
arr = [
name: 'foo'
pass: 'oof'
,
name: 'bar'
pass: 'rab'
]

In the spirit of CoffeeScript, we want to avoid using braces as much as possible. Therefore, the second method is preferred.

Share JavaScript Code between Browser (front-end) and Node (back-end)

One of the benefits coding in JavaScript is the ability to share code between
browser (front-end) and Node (back-end) environments.

To share code between two different environment, first need to understand what
are specific to browser and what are specific to Node. Shared code should be
agnostic to both environments.

In browser environment, there is an obvious one: window object. In Node
environment, there are a few objects that are not globally defined in browser:
global, process, module, exports and more, see global objects. All
these variables work, but exports is preferred.

1
console.log('Hi ' + (typeof window !== 'undefined' ? 'browser!' : 'Node!'));

Once environment is identified, then it is just the matter of writing style,
either browser style or Node style.

For example, writing in Node style by passing the exports as the parameter:

1
2
3
4
5
(function (exports) {
var foo = exports.foo = {};
// Node style code goes here.
}(typeof exports === 'undefined' ? this : exports));

By using browser style, here is an example from the annotated source code of
Underscore:

1
2
3
4
5
6
7
8
9
10
11
// Export the Underscore object for Node.js, with backwards-compatibility
// for the old require() API. If we're in the browser, add _ as a global
// object via a string identifier, for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}

To make it easier without concerning the backward compatibility:

1
2
3
4
5
6
7
8
9
10
11
(function () {
var foo = {};
if (typeof exports !== 'undefined') {
exports.foo = foo;
} else {
this.foo = foo;
}
// Browser style code goes here.
}).call(this);

this is the same as window object in the browser environment.

1
2
3
(function (window) {
// Browser style code goes here.
}(this));

One more thing, writing in CoffeeScript is even better, because the compiled
code is already wrapped in closure, fewer indentation is needed.

1
2
3
4
5
6
7
foo = {}
if exports isnt 'undefined'
exports.foo = foo
else
this.foo = foo
# Browser style code goes here.

Compile to JavaScript by CoffeeScript 1.6.3:

1
2
3
4
5
6
7
8
9
10
11
12
(function() {
var foo;
foo = {};
if (exports !== 'undefined') {
exports.foo = foo;
} else {
this.foo = foo;
}
}).call(this);

Everything will be encapsulated inside the foo object.