Javascript and first-class citizenry: require.js & node.js
Javascript on web pages is ubiquitous and its problems are legendary. Javascript, seen as a second-class code citizen, is usually hacked together even by seasoned developers. New libraries (jQuery, prototype, backbone, knockout, underscore) and runtime tools (firebug, jasmine) look like they solve many problems - and they do. But they still leave poorly written code as just that. One key problem is that all javascript code lives globally and this results in poorly managed, tested and delivered code.
In this session, I will illustrate that we can treat javascript as a first-class citizen using with require.js and node.js: it can be modular, encapsulated and easily unit tested and added to continuous integration cycle. The dependencies between javascript modules can also be managed and packaged just like in C# and Java. In the end, we can resolve many javascript difficulties at compile time rather than waiting until runtime.
AWS Community Day CPH - Three problems of Terraform
Javascript first-class citizenery
1. JAVASCRIPT &
1ST CLASS CITIZENRY
Fast, modular code with RequireJS &
node.js
talks@goneopen.com
2. 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/
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: Global functions
<body>
<button onclick="addMovie()">add
Movie</button>
<script type="text/javascript”>
function addMovie() {
$.observable( movies ).insert (
this.name);
}
</script>
</body>
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
});
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);
});
}
19. 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++);
});
28. 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
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 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!