Real Life CoffeeScript
What is CoffeeScript?CS is a language that compiles intoJavaScript (like HAML and SASS)
Not a superset of JS (not like SCSS)
Not a JS framework (not a replacement forjQuery)
From the makerCoffeeScript is a little language that compiles into JavaScript. Underneath all of thoseembarrassing braces and semicolons, JavaScript has always had a gorgeous objectmodel at its heart. CoffeeScript is an attempt to expose the good parts of JavaScriptin a simple way. - Josh Ashkenas
Why all the fuss?CS will be a standard part of Rails 3.1 (along
with SASS)
It makes JS more like Ruby!
CS is an npm package, hence a gatewaydrug to Node.js and Socket.IO
Why use CS?compiles to lint-free JS, nothing fancy
easy to write "good parts javascript" thatlooks clean
makes good practices less tedious
HAML and SASS are awesome and nowyou're using those...
What are the features?Easy variable declaration (lexical scoping)
Easy class inheritance and binding
Easy instance variables @name =>this.name
Implicit return from functions
"String #{interpolation}"
Semantic Shortcuts-> and => instead of function(){}
execute() if that is thing1 and that isnt thing2
and/or/not instead of &&/||/!
that = thing1 or thing2
that or= thing3
Conditionalsif condition? (no parantheses, ? is .nil? not
.blank?)
if condition?() to evaluate if a functionreturns null
throwAFit() unless @name in ["Paul", "John"]
How much more?list traversal: for result in results
return (transform result for result in results)
switch/when/else instead ofswitch/case/default
yes/no, on/off for true/false
Show me some code!
Some ugly JavaScriptvar sortFunction = function(a,b) { if (typeof(a[sort_col]) === 'number') { // numeric sort if (sort_dir === 'up') return (a[sort_col] - b[sort_col]); else return (b[sort_col] - a[sort_col]); } else { // string sort var aName = a[sort_col].toLowerCase(), bName = b[sort_col].toLowerCase(); if (sort_dir === 'up') return (aName > bName); else return (bName > aName); }}this.getResults = function(){ // here is the place to apply filter var results = parent.results; if (filter_text) { results = $.makeArray($.map(results, function(result, i){ var haystack = [result.name, result.brands, result.address, result.city].join(', ') return (haystack.indexOf(filter_text) != -1) ? result : null; })); } if (sort_col && sort_dir) { results = results.sort(sortFunction); } return results;}
class Results setSort: (col, dir) -> if col? and dir? results = _(@results).sortBy (result) -> result[col] @results = if dir is 'down' then results.reverse() else results setFilter: (filter) -> if filter? matching = (needle) -> haystack = _.join needle.name, needle.brand_list, needle.address, needle.city _(haystack).includes(needle) @results = _.select @results, matching
standard jQuery with object binding$('#view_as_table').click => @.setViewType('Table', true)$('#view_as_thumbs').click => @.setViewType('Thumb', true)$('#view_as_list').click => @.setViewType('List', true)
Big Long jQuery Callvar makeSortable = function(){ $('#widgets .col').sortable({ items: 'div.widget', dropOnEmpty: true, handle: '.header h3', appendTo: 'body', connectWith: '.col', ... });makeSortable = -> $('#widgets .col').sortable items: 'div.widget' dropOnEmpty: yes handle: '.header h3' appendTo: 'body' connectWith: '.col' revert: yes cursor: 'move' placeholder: 'drag-over' stop: updateWidgetOrder
class JKT.GoogleLoader constructor: (callback) -> @callback = callback if google? then @loadComponent() else @loadGoogle() loadComponent: -> return loadGoogle: () -> script = document.createElement "script" script.src = "http://www.google.com/jsapi?key=#{JKT.google_api_key}&sensor=false" script.type = "text/javascript" if script.attachEvent? # IE script.attachEvent 'onreadystatechange', => if script.readyState in ['loaded', 'complete'] @loadComponent() else script.onload = => @loadComponent() document.getElementsByTagName("head")[0].appendChild(script)
class JKT.MapLoader extends JKT.GoogleLoader loadComponent: -> google.load 'maps', '3', other_params: "sensor=false" callback: @callback return
super Is Superclass JKT.Search.TableRenderer constructor: (results) -> @results = results render: -> $('#results').html $('#listing_grid').render [{foo:"bar"}] $('#results tbody').html $('#listing_grid_row').render(@results)class JKT.Search.MapRenderer extends JKT.Search.TableRenderer render: -> super if @results.length <= 300 $('#results tbody tr').each -> ...
class Results ... paginate: (start, end) -> if start? and end? @results = @results[start..end] toSentence: -> size = _.size(@results) or 0 if size is 1 then "1 result" else \ "#{size} results" enoughForMap: -> _.size(@results) < 300 any : -> _.isArray(@results) and _.any(@results)
Sources of resistance?
I already know JavaScriptKudos to you. I do too.
Not a substitute for knowing JS.
Your own JS isn't as clean as the compiledJS
Unless your last name is Resig or Crockford
It looks weird.It's the bastard child of Ruby and Python.
Once you get used to it, it's the JS that lookswhack.
I don't write JS, I writejQuery.
No you write JS using jQuery.
I use jQuery too.
CS makes your jQuery cleaner.
Sugar AllergiesAre you allergic to "syntactic sugar"?
CS is more than that.
We need a better name because we allknow sugar is bad for you.
Line Noise Reduction
Sugar = Noise ReductionBad line noise highly affects the readability of our code. It is true we get used to them,but once you remove it, there's no way back. - Jose Valim
Indentation Allergies"I like my curly braces"
CS uses Pythonic whitespace
You get used to it - cost/benefit
I really don't understand why using indentation level for blocks is socontroversial. You do indentation anyway; you don't want to violate OAOO; it
avoids hard-to-spot errors where indentation and begin/end or {/} differ; it lookscleaner; there's no way to have unmatched braces; and I never get indentationwrong, but sometimes I do have to count braces. In other words, don't let this
issue stop you from trying out Python; indentation-denoted blocks are very easyto get used to. -- Falk Bruegmann
Firebug Will BreakYou lose the correspondence between your
code and line numbers in Firebug
There is no FireCoffee
Use small coffee files to get approximate linecorrespondence
The compiled JS is not magical - you can stillread it
What about syntaxhighlighting?
No plugin for Eclipse/Aptana
Use TextMate, jEdit, gEdit, vim, emacs
The TextMate bundle is great!
Maybe on the next projectUnless your current project is almost done
and will never be maintained...
Porting that project is just what you need tolearn CS.
It's not "all or nothing": one file at a time
If you don't like it you can keep the change.
Where do I start?Don't just dive in or you'll be coding before
you're ready
Read the CS overview a few times(http://jashkenas.github.com/coffee-script)
Install CS and the TextMate bundle
Port a project's JS to CS
Write some new CS code