Programming in Ruby makes me happy. It’s a lovable language, with a pleasantly quirky syntax and lots of expressive power.

Programming in JavaScript, on the other hand, frustrates me to no end. JavaScript could be a reasonable language, but it has all sorts of ugly corner cases, and it forces me to roll everything from scratch.

I’ve been trying to make JavaScript a bit more like Ruby. In particular, I want to support Ruby-style metaprogramming in JavaScript. This would make it possible to port over many advanced Ruby libraries.

You can check out the interactive specification, or look at some examples below. If the specification gives you any errors, please post them in the comment thread, and let me know what browser you’re running!

Taking inspiration from Ruby

Ruby libraries often seem a little bit magical. Rails is an excellent example. Assuming we have a database with two tables, employees and projects, we can write:

class Employee < ActiveRecord::Base
  has_many :tasks
end

class Task < ActiveRecord::Base
  belongs_to :employee
end

employee = Employee.find_by_name("Joe Smith");
employee.tasks.each {|task| print task.name }

This is a complete interface to our database! We only need to declare the relationship between employees and tasks, and Ruby automatically declares find\_by\_name, tasks, and dozens of other methods for us.

There are two tricks here:

  1. has\_many and belongs\_to modify our classes at runtime, adding methods as needed.
  2. ActiveRecord looks at our database tables, and notices that we have fields like name. It uses this information to automatically add find_by_name and other methods to our class.

This style of programming is powerful, flexible, and concise. It also has some limitations. There’s no way to type-check this kind of code, so we need to write lots of test cases.

Can we do stuff like this in JavaScript?

Yup! You can grab the necessary code from my Subversion repository:

svn co http://www.randomhacks.net/svn/planetary/trunk/ planetary

The jsr subdirectory of this project contains everything you need to build Ruby-style libraries in JavaScript. Let’s begin with a Ruby-style class declaration:

var Greeter = JSR.Class.extend();
with (Greeter.prototype) {

  def("initialize", function (message) {
    this.message = message || "Hello!";
  });

  def("hello", function () {
    return this.message;
  });
}

Here, Greeter is a class with two methods, initialize and hello. The def function adds a new member function to Greeter at run time, just like the def statement in Ruby.

We can use our new class as follows:

var greeter = new Greeter("Hello, world!");
println(greeter.hello());

We can also subclass Greeter and override our hello method. Note that we can call the original version of hello using applySuper:

var ChattyGreeter = Greeter.extend();
with (ChattyGreeter.prototype) {

  def("hello", function () {
    var before = arguments.callee.applySuper(this, arguments);
    return before + " How are you today?";
  });
}

Getting JavaScript to support applySuper was fairly tricky; I owe many thanks to Joshua Gertzen for explaining how to do it.

Now, we need to write some test cases!

Behavior-driven development

Test-driven development (TDD) is a technique for designing and building software incrementally. First, you begin by writing a test case. Then, you write just enough code to make that test case work. Finally, you repeat the whole process from the beginning.

But many programmers find TDD fairly counter-intuitive. It’s hard to know which tests to write when, and how big each test should be. When Dan North encountered this problem, he argued that programmers found TDD confusing because of bad terminology. He proposed Behavior-driven development (BDD), which basically just replaces “test cases” with “specifications,” and changes the other terminology to match. But this small change has a powerful psychological effect, making it easier to write good test cases.

One popular BDD library is RSpec, which has been catching on in the Ruby community. It provides a concise language for writing specifications:

describe "Array" do
  it "should have a last() method returning the last element" do
    [1,2].last.should == 2
    lambda { [].last }.should raise_error(IndexError)
  end
end

We can do the same thing in JavaScript. Unfortunately, we have to put up with quite a bit of syntactic noise:

spec("An array", JSSpec.Spec, function () {with(this){
  it("should have a last() method returning the last element", function () {
    [1,2].last().shouldEqual(2);
    (function () { [].last(); }).shouldThrow();
  });
}});

The it function works much like def in the previous section.

To see this library in action, check out the interactive specification.

What’s next?

There are several projects which improve JavaScript in various ways. Prototype adds a wealth of standard Ruby features, including each and many other iterator functions. TrimPath includes a partial implementation of ActiveRecord in JavaScript, but it hasn’t been updated in the past two years. Or if you’d prefer a less dynamic approach, haXe offers static type declarations, a full-fledged type inferencer, and a server-side VM.

The biggest problem with the approach described in this article is the syntactic noise. Perhaps a haXe-style syntactic preprocessor would help?

(Thanks to Aubrey Alexander for testing an earlier version of this library with IE 7 and Opera.)