JAVASCRIPT &
1ST CLASS CITIZENRY

Fast, modular code with RequireJS &
node.js
                              talks@goneopen.com
Size and complexity
As JavaScript applications grow in size and
complexity we need a real tool to handle
dependencies and keep our code modular.
RequireJS give you an easy way to define
dependencies on your module and it'll make
sure it‟s loaded before your code runs. It also
has a nice build system which can be used to
compress and obfuscate your code using a
closure compiler.
  Adapted from: http://www.meetup.com/jQuery-Boston/events/16191792/
Agenda
• Global Pollution
• Dependencies &
  Modularisation
• Building and Testing
• Optimisation
Global Pollution
In Javascript, global functions are not a
good strategy. They pollute. So, too do well
known namespaces – but we‟ll live with
that. Modules provide a way forward
against these problems.
Badness 1: Global functions
<body>
      <button onclick="addMovie()">add
Movie</button>
      <script type="text/javascript”>
           function addMovie() {
            $.observable( movies ).insert (
this.name);
                }
           </script>
</body>
Badness 2: Well-known namespaces
<html>
<head>
          <script src=http://code.jquery.com/jquery.js …
          <script src="jsrender.js" type="text/javascript"></script>
</head>
<body>
          <div id="movieList"><b>Our Movies</b><br /></div>

           <script type="text/javascript”>
                               $.templates({ movieTemplate: "#movieList” })
                     $.template.moveTemplate.render( )
           </script>
</body>
</html>
Template Pattern

(function($) {
    // nothing outside here can call this function
    function sumWithInterest( el, interset){
       return $(el).val() * interest;
    }
    $.extend($.fn , sumWithInterest) // except …
}(jQuery));
RequireJs (module) pattern

define([“jquery”], function($) {
     function sumWithInterest( el, interest){
       return $(el) .val() * interest;
     }
    return sumWithInterest// object or function
});
Dependencies & Modularisation
Let‟s take that all a little bit more slowly.
Each module ecapsulates
Create                     Consume

define(“main”,             require(“main”)
  function(){
      //all code in here
          // is scoped
  }
)
Access in is via a return only
Create            Consume

define(“main”,    require(“main”).init()
  function(){
      return {
      init:
function(){…}
      }
    }
)
Dependencies are explicit

define(“main”, [„log‟, „jquery‟],
  function( log, $ ){
       log.debug( $(„body‟).text() )

    }
)
Module loading can be ordered
define(“main”, [„log‟, „order!jquery‟, „order!jsrende
r‟],
    function( log, $ ){
       $.template(„body‟).render()
       log.debug( $(„body‟).text() )
    }
)
Building & Testing
Build command line
•   Rake
•   Gradle
•   Psake
•   Msbuild
•   Ant
•   Bat
•   sh
Test-first via jasmine
Units
• Views
• Events

Acceptance
• Full loading
Test html
           Code template <li>
                              <i>{{:type}}</i> (<abbr
                           class="date”{{:ordered}}</abbr>)
                           </li>

Test
describe("Html views", function() {
 var data = { type: "small", ordered: "1 min ago" }

  it("has a line item", function() {
   _requires(["text!coffee/views/_item.html", 'jsrender'],
         function( text ){
            $( "#test" ).html( $.templates( text ).render( data ) );
            expect($("li", "#test" ).size()).toEqual(1);
      });
  }
Test Events
it("should register add order handler”, function() {
     _requires([“coffee/loader”]));
    spyOn(_, 'order').andCallThrough()
    _.orderHandler( $('#test'), function(){ return {} )

  $('#test').click()

   expect(_.order).toHaveBeenCalled()
 });
Test that it actually works!

    it("should be able to add new order", function()
{
       _requires(["coffee/main"]));
       val = $('li', ‟#orders').size();
       $('button.order', ‟#orders').click()

expect($('li', ‟#orders').size()).toEqual(val++);
 });
Build and get runtime errors
$ rake build
node lib/r.js -o app.build.js

Tracing dependencies for: coffee/main

Error: Error evaluating module "undefined" at
location
"/Users/todd/Documents/src/coffee/build/scripts/
coffee/main.js":
Unexpected token: punc (,) (line: 4, col: 16, pos:
152)
Throw away the debugger


 no break points or step through

   … no really, I‟m serious
Optimisation
We still need to package code ready for
serving. Why wait until run?
Script-tag vomit
In development, it’s good   Production, it’s not
Pretty clean
Create bundles & linted
Script and CSS compressed
Ready for deployment
Gotchas!
• File paths between development and
    production
•   CSS url paths
•   Text (html, json) loading without server
•   Creating a localhost server for text
•   Hassle for smaller projects
Follow-up areas
•   Different AMD implementations
•   Plugins (page load, internationalisation)
•   Lazy and dynamic loading
•   Wrapping non-AMD code
•   Coffeescript
•   Data-main loading
•   Build runners and CI integration
Finally, what does this mean?
• We can unit test web-based application at the
  GUI layer
• We CAN reduce the number of
  expensive, system tests (eg selenium)
• Modularity and testing are likely to allow
  smaller pieces of work and potentially
  increased flow
• All this (coupled with REST) means interesting
  times!
Useful refs (and referred to
materials)


• http://www.angrycoding.com/2011/09/managing-dependencies-with-
    requirejs.html
•   http://patrickmcelhaney.com/AMD-Talk/#microjs
•   http://projects.haykranen.nl/amsterdamjs
•   http://www.slideshare.net/JulienZee/parisjs-10-requirejs-9111799
•   http://www.slideshare.net/tim_doherty/requirejs-8858167
•   http://www.slideshare.net/timoxley/testable-client-sidemvcappsinjavascript
•   http://www.slideshare.net/sioked/requirejs
Good luck
                               Todd Brackley

                               goneopen.com
                               twitter: toddbNZ


  code: github.com/toddb/javascript-amd

Javascript first-class citizenery

  • 1.
    JAVASCRIPT & 1ST CLASSCITIZENRY Fast, modular code with RequireJS & node.js talks@goneopen.com
  • 2.
    Size and complexity AsJavaScript applications grow in size and complexity we need a real tool to handle dependencies and keep our code modular. RequireJS give you an easy way to define dependencies on your module and it'll make sure it‟s loaded before your code runs. It also has a nice build system which can be used to compress and obfuscate your code using a closure compiler. Adapted from: http://www.meetup.com/jQuery-Boston/events/16191792/
  • 3.
    Agenda • Global Pollution •Dependencies & Modularisation • Building and Testing • Optimisation
  • 4.
    Global Pollution In Javascript,global functions are not a good strategy. They pollute. So, too do well known namespaces – but we‟ll live with that. Modules provide a way forward against these problems.
  • 5.
    Badness 1: Globalfunctions <body> <button onclick="addMovie()">add Movie</button> <script type="text/javascript”> function addMovie() { $.observable( movies ).insert ( this.name); } </script> </body>
  • 6.
    Badness 2: Well-knownnamespaces <html> <head> <script src=http://code.jquery.com/jquery.js … <script src="jsrender.js" type="text/javascript"></script> </head> <body> <div id="movieList"><b>Our Movies</b><br /></div> <script type="text/javascript”> $.templates({ movieTemplate: "#movieList” }) $.template.moveTemplate.render( ) </script> </body> </html>
  • 7.
    Template Pattern (function($) { // nothing outside here can call this function function sumWithInterest( el, interset){ return $(el).val() * interest; } $.extend($.fn , sumWithInterest) // except … }(jQuery));
  • 8.
    RequireJs (module) pattern define([“jquery”],function($) { function sumWithInterest( el, interest){ return $(el) .val() * interest; } return sumWithInterest// object or function });
  • 9.
    Dependencies & Modularisation Let‟stake that all a little bit more slowly.
  • 10.
    Each module ecapsulates Create Consume define(“main”, require(“main”) function(){ //all code in here // is scoped } )
  • 11.
    Access in isvia a return only Create Consume define(“main”, require(“main”).init() function(){ return { init: function(){…} } } )
  • 12.
    Dependencies are explicit define(“main”,[„log‟, „jquery‟], function( log, $ ){ log.debug( $(„body‟).text() ) } )
  • 13.
    Module loading canbe ordered define(“main”, [„log‟, „order!jquery‟, „order!jsrende r‟], function( log, $ ){ $.template(„body‟).render() log.debug( $(„body‟).text() ) } )
  • 14.
  • 15.
    Build command line • Rake • Gradle • Psake • Msbuild • Ant • Bat • sh
  • 16.
    Test-first via jasmine Units •Views • Events Acceptance • Full loading
  • 17.
    Test html Code template <li> <i>{{:type}}</i> (<abbr class="date”{{:ordered}}</abbr>) </li> Test describe("Html views", function() { var data = { type: "small", ordered: "1 min ago" } it("has a line item", function() { _requires(["text!coffee/views/_item.html", 'jsrender'], function( text ){ $( "#test" ).html( $.templates( text ).render( data ) ); expect($("li", "#test" ).size()).toEqual(1); }); }
  • 18.
    Test Events it("should registeradd order handler”, function() { _requires([“coffee/loader”])); spyOn(_, 'order').andCallThrough() _.orderHandler( $('#test'), function(){ return {} ) $('#test').click() expect(_.order).toHaveBeenCalled() });
  • 19.
    Test that itactually works! it("should be able to add new order", function() { _requires(["coffee/main"])); val = $('li', ‟#orders').size(); $('button.order', ‟#orders').click() expect($('li', ‟#orders').size()).toEqual(val++); });
  • 20.
    Build and getruntime errors $ rake build node lib/r.js -o app.build.js Tracing dependencies for: coffee/main Error: Error evaluating module "undefined" at location "/Users/todd/Documents/src/coffee/build/scripts/ coffee/main.js": Unexpected token: punc (,) (line: 4, col: 16, pos: 152)
  • 21.
    Throw away thedebugger no break points or step through … no really, I‟m serious
  • 22.
    Optimisation We still needto package code ready for serving. Why wait until run?
  • 23.
    Script-tag vomit In development,it’s good Production, it’s not
  • 24.
  • 25.
  • 26.
    Script and CSScompressed
  • 27.
  • 28.
    Gotchas! • File pathsbetween development and production • CSS url paths • Text (html, json) loading without server • Creating a localhost server for text • Hassle for smaller projects
  • 29.
    Follow-up areas • Different AMD implementations • Plugins (page load, internationalisation) • Lazy and dynamic loading • Wrapping non-AMD code • Coffeescript • Data-main loading • Build runners and CI integration
  • 30.
    Finally, what doesthis mean? • We can unit test web-based application at the GUI layer • We CAN reduce the number of expensive, system tests (eg selenium) • Modularity and testing are likely to allow smaller pieces of work and potentially increased flow • All this (coupled with REST) means interesting times!
  • 31.
    Useful refs (andreferred to materials) • http://www.angrycoding.com/2011/09/managing-dependencies-with- requirejs.html • http://patrickmcelhaney.com/AMD-Talk/#microjs • http://projects.haykranen.nl/amsterdamjs • http://www.slideshare.net/JulienZee/parisjs-10-requirejs-9111799 • http://www.slideshare.net/tim_doherty/requirejs-8858167 • http://www.slideshare.net/timoxley/testable-client-sidemvcappsinjavascript • http://www.slideshare.net/sioked/requirejs
  • 32.
    Good luck Todd Brackley goneopen.com twitter: toddbNZ code: github.com/toddb/javascript-amd