CoffeeScript is great. I’m not going to spend any time backing that statement up in this article, but you are free to stop reading if it’s not your cup of tea.
My main gripe with CoffeeScript, however, is a small impedance mismatch with the Ember.js object model. Creating classes using the
new operator in coffeescript does not translate well to the Ember convention of creating object’s via the
Specifically, consider the following CoffeeScript:
1 2 3
Running this through the CoffeeScript compiler generates something along the lines of:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
When dealing with Ember.js, however, it would be desirable to output the following:
1 2 3 4
Fortunately, I have been following the CoffeeScript Redux Project by Michael Ficarra closely and am excited by its potential. From its own description, it aims to be a “rewrite of the CoffeeScript compiler with proper compiler design principles and a focus on robustness and extensibility.” Although not complete, extensibility is exactly what we need here and it is far enough along for a proof of concept.
In this post I am going to detail exactly that: how one would go about creating a modified, Ember-specific version of the CS compiler which conforms to the Ember object model.
In order to make this modification, it is important to grok the structure of the CSR compiler. Below are the steps the
coffee binary goes through during compilation:
- Preprocessing - Perform lexical analysis and convert the input into tokens.
- Parsing - Construct the CoffeeScript abstract syntax tree (AST). This is written using PEG.js.
- Optimising - Perform optimizations and simplify the AST.
- Code generation - Output JS, this uses escodegen.
Modifying the Code
After checking out the code from the GitHub repo, let’s try and test out the existing behavior. Create a file called
test.coffee in the root of the repository and populate it with the example at the top of this post. Then run:
For the purposes of this blog post, there are three main changes that need to be made:
- Base class definitions should use
- Subclasses should call
extendon the base class.
- Class instantiation should use
The code for the compilation phase is predictably located in
src/js-nodes.coffee. For the first two changes above, the pertinent code is located in
compiler.coffee near the code:
1 2 3 4 5 6
As CoffeeScript supports executable class bodies, bound methods, etc., the logic inside here is non-trivial. However, looking towards the bottom of the method, we see that it returns an expression consisting of:
This represents the outer function of a CoffeeScript class definition. For ember, we want to change this to a call to
Ember.Object.extend. The following code does exactly that:
We also need to comment out the existing inheritance logic and change the target of the create expression when the class definition is extending another class. I am not going to include the code here, but feel free to check out the repo which contains these changes.
new to use
create, the change is rather trivial. Simply change:
This changes the CoffeeScript new expression from outputting a JS new expression to outputting a JS call expression. If you now re-run the command you should see the following output:
1 2 3 4 5 6 7 8
That is all their is to it! The compiler now outputs class definitions using the Ember object-model. You can view the working code on github.
As a disclaimer, this simple prototype is an experiment and will not provide much real-world value. It is only the beginning of an Ember-infused CoffeeScript fork. A more sophisticated version would take into account class bodies, mixins, bindings, getters/setters and other Ember goodness, far beyond the scope of this post. But stay tuned :).
1. Technically, Ember classes actually are compatible with the new operator, but there is no way to pass in a hash parameter as the class body for initialization.