and Drupal Mark Horgan DrupalCamp Cork 2013
was inspired by Ruby, Python and Haskell $ -> $(.button ).click -> $.ajax /delete, white space is signticant (Python) type: POST success: (data) -> parentheses are optional (Ruby) alert Success error: (jqxhr) -> alert There was an error deleting your data: #{jqxhr.responsetext (function() { $(function() { return $(.button ).click(function() { return $.ajax( /delete, { type: POST, success: function(data) { return alert( Success ); error: function(jqxhr) { return alert( There was an error deleting your data: + jqxhr.responsetext); ); ); ); ).call(this);
Fits in with existing environment compiles down to so, works with JQuery or any library/platform including Node.js
Your investment in a client-side toolkit can be re-used with different serverside frameworks. Drupal Symfony Rails Django Grails Client-side toolkit (Sass, etc.)
Each file is enclosed in a closure so your variables won t clash with code in other files. (function(){ main.coffee ).call(this);
Variables will always be defined in the local scope. somefunction -> localvariable = 1 var somefunction; somefunction = function() { var localvariable; return localvariable = 1; ; No accidentally defining something in the global scope. var somefunction = function(){ somevariable = 1;
Like Ruby parentheses are optional, but I prefer to include them most of the time, but it s handy to leave them out in callbacks. $(.button ).click -> $.ajax /delete, type: POST success: (data) -> alert( Success ) error: (jqxhr) -> alert( There was an error deleting your data: #{jqxhr.responsetext )
Everything is an expression (at least, as much as possible). Which means the return statement is optional but I prefer to insert them anyway. grade = (student) -> if student.excellentwork A+ else if student.okaystuff if student.triedhard then B else B- else C var grade; grade = function(student) { if (student.excellentwork) { return A+ ; else if (student.okaystuff) { if (student.triedhard) { return B ; else { return B- ; else { return C ; ;
String interpolation numerrors = 2 alert( There are #{numerrors in the form ) var numerrors; numerrors = 2; alert( There are + numerrors + in the form );
Existential operator if obj? console.log( obj exists ) if obj?.property console.log( property of obj exists ) x = a? b x is equal to a if a exists, otherwise it s equal to b x?= defaultvalue if x doesn t exist, set it to defaultvalue
Iterating over arrays for user in users console.log( #{user.firstname #{user.lastname ) and objects for key, value of object console.log( #{key: #{value )
Fat arrow class MessageDialog showmethod: -> $(.closebutton ).click => @dosomethingmethod() dosomethingmethod: -> console.log( Hide the message dialog ) MessageDialog.prototype.showMethod = function() { var _this = this; return $(.closebutton ).click(function() { return _this.dosomethingmethod(); ); ;
Support for classes class Animal constructor: (@type) -> whatami: -> console.log("i am a #{@type") class Dog extends Animal constructor: -> super('dog') class Cat extends Animal -> constructor: -> super('cat') dog = new Dog() dog.whatami() cat = new Cat() cat.whatami()
Criticisms Debugging is an issue Though source maps help Verbally readable!= quicker comprehension Though you can still use && instead of and if (five && six && seven) dostuff(); dosomething() if five and six and seven Significant white-space means will always be compiled. Since you will want to minify the code when serving it to a browser. from Ryan Florence s Blog - bit.ly/isgzmk
Where to learn more? bit.ly/kjijsy bit.ly/ukwyn1 if you subscribe to Safari coffeescript.org a little bit brief coffeescriptcookbook.com has a section on design patterns
Using with Drupal Create a sub-theme from Omega 4 theme. Omega 4 supports Sass, Grunt and Bower. Grunt builds your Sass and Cofffeescript Bower manages your 3rd-party libraries To set up the client-side development environment on OSX see my blog post - bit.ly/19hjqro
To add support for : npm install grunt-contrib-coffee --save-dev And update Gruntfile.js:... watch: {... coffee : { files: [ coffeescript/**/*.coffee ], tasks: [ coffee:dev ] Terminal coffee: { options: { join: true dev: { options : { sourcemap : true files : { js/my-theme-behaviors.js : [ coffeescript/my-theme-behaviors.coffee ] );... grunt.loadnpmtasks( grunt-contrib-coffee );...
The example that comes with Omega 4 will look like this in : do ($ = jquery) -> Drupal.theme::myThemeExampleButton = (path, title) -> $( <a href= #{path title= #{title >#{title</a> ) Drupal.behaviors.myThemeExampleBeahvior = attach: (context, settings) -> $(.some-selector, context).once foo, -> $anchor = Drupal.theme( mythemeexamplebutton, settings.myexamplelinkpath, settings.myexamplelinktitle) $anchor.appendto(this)
To get your Sass and to compile any time you make changes: grunt watch Terminal Grunt supports LiveReload so any changes to Sass or will be updated in the browser without having to press the Refresh button. Google Chrome supports source maps so you can debug in the browser:
Unlike Compass, doesn t minify so you will have to use Uglify to do it:... coffee : { options : { join: true dist : { options : { sourcemap : false files : { js/my-theme-behaviors.normal.js : [ coffeescript/my-theme-behaviors.coffee ] my-theme-behaviors.coffee my-theme-behaviors.normal.js Gruntfile.js my-theme-behaviors.js uglify : { dist : { options : { mangle : true, compress : true files : [{ expand : true, cwd : js, src : [ **/*.normal.js,!**/*.min.js ], dest : js, ext :.js ] );... grunt.registertask( build, [ coffee:dist, uglify:dist ]); minified -.js rather than.min.js so you don t have to change.info file
Dependencies need to be ordered manually: coffee: { options: { Gruntfile.js join: true dev: { options : { sourcemap : true files : { js/my-theme-behaviors.js : [ coffeescript/utils/miscutils.coffee, coffeescript/ my-theme-behaviors.coffee ] There is a library called Coffee Percolator that allows you to specify dependencies at the start of each file but the Grunt plug-in doesn t handle source maps properly. #import utils.miscutils do ($ = jquery) -> Drupal.theme::myThemeExampleButton = (path, title) -> $( <a href= #{path title= #{title >#{title</a> )