→ Johnson

21 July 2009

Johnson has long been one of my favorite Ruby projects. As the description says, “Johnson wraps JavaScript in a loving Ruby embrace.” In other words Johnson lets you evaluate and manipulate JavaScript with Ruby (thanks to Mozilla’s Spidermonkey engine).

Ruby

A simple example from the README:

require "johnson"

Johnson.evaluate("4 + 4") # => 8
Johnson.evaluate("4 + foo", :foo => 4) # => 8

Why? Well, maybe you want to let people write plugins for your app in JavaScript – thereby locking down and controlling the environment in a way not really possible with Ruby.

Or maybe you want to run your JavaScript tests on the command line before booting up your browser.

Or maybe you want to run a JavaScript REPL.

$ johnson
js> 1 + 1
=> 2
js> var name = 'chris'
=> nil
js> name
=> "chris"

Emacs Lisp

If you’re using Emacs like me, you can setup johnson to run as your JavaScript shell by setting the custom variable javascript-shell-command to “johnson”.

Also you can use it to execute a buffer:

(defun js2-execute-buffer () 
  (interactive)
  (shell-command (concat "johnson " (buffer-file-name))))

Or a single line:

(defun js2-execute-line ()
  (interactive)
  (save-excursion
    (call-process-region (point-at-bol) 
                         (point-at-eol)
                         "johnson"
                         nil
                         (get-buffer-create "*johnson-line*"))
    (with-current-buffer (get-buffer "*johnson-line*")
      (search-backward "\n\n" nil t)
      (replace-match "" nil t)
      (message (buffer-string))
      (kill-buffer nil))))

I bind these functions to ⌘r and ⌘R.

(define-key js2-mode-map (kbd "A-r") 'js2-execute-buffer)
(define-key js2-mode-map (kbd "A-R") 'js2-execute-line)

Python

Python person? Try davisp’s python-spidermonkey or my fork (which does nothing but add a dead simple REPL script).

It’s very similar to Johnson. Except, you know, for Python.

>>> import spidermonkey
>>> rt = spidermonkey.Runtime()
>>> cx = rt.new_context()
>>> cx.execute("var x = 3; x *= 4; x;")
12

Again, the README has it all.