GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS

Written by thoughtbot

CoffeeScript: Spartan JavaScript

I’ve been writing some CoffeeScript lately, and it’s been fantastic. I wish I had started looking into this language sooner. Here’s some thoughts about it.

the good

Writing CoffeeScript instead of JavaScript feels like I’m fighting a whole new battle. JavaScript is not a verbose language. Writing JavaScript properly, and in an OO manner, requires you to be verbose. What I like best is that CoffeeScript is simply…spartan.

You bring only this snippet of code to battle?

You! What is your keyword?

It’s function, sir.

And you?

I’m _.bindAll, sir.

CoffeeScript! What are your keywords?

-> ! => ! -> !

See old friend, I brought better code than you did.

functions & binding

I’m most excited about how accessible functions have become in CoffeeScript, and how the worry of doing _.bindAll to keep the current object in this just vanishes. Even our old friend $(document).ready loses a few pounds of syntax weight:

$ ->
  new ItemsRouter()
  Backbone.history.start()

It’s ridiculously neat to me that it just works. If you’re doing a loop or something in a Backbone.View to render many things, you can use the “fat arrow” to make sure this stays as the current object.

class ItemView extends Backbone.View
  render: ->
    @options.items.each(@renderItem)
  renderItem: (item) =>
    @el.append item.get("title")

No more worrying about _.bindAll! Oh yeah, using @ for instance methods (it substitutes this.) is a great little cupcake of syntax sugar that makes code so much easier to read.

hashes/objects

We know the Ruby 1.8 syntax for hashes is dead, but CS takes this one step further. In this example, Item is a Backbone.Model:

item = new Item title: "Awesome", id: 2
Omitting parentheses is something I’ve been trying with this transition. My philosophy here is to push it to the limit and see how it feels. We’ll see if it passes [code review](http://playbook.thoughtbot.com/working-in-feedback-loops/feature-branch-code-reviews/) in the next few weeks. #### local variables By default, JavaScript creates all variables in the global scope. How [useful](https://twitter.com/#!/hgimenez/status/103574840019140608). By default, CoffeeScript makes local variables by default and makes sure to stick `var` in front of declarations.
test = ->
  foo = "bar"
  console.log(foo)
>> test()
"bar"
>> foo
ReferenceError

This is reason enough to use CoffeeScript. I think anyone writing JavaScript has to pay such close attention to scope that it’s simply just a waste of time.

string interpolation

How many times have you written something like:

var html = "";
This is such nonsense. Why did we put up with this? In CoffeeScript, you can write:
html = ""
By the way, the latest version of Backbone has a [make](http://documentcloud.github.com/backbone/#View-make) method that helps doing little element creation like this. #### inheritance The [official docs](http://jashkenas.github.com/coffee-script/#classes) on the subject put it best:
JavaScript’s prototypal inheritance has always been a bit of a brain-bender
So far I’ve used inheritance only in context with subclassing Backbone’s core classes, but I like what I’m seeing so far with the `class` and `extends` keywords.
class Item extends Backbone.Model
  url: ->
    "/events/#{@get('event_id')/items/#{@id}"

The bad

Debugging

This has been a major concern of mine since we’ve switched. Luckily it hasn’t been too bad yet, the JavaScript that is generated is actually readable. Of course, there’s the problem of there’s not a one-to-one mapping between the JS and CS, but we manage to get by with SASS/CSS just fine.

I have a feeling this will be an issue going forward, at least until Chrome or Firefox can understand CoffeeScript natively.

Silly keywords

“Silly” is probably the only way I can describe this:

true, yes, on  true
false, no, off  false

Not sure why we couldn’t just stick with true and false. There are always quirky parts to every language, but if I see @item.has("title") == yes in a pull request I’ll be sending a storm of commit comments.

Deploying

I’ve been using Barista to integrate with Rails 3.0 which has worked out quite well.

Our current config/initializers/barista_config.rb looks like:

if Rails.env.development? || Rails.env.test?
  Barista.configure do |c|
    c.root = Rails.root.join("app", "assets")
    c.output_root = Rails.root.join("public", "coffeescripts")
    c.bare = true
    c.add_preamble = false
  end
end

This tells Barista to look for coffeescripts in app/assets, which hopefully will make the move to Rails 3.1 a little easier. We’re dumping them into public/coffeescripts, which is is ignored by git so we don’t check in the generated files. Finally, there’s some extra unnecessary noise that Barista adds so the two final configuration options turns that off.

We’re also using Kumade to deploy to Heroku’s bamboo stack. I’m really hoping this entire situation gets easier in the next few months, because right now it’s a real annoyance to set up all of this.

the end

Hopefully this gives you a good overview of the nice additions that CoffeeScript provides. We talked internally about using CoffeeScript for months before integrating it. Part of me wishes we had done it sooner, but to really enjoy it fully you have to understand the problems JavaScript has, especially in a large codebase.

Huge thanks go out to the folks at DocumentCloud and Jeremy Ashkenas for making JavaScript application development less painful. There’s plenty of features I didn’t cover and pages of beautifully documented source code on the project’s site that you should read.