Ringo, Narwhal, Node, and JavaScriptCore support executing scripts with a shebang.

Here are some quick examples to get you started.

Ringo

    #!/usr/bin/env ringo
    print("ringo");

Narwhal

    #!/usr/bin/env narwhal
    print("narwhal");

Node

    #!/usr/bin/env node
    require("sys").print("node\n");

JavaScriptCore

    #!/usr/bin/env jsc
    print("jsc");

JavaScriptCore nightly added support for shebangs on 6 December 2010.

Since Node and JavaScriptCore are binaries, you can use the full path instead of env.

Tagged with:
 

JavaScript is used in many different environments, and each environment has its own method for deploying modules. Some allow importing objects into a local namespace, but notably web browsers do not. So, what is the best way to write a module that can be safely deployed across different environments?

Anonymous closure module pattern

My example module will be implemented as an anonymous closure. It will have one public class and a private member.

    (function () {
      var PublicClass = function () {};
      var PrivateMember = new Object();
    })();

I will incrementally add support for each environment by detecting whether that environment is present.

Web browsers

Web browsers do not support importing into a local namespace, so I simply add PublicClass to the global window namespace.

    (function () {
      var PublicClass = function () {};
      var PrivateMember = new Object();

      if (typeof(window) !== 'undefined') {
        // browser
        window.PublicClass = PublicClass;
      }
    })();

Client must first include the module with a script HTML tag.

    <script type="text/javascript" src="sample-module.js"></script>
    <script type="text/javascript">
      var instance = new PublicClass();
    </script>

CommonJS

CommonJS is a pattern implemented by some server-side JavaScript engines such as Node and Narwhal. In this model, a module assigns its public interface to a special exports object.

    (function () {
      var PublicClass = function () {};
      var PrivateMember = new Object();

      if (typeof(window) !== 'undefined') {
        // browser
        window.PublicClass = PublicClass;
      } else if (typeof(exports) !== 'undefined') {
        // commonjs
        exports.PublicClass = PublicClass;
      }
    })();

Clients require the module, which returns the module’s exports.

    var PublicClass = require('./sample-module').PublicClass;
    var instance = new PublicClass();

JavaScriptCore

JavaScriptCore (or “jsc”) does not follow the CommonJS pattern. Instead, when a file is included with the load function, the last value in the file is returned to the caller.

I return an object literal from the anonymous function which simulates the exports object in CommonJS. Since the anonymous function is executed as the last statement in the file, the exports are returned to the client. This case doesn’t rely on any special variable such as window or exports, so I will make it default rather than detecting the JavaScriptCore environment.

    (function () {
      var PublicClass = function () {};
      var PrivateMember = new Object();

      if (typeof(window) !== 'undefined') {
        // browser
        window.PublicClass = PublicClass;
      } else if (typeof(exports) !== 'undefined') {
        // commonjs
        exports.PublicClass = PublicClass;
      } else {
        // jsc (default)
        return {
          "PublicClass": PublicClass
        };
      }
    })();

Clients load the module, which returns the module’s exports.

    var PublicClass = load('sample-module.js').PublicClass;
    var instance = new PublicClass();

Why not use a global variable?

There is another JavaScript module pattern that declares a global variable to contain the module’s public interface, as in this example.

    var PublicClass = (function () {
      var PublicClass = function () {};
      var PrivateMember = new Object();
      return PublicClass;
    })();

This pattern will work perfectly in web browsers and JavaScriptCore, but it also pollutes JavaScriptCore’s global namespace. Since JavaScriptCore supports local imports, global variables should be avoided. Furthermore, this pattern does not support multiple public interfaces, e.g., a single anonymous closure that exports two public classes.

Example: GraphJS

GraphJS is a JavaScript module of mine that uses the module deployment pattern I described. It works with Node, Narwhal, Ringo, JSC, and in web browsers.

 

I use GNU Screen to keep a few terminal windows open over a shared set of screen windows logged into a large number of other machines.

Wrapper screen

I log in to the machine that will host my screens and start the wrapper screen.

  screen -S wrapper -c .screenrc_wrapper

The wrapper screen’s configuration just specifies the interrupt key.

  escape ^Ww

I create a screen window in wrapper for each terminal window I want to use, as many as four. Each terminal window will run one of the wrapper windows.

SSH agent

I use SSH agent forwarding to log in to my screen host, but if you don’t then you might need to run ssh-agent inside the wrapper screen before starting the ssh screen.

  exec ssh-agent bash
  ssh-add

This is necessary for the next step where I ssh into a bunch of other machines. I have already installed my public key on the destination machines.

SSH screen

I start the ssh screen from inside the wrapper screen.

  screen -S ssh -c .screenrc_ssh

The ssh screen’s configuration creates a bunch of windows that ssh into other machines I want to work on. I like to have at least two windows for each machine.

  escape `` 

  screen -t "host 1" ssh host1
  screen -t "host 1" ssh host1

  screen -t "host 2" ssh host2
  screen -t "host 2" ssh host2

  screen -t "host 3" ssh host3
  screen -t "host 3" ssh host3

  select 0

Inside the other wrapper windows, I attach to the running ssh screen.

  screen -x ssh

At the start of the day, I open up a few terminal windows, log in into the screen server, and attach to the running wrapper screen. This is simplified with an alias.

  alias wrapper='screen -x wrapper'

Tagged with: