You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This compiled fine! I actually (here's where the my-fault part comes in) committed and pushed this code, after verifying that it compiled, but before seeing if it did what I thought it did.
Of course, it didn't. It actually compiles to two statements, an assignment and a unary plus:
This is actually completely correct and desirable behaviour. CoffeeScript can't know whether you wanted the binary or the unary form of + (or -, if I'd used that) so it has to assume I wanted the unary one. Had I been using an operator that doesn't have a unary version, like *, both versions would have given me a compilation error.
Solution
Leave the operator in a binary operation on the first line:
Of course, the real solution is to chop it up into shorter statements:
left=parseInt(@$dialog.find('some-element').css('padding-left'), 10)
right=parseInt(@$dialog.find('.some-element').css('padding-right'), 10)
canvas.width-= left + right
Our intent is to bind to the click and hover handlers of a button which shows a dialog. However, our click handler is being bound inside the hover callback, chained to $(this).css('background', '#foo'):
The worst kind of bug. At first, everything works great. In hover, our click callback is bound to the right element, as this and .dialog-button are the same element. When we click our dialog appears as expected. We move on to impelement something else, assured that CoffeeScript is doing exactly what we think it's doing.
However, each time we hover over the button, a new click handler is bound. Over time, our click handlers are piling up, and suddenly clicking on the button invokes dozens of fadeIn animations on the same element. This is the kind of ugly bug you'd likely only find by examining the compiled JavaScript in detail.
Details
An annoying (seeming) inconsistency in CoffeeScript's indentation
We have a method returning a promise (or other chainable object), and we want to invoke a few additional methods on it such as done or fail:
$.get('/users')
.done->## <several lines of code>#
.fail->showError()
Our fail callback is pretty short, so we might be tempted to put it on one line:
$.get('/users')
.done->## <several lines of code>#
.fail->showError()
This works fine, but it has opened to door for a few weird gotchas.
Gotcha
Suppose our done turns out to be shorter than we thought. We might refactor both callbacks to a single line:
If showSuccess happens to return a promise which also responds to fail, this entire block of code will work just fine until we discover that our error handling code isn't handling our errors, likely leading to a really ugly user experience.
Another way it might fail silently:
If we'd decided to reorganize our code so that fail is bound first...
...our AJAX call would appear to work but our success callback would never fire, as it's being bound inside our fail callback which won't be executing unless our AJAX call happens to fail too:
$.get('/users')
.fail(->showError())
.fail(showError) # drop arrow and supply callback as argument
.done(->showSuccess()) # not strictly necessary
As noted, it's not strictly necessary for the last method invocation in a chain, but by omitting it you're leaving yourself open to potential future confusion when you attempt to add another method to the chain.
A short list of CoffeeScript gotcha's I've encountered writing CoffeeScript professionally.
These are not problems with CoffeeScript, rather they are strange one-off situations where, especially coming from Ruby, differences in CoffeeScript's syntax have led to unexpected JavaScript.
But that line is getting a little long. Especially coming from a Ruby background, we may be tempted to simply break the method up into two lines in any of the following ways:
The problem is CoffeeScript has taken our single arguments hash and turned it into two arguments hashes. The "fixed" versions above compile to the following JavaScript:
This again gives us two hashes: user.save({first_name: 'bob'}, {last_name: 'smith', login: .... }).
Solution
Either don't indent the second line, or put the entire options hash, one key/value per line, after the method invocation. Either of these produce the desired output:
However the second example introduces it own gotcha. If you miss the trailing , on the first line, the second line is treated like a separate statement, and you get another difficult to detect bug. This is a reasonable thing to miss, given CoffeeScript makes the use of commas optional when declairing objects.
For example:
user.savefirst_name:'bob', last_name:'smith', login:'bob.smith'# <- no commaemail:'[email protected]', homepage:'bobsmith.com'
It's also worth noting the odd behaviour of comma-first style here. If we put the comma on the second line instead of leaving it on the first line, the problem vanishes, regardless of the level of indentation of the second line:
Not really a CoffeeScript gotcha, but something that could burn a few minutes:
If you forget you're writing CoffeeScript/JavaScript and try to use the =~ operator...
my_string=~/what/
... it will happily compile (in CoffeeScript) to the following and run (in CS or JS) without errors:
my_string=~/what/
Instead of testing the contents of my_string, we're applying the bitwise NOT operator,~, to a regular expression, which is coerced to an integer (0) and bitflipped to -1.