Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CoffeeScript and Drupal

CoffeeScript and Drupal

Slides for DrupalCamp Cork 2013 talk. It covers the features of CoffeeScript and how to integrate it into your development environment for Drupal themes.

Mark Horgan

November 08, 2013
Tweet

More Decks by Mark Horgan

Other Decks in Programming

Transcript

  1. (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); CoffeeScript was inspired by Ruby, Python and Haskell $ -> $(‘.button’).click -> $.ajax ‘/delete’, type: ‘POST’ success: (data) -> alert ‘Success’ error: (jqXHR) -> alert “There was an error deleting your data: #{jqXHR.responseText}” CoffeeScript JavaScript white space is signticant (Python) parentheses are optional (Ruby)
  2. Fits in with existing JavaScript environment CoffeeScript JavaScript JQuery or

    any JavaScript library/platform including Node.js compiles down to so, works with
  3. Your investment in a client-side toolkit can be re-used with

    different server- side frameworks. Drupal Symfony Rails Django Grails Client-side toolkit (Sass, CoffeeScript etc.)
  4. Each file is enclosed in a closure so your variables

    won’t clash with code in other files. main.coffee (function(){ }).call(this); JavaScript
  5. Variables will always be defined in the local scope. No

    accidentally defining something in the global scope. someFunction ->     localVariable = 1 var someFunction; someFunction = function() {     var localVariable;     return localVariable = 1; }; var someFunction = function(){     someVariable = 1; } JavaScript JavaScript CoffeeScript
  6. 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}”) CoffeeScript
  7. 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”; } }; CoffeeScript JavaScript
  8. String interpolation numErrors = 2 alert(“There are #{numErrors} in the

    form”) var numErrors; numErrors = 2; alert(“There are “ + numErrors + “ in the form”); CoffeeScript JavaScript
  9. Existential operator if obj?      console.log(‘obj exists’) if obj?.property     

    console.log(‘property of obj exists’) x = a ? b x ?= defaultValue x is equal to a if a exists, otherwise it’s equal to b if x doesn’t exist, set it to defaultValue CoffeeScript CoffeeScript CoffeeScript
  10. Iterating over arrays and objects for key, value of object

         console.log(“#{key}: #{value}”) for user in users console.log(“#{user.firstName} #{user.lastName}”) CoffeeScript CoffeeScript
  11. 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(); }); }; CoffeeScript JavaScript
  12. 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() Support for classes CoffeeScript
  13. Criticisms • Debugging is an issue Though source maps help

    • Verbally readable != quicker comprehension Though you can still use && instead of and • Significant white-space means CoffeeScript will always be compiled. Since you will want to minify the code when serving it to a browser. if (five && six && seven) doStuff(); doSomething() if five and six and seven from Ryan Florence’s Blog - bit.ly/IsGZMk
  14. 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
  15. Using CoffeeScript with Drupal Create a sub-theme from Omega 4

    theme. Omega 4 supports Sass, Grunt and Bower. To set up the client-side development environment on OSX see my blog post - bit.ly/19hJqRo Grunt builds your Sass and Cofffeescript Bower manages your 3rd-party libraries
  16. To add support for CoffeeScript: And update Gruntfile.js: npm install

    grunt-contrib-coffee --save-dev ... watch: { ... coffee : { files: [‘coffeescript/**/*.coffee’], tasks: [‘coffee:dev’] }, }, coffee: { options: { join: true }, dev: { options : { sourceMap : true }, files : { ‘js/my-theme-behaviors.js’ : [‘coffeescript/my-theme-behaviors.coffee’] } }, } }); ... grunt.loadNpmTasks(‘grunt-contrib-coffee’); ... Terminal JavaScript
  17. The example JavaScript that comes with Omega 4 will look

    like this in CoffeeScript: 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, set- tings.myExampleLinkTitle) $anchor.appendTo(this) CoffeeScript
  18. To get your Sass and CoffeeScript to compile any time

    you make changes: Grunt supports LiveReload so any changes to Sass or CoffeeScript will be updated in the browser without having to press the Refresh button. Google Chrome supports JavaScript source maps so you can debug CoffeeScript in the browser: grunt watch Terminal
  19. Unlike Compass, CoffeeScript 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’] } } }, 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’]); JavaScript Gruntfile.js my-theme-behaviors.coffee my-theme-behaviors.normal.js minified - .js rather than .min.js so you don’t have to change .info file my-theme-behaviors.js
  20. Dependencies need to be ordered manually: 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. coffee: { options: { join: true }, dev: { options : { sourceMap : true }, files : { ‘js/my-theme-behaviors.js’ : [‘coffeescript/utils/MiscUtils.coffee’, ‘coffeescript/ my-theme-behaviors.coffee’] } }, } #import utils.MiscUtils do ($ = jQuery) -> Drupal.theme::myThemeExampleButton = (path, title) -> $(“<a href=’#{path}’ title=’#{title}’>#{title}</a>”) JavaScript Gruntfile.js CoffeeScript