I’m pretty excited about CoffeeScript as a clean-syntax replacement for pure JavaScript.
What is CoffeeScript?
Imagine all the syntactical delights of Ruby and Haml for your JavaScript. You write in a nice language, but get normal JavaScript at runtime. All whilst having full access to 3rd-party JavaScript libraries (jQuery, PrototypeJS), debugging support (it becomes pure, readable JavaScript), existing support from test suites (it’s normal JavaScript) and growing support from various text editors (TextMate, Vim, Emacs).
What simple delights?
No trailing semi-colons. No { some_code() }
function/closure brackets. String interpolation. Multi-line strings. Explicit class
syntax. Array slicing. An existential ? operator.
Scroll down the home page for awesome example after example.
These aren’t library extensions. This is clean, purposeful syntax.
You can play with the joyful syntax of CoffeeScript on the website. After reading the basic examples on the CoffeeScript home page, press “TRY COFFEESCRIPT” in the header menu.
As you play with the syntax, the equivalent JavaScript is printed on the right hand side (see image above).
How nice is that syntax? Very.
Installing CoffeeScript
- Install NodeJS
- Install CoffeeScript
For NodeJS (get latest release URL; using 0.1.31 as 0.1.32 doesn’t unpack for me):
cd /usr/local/src
wget http://nodejs.org/dist/node-v0.1.31.tar.gz
tar xfv node-v0.1.31.tar.gz
cd node-v0.1.31
./configure
make
sudo make install
For CoffeeScript (get latest release URL):
cd /usr/local/src
wget http://github.com/jashkenas/coffee-script/tarball/0.5.5
tar xfv jashkenas-coffee-script-bcf7b3f.tar.gz
cd jashkenas-coffee-script-bcf7b3f
sudo bin/cake install
Now test that everything is in place:
$ coffee --version
CoffeeScript version 0.5.5
$ coffee -e "sys: require 'sys'; sys.puts 'hello world\n'"
hello world
Phew!
Note, in the command-line/on the server, you are using the NodeJS JavaScript environment. It supports the CommonJS API for loading modules (normal JavaScript: var sys = require('sys')
).
Um, but how do I use it in my web app?
Your application source code will have *.coffee
files containing your sexy, short CoffeeScript. But at runtime, the browser needs the generated JavaScript.
I’ve been using the Jonas Nicklas’ bistro_car gem:
gem install bistro_car
mkdir -p app/scripts
In your Rails config/environment.rb
file, add:
config.gem 'bistro_car'
And in your layouts, such as app/views/layouts/application.html.erb
add to the <head>
or the bottom:
<%= coffee_script_bundle %>
Now you’re good to go. Add your CoffeeScript files in app/scripts/*.coffee
and they will be automatically available as JavaScript.
WARNING: Check your version of CoffeeScript
Check that this hasn’t happened:
$ coffee --version
CoffeeScript version 0.3.2
$ which coffee
/usr/bin/coffee
Arrgh, we should be using /usr/local/bin/coffee
. bistro_car
currently installs the old rubygem-based version of coffee-script; and you might be unlucky to have your $PATH find the wrong one first.
Either delete it (sudo rm /usr/bin/coffee
and restart your shell) or make sure /usr/local/bin
is earlier in your $PATH
than /usr/bin
, where RubyGems installed the old, unnecessary version of coffee
command.
Let’s drink the CoffeeScript
Create a file app/scripts/application.coffee
with contents:
powers: [1,2,3,4].map (i) -> i * i
alert powers
Load up a view in a browser and see [1,4,9,16]
. You win! Throw in some jQuery/PrototypeJS/whatever. Beautiful.
View the source of the page, navigate to public/javascripts/bundle/default.js
and you’ll see the generated source:
(function(){
var powers;
powers = [1, 2, 3, 4].map(function(i) {
return i * i;
});
alert(powers);
})();
The problem: Heroku doesn’t have CoffeeScript installed
Heroku is a great place to host apps. Though it doesn’t have CoffeeScript installed so it cannot dynamically convert the *.coffee
files into JavaScript.
If you want to use Heroku I guess we need to perform the conversion locally and deploy it.
But. In development and integration testing I want bistro_car’s dynamically generated default.js
. In production, I need a cached version.
In application.html.haml
I use (I can’t keep pretending I use erb):
- if Rails.env.production?
= javascript_include_tag "coffeescripts"
- else
= coffee_script_bundle
Now we’re just left with the hassle of automatically generating public/javascripts/coffeescripts.js
.
First, a rake task. Second, a git pre-commit hook.
Create lib/tasks/bistro_car.rake
:
desc "Generate the cached bundle/default.js file from app/scripts/*.coffee files"
task :bistro_car => :environment do
path = "public/javascripts/coffeescripts.js"
puts "Building *.coffee -> #{path}"
File.open(path, "w") { |file| file << BistroCar::Bundle.new('default').to_javascript }
end
file "public/javascripts/coffeescripts.js" => Dir[File.join(Rails.root, 'app/scripts/*.coffee')] do |t|
Rake::Task["bistro_car"].invoke
end
Now you can create coffeescripts.js
and add it to the repo with:
rake public/javascripts/coffeescripts.js
git add public/javascripts/coffeescripts.js
git commit -m "Initial bundled coffeescripts file"
Now create .git/hooks/pre-commit
:
#!/bin/sh
exec rake public/javascripts/coffeescripts.js
And make it executable (and git commit
will invoke it automatically):
chmod +x .git/hooks/pre-commit
Phew.
Now, whenever you change a *.coffee script and you are about to commit it, the cached-production-only coffeescripts.js
is automatically updated and included in the same commit.
Seems like a clean hack.
Summary
Why not make a library to do this? Well I’m hoping there is a better, cleaner way. Perhaps bistro_car
can include a rails generator to package these bits and pieces itself, if my approach happens to be the best way.
Nonetheless, let history record that CoffeeScript is very cool though in the world of Heroku living with it is non-trivial at the moment.
Related posts:
- Instant new Rails applications with the App Scrolls When I start a new project I want to start...
- Validate and Save your Ruby in TextMate – with secret Rubinus superpowers In some TextMate bundles, if you save a file it...
- Showcase of CoffeeScript – 2.5 mins for your next Dev Group meeting If you are giving an “Introduction to CoffeeScript” talk...