In Rails 3.1, CoffeeScript was introduced as the default language for client side interactions (and with vociferous discussion from all sides). I was very excited by the choice made by the Rails team and have been gnashing at the teeth to get my hands on a project with Rails and CoffeeScript working in gorgeous harmony. What I found was that for production code Rails and CoffeeScript work together swimmingly, but the development story is, at best, incomplete.
Writing a feature in CoffeeScript
A thought experiment: let's walk through the development of a feature. Let us try GitHub's "Command-T" functionality to search for files. The feature calls for the following functionality:
- When a user presses 't' in her repository view, show the repository search view.
- In the repository search view, the user is allowed to search for files by just typing in any part of the desired filename.
- After a short delay, the search is executed and the results are displayed.
- The user can then use the up and down arrow keys to browse the results.
- When the user hits enter/return, she is then taken to the selected result's page.
How might we do this in Rails? Well, the first requirement is easy enough; it is a one liner in CoffeeScript. We could even add a Cucumber on Selenium test that verifies the hiding and showing of the correct panels.
The next part starts to get complicated, though. We need to be able to capture all of the user's keyboard interactions, decide if it is alphanumeric, save the key somewhere and then display it. Depending on how naive we are implementing this, we are talking about a significant function with many branches. We could probably get through the pain of writing Cucumber on Selenium tests for this particular part but we can already see that this is not going to be sustainable. If just one part of this breaks, then our Selenium tests are only going to be able to tell us as much as a user could: "It's broken. I don't know why but it just breaks!"
A Better Solution
# spec/user_spec.coffee describe "User", -> spec -> user = new User() user.setName "Bob" user.name.should equal "Bob"
Again, this is ridiculously simple and what I've done is a proof of concept. I plan to do some more work on this specific solution but I'm hoping that this is just an inspiration for more work in making the CoffeeScript inside of Rails story much, much better.
Not the Best Solution
My solution above has several flaws that I'd like to point out (and if someone has a resolution for it, please let me know!).
- CSpec is very, very incomplete. This could rapidly change but please be aware I'm not necessarily advocating this for real use today.
- CSpec requires NodeJS to be installed. This is yet another dependency that a developer must install in order to get up and running. Looking at other solutions like therubyracer is something that I would like to do.
- CSpec requires that you "publish" your classes via the exports module, if it exists. It's a small but hacky fix to get at classes defined in other files.
- CSpec does not have a browser. That means that JQuery is not supported. You'll have to decouple your logic from JQuery (a good thing) but you will still need to do JQuery testing through Selenium and/or Jasmine.
- CSpec is not well integrated with Rails. This is something that could be quickly resolved with the appropriate generators and other railties in a cspec-rails gem.
Again, while I do hope to improve upon my solution what I really hope is that the Rails community can improve upon the development flow of CoffeeScript. I think we can and should encourage a close relationship between Rails and CoffeeScript and that we should have the same high standards for both.
Discuss this post at Hacker News