I write most, if not all, of my client side code in Coffeescript now. While thats fine in Rails, which has a great asset management system, in other projects it can be a bit of a pain remembering to compile the files each time.
Now Coffeescript has its own watch
flag which tells the compiler to watch a directory/file and compile it each time it changes, but sometimes the files are not all in one location and may be spread out through a project (for example each CakePHP plugin having its own CS files)
Enter Guard and Rake. Any Rubyist will be very familiar with the latter of these two gems, and most will have come across the former. I use them to run automated tasks on a project when certain events occur (like the saving of a file)
First off I’m going to assume you have Ruby installed, but if not check out RVM
or rbenv
, which are both great ways to get Ruby installed and manage different versions of the language.
Also, I’m assuming you have the Coffeescript compiler installed, which is beyond the scope of this post, but you can find out about it over at coffeescript.org
Next you’ll need three gems
$ gem install guard
$ gem install rake
$ gem install guard-rake
It’s very likely that rake
will already be installed, but I’ve added it here for completeness.
Rake
You will need a Rakefile in your CakePHP app/ directory, mine contains a lot of tasks I use in my CakePHP workflow, but I’ve extracted the Coffeescript task below. I’ll touch on the other things I use Rake for in other posts
require 'fileutils'
task default: :coffee
desc "Compile the CoffeeScript files into JavaScript"
task :coffee do
coffee_path = 'coffee'
sh "#{coffee_path} -o webroot/js/ -c Assets/js/coffee/"
FileList.new('Plugin/*/').each do |plugin|
if File.directory? "#{plugin}Assets/coffee"
sh "#{coffee_path} -o #{plugin}webroot/js/ -c #{plugin}Assets/coffee/"
end
end
end
This task will allow you to run
$ rake coffee
in your app/ directory to compile all the Coffeescript files in the project.
The way my setup works, is that in my app directory I create an assets folder, which contains Coffeescript files in a coffee directory, something like this
-app
|-Assets
|-coffee
|-Config
|-Console
|-Controller
<-snip->
(I also put my SASS files in here, but thats another post altogether)
The rake task looks for the Assets/coffee dir in the main app path and in all plugins in the Plugin path. It compiles the coffee script files to a Javascript file with the same name in the relative webroot/js directory.
This takes care of the compiling, and now we use Guard to automate it.
Guard
Much like rake, you will need a Guardfile in your app/ directory.
This contains a very simple block of code, which sets up Guard to watch all files in the app/ directory path with a .coffee
extension, and if they change it runs the rake task we setup earlier
guard 'rake', :task => 'coffee' do
watch(%r{.*?coffee$})
end
It basically tells Guard to run the rake
command, passing the task
coffee
when the watched files change.
The watch command takes a regular expression for what type of files to watch.
You can check out more Guard functionality on the Github page
Running Guard
The last thing to do now is run the guard command in your app/ directory and it will run the rake task then start watching for changed to your coffee files.
Use CTRL-D
to stop guard running
Final comments
The use of the Assets directory in my CakePHP workflow comes from an attempt to port an asset pipeline similar to the one in Rails over to CakePHP. I’m working through a post about how to do that and it will be up soon.
I prefer to use my asset pipeline method on new apps, but this form here works really well if you’re adding Coffeescript in to an existing app.
I also hope that it shows off some of the great tools in the Ruby eco-system that can help in any project regardless of the language its written in.
Guard-Coffeescript
It has been pointed out to me, that there is a Guard plugin especially for Coffeescript.
I wasn’t aware of it at the time I built this into my CakePHP workflow, but have recently installed it and will give it a try to see if it simplifies this process (I imagine it will)
Once I have enough experience with it I’ll either amend this post or write a new one about using the plugin with CakePHP