Switching to Octopress

Posted

Just a quick note to let you know that if you follow this blog, I'm moving my blogging efforts to Octopress, and my blog will have a new home at rmurphey.com. This URL (blog.rebeccamurphey.com) and the content will remain alive for the forseeable future, but I don't intend to post anything new here, and eventually I hope to move the content that's here to the new location. Thanks for reading, and I hope you'll follow me over to my new blogging home :)

Comments (2)
Posted

A new chapter

Posted

It was three years ago this summer that I got the call, bought the Yuengling, smoked the cigarettes, and began life as an independent consultant. It’s been (almost) three years of ups and downs, and, eventually, among the most rewarding experiences of my life. Day by day, I wrote my own job description, found my own clients, set my own schedule, and set my own agenda.

Starting tomorrow, it’s time for a new chapter in my working life: I’ll be joining Toura Mobile full-time as their lead JavaScript developer, continuing my work with them on creating a PhoneGap- and Dojo-based platform for the rapid creation of content-rich mobile applications.

I’ve been working with Toura for about six months now, starting shortly after I met Matt Rogish, their director of development, at a JavaScript event in New York. They brought me on as a consultant to review their existing application, and the eventual decision was to rewrite it from the ground up, using the lessons learned and knowledge gained from the first version to inform the second. It was a risky decision, but it’s paid off: earlier this year, Toura started shipping apps built with the rewritten system, and the care we took to create modular, loosely coupled components from the get-go has paid off immensely, meeting current needs while making it easier to develop new features. With the rewrite behind us, these days we’re using the solid foundation we built to allow users of the platform to create ever more customized experiences in their applications.

If you know me at all, you know that I’ve been pretty die-hard about being an independent consultant, so you might think this was a difficult decision. Oddly, it wasn’t — I’ve enjoyed these last several months immensely, the team I work with is fantastic, and I’ve never felt more proud of work I’ve done. Whenever I found myself wondering whether Toura might eventually tire of paying my consulting rates, I’d get downright mopey. Over the course of three years, I’ve worked hard for all of my clients, but this is the first time I’ve felt so invested in a project’s success or failure, like there was a real and direct correlation between my efforts and the outcome. It’s a heady feeling, and I hope and expect it to continue for a while.

By the way, I’ll be talking about the rewrite at both TXJS and GothamJS in the next few weeks.

Also: we’re hiring :)

Comments (11)
Posted

A Dojo Boilerplate

Posted

When I first started playing with the Dojo Toolkit, it was easy enough to use the CDN-hosted dojo.js and get started, but before long I wanted to make use of one of the features that drew me to Dojo in the first place: the build system that parses your code’s dependencies as expressed by dojo.require() statements and creates production-ready files.

Coming from a world where this was entirely a DIY affair, the patterns I should follow for taking advantage of Dojo’s system were, shall we say, less than clear. There was a lot of frustration, a lot of swearing, and a lot of pleas for help in #dojo on Freenode.

These days, I’m talking about Dojo a lot, and I’ve gotten pretty comfortable with how to set up a project — I even wrote a post about scaffolding a Dojo app once I felt like I had the basics down — but for a long time I’ve wanted to release a ready-made starter project, rather than making people follow seven lengthy steps.

With the help of Colin Snover, I’m pleased to release the Dojo Boilerplate, a simple starter project if you’d like to get your feet wet with Dojo and the power of its dependency management and build system. It comes with a bare-bones do-nothing app, a shell script for downloading the Dojo SDK and getting it in the right place, and a shell script and profile file for actually creating a built version. For the brave, it also includes a work-in-progress router for single-page apps — one of the few features that I feel Dojo itself is missing. Everything you should need to know is documented in the README.

I’ve also created a small demo app that uses the boilerplate and shows some of the basic concepts of MVC development using Dojo, including separating your code into models, views, controllers, and third-party services. It includes an example of templated widgets, which are one of the biggest selling points of Dojo for me, as well as an uber-basic example of object stores, new in Dojo 1.6.

The goal of the boilerplate and the demo app is to eliminate some of that pain and WTF that I went through — while Dojo is ridiculously powerful, the barrier to entry can seem daunting. Over and over again, though, I am grateful that I took the time to overcome it.

Finally: as always, pull requests and issues are welcome. Enjoy.

Update: Colin is now the maintainer of the boilerplate; I've updated the links above accordingly.

Comments (4)
Posted

When you're building a non-trivial JS application ...

Posted

I sense another round of discussion of this is about to begin, and 140 characters isn't quite enough to say what I want to say, so:

When you're building a non-trivial JS application, you don't want a jQuery developer, or a Dojo developer, or a YUI developer, or, frankly, any developer who chooses their tool before they evaluate the problem. For god's sake, you want a JavaScript developer. Can you roll your own solution with jQuery as the base? Yes! Should you? I don't think so, and I advise my clients against it for reasons I've written about at length, but I'm open to hearing compelling, articulate, fact-based arguments in favor of it!

But do me a favor, OK? Don't base your arguments solely on the winner of a popularity contest. Don't tell me how easy it is to find developers familiar with one library or another, because I'll come right back and ask you just how good those developers will be at solving problems that aren't addressed by said library. And please tell me you've at least explored some of the other options besides [insert the library you're advocating here]. 

People read what I write about JavaScript libraries and they write me heartfelt tweets and e-mails saying OMG YOU HATE JQUERY NOW WHAT HAPPENEDDDDD? I don't hate jQuery! It is a perfectly viable and valuable tool for so many things! But when people argue not just its viability but its absolute supremacy, when people get defensive and possibly even angry that I suggest there are solutions that are vastly better suited to a certain set of problems, when people contort themselves into pretzels to make their case and their case is "well, it's not that bad" ... well, that smacks of blind loyalty, not a thoughtful weighing of the tradeoffs and challenges we face as developers, and I question how those people would fare if actually confronted with the needs of a non-trivial application. 

So, please: Tell me what solutions you've looked at for non-trivial application development. Tell me where they work, tell me where they fall short. Tell me what you're working on and how you chose the tools. Don't tell me why I'm wrong -- tell me why you're right. Deal? Discuss.

Filed under  //  javascript  
Comments (15)
Posted

The Future of jQuery Fundamentals (and a confession)

Posted

About 9 months ago, I released jQuery Fundamentals, a free, online training curriculum for people interested in learning jQuery based on material I’d assembled while leading jQuery trainings.

The response was and has continued to be amazing: not only has the book seen hundreds of thousands of visits, but it has also received content contributions and bug reports from dozens of people. It has become something of a collaborative work, and one of the go-to resources for jQuery and beginning JavaScript learning. It has been used to teach classes internally at companies and at colleges and universities, and it’s been translated into multiple languages. It’s even made me a tad bit of money — I recently granted a license to Webucator to create derivative works for their jQuery class — and landed me near the top of Google’s search results for “jQuery training”.

And so here is where we get to the confession part: while I’ve stayed very much in touch with the evolution of jQuery these last couple of years, written gobs of sample code in efforts to make people better at using the library, and even played a bit of a role in some of the new features in jQuery 1.5, the last time I chose the library for a project was in the fall of 2008. The last time I used it on a project at all was in the summer of 2010, and in a matter of a few weeks I was gutting the fragile, bug-ridden, DOM-centric code and re-writing the single-page application with — wait for it! — Dojo. jQuery and I have gone from being in a committed relationship to seeing other people to pretty much just saying hi on Facebook now and again.

This has put me in a strange place with jQuery Fundamentals — I want to be investing my energy supporting projects that I use, and while I can still write jQuery just fine and stay in touch with what’s going on with it, I really don’t … use it. That’s made it increasingly difficult to continue maintaining jQuery Fundamentals as a resource for the jQuery community.

Burying the Lede

At the jQuery conference in Boston last fall, John Resig invited me to participate in a conversation about an effort by the project to create a learning resource for the community, and through the course of that and future conversations, jQuery Fundamentals has found its new home.

I’ve been working actively with jQuery team member (and yayQuery co-host) Adam J. Sontag and community member Dan Heberden to get the book into good shape as it transitions to being “owned” by the jQuery project. I’ve also donated a third of the proceeds of the Webucator licensing arrangement to the jQuery project, to recognize the contributions of the community and to give even a wee bit of financial support to the learning efforts.

Adam, Dan, and I will be working hard to address some of the open issues with the book in the coming weeks. If you’re interested in helping, drop me an email, hit me up on Twitter, or just submit a pull request (though you may want to talk to us first if the solution to an issue isn’t straightforward). From formatting fixes to writing new content to updating the book to reflect the changes in jQuery 1.5, there’s a lot to be done.

What’s Next?

These days I’m working with a fantastic client doing mobile application development with PhoneGap and Dojo. It’s pretty much the most challenging, engaging, rewarding project I’ve had an opportunity to work on in nearly three years of independent consulting. These days, when I get the very inquiries I hoped to get by releasing jQuery Fundamentals in the first place, I direct people to the excellent folks at Bocoup. Slowly, I’m recalibrating my efforts and attention toward the projects that make my day-to-day development life better. As soon as I feel like jQuery Fundamentals is in a good place where I don’t have to worry about its future, you can expect to see a lot more learning-related content coming from me again; just, this time, it probably won’t be about jQuery.

I hope you’ll stick around.

Filed under  //  jquery fundamentals   training  
Comments (12)
Posted

In Search of JavaScript Developers: A Gist

Posted

I posted a gist the other day in an attempt to locate some JavaScript help. I’m finding, lately, that I’m being asked to do more work than I can comfortably take on without having some overflow capacity, but I’ve been a little lax at actually identifying people who can provide that capacity up until now. That meant I was turning down work, and that’s not a good thing.

I had a great time the last couple of days watching people come up with solutions, and was especially gratified that so many people who weren’t looking for work thought that answering JavaScript questions sounded like fun.

A lot of people have asked if I’d be providing “answers” to the questions I posed in the gist, so I wanted to try to do that, mixed in with a bit of commentary about what I was looking for.

Question 1: Writing Readable but Terse Code

// 1: how could you rewrite the following to make it shorter?
if (foo) {
  bar.doSomething(el);
} else {
  bar.doSomethingElse(el);
}

Answers to this question told me whether to keep reading; I was looking for something like:

bar[foo ? 'doSomething' : 'doSomethingElse'](el);

// OR

bar['doSomething' + (foo ? '' : 'Else')](el);

To be honest, the second one is maybe a little too clever. But generally, I wanted to see submissions that understood using the ternary operator instead of an if statement to create shorter but still-readable code when all that is different is a method name.

Some people submitted an answer that used a ternary operator, but didn’t take advantage of it to just switch the method name:

foo ? bar.doSomething(el) : bar.doSomethingElse(el);

This is an improvement, definitely, but there’s room for more.

Question 2: Understanding Variable Scope

var foo = 'hello';

(function() {
  var foo = foo || 'world';
  console.log(foo);
})();

This was a question where, in hindsight, what I was looking for may not have been super-clear. I got a lot of right-enough answers, but really what I wanted to hear was that the || statement was absurd, because foo would always evaluate to 'world' due to variable hoisting.

This can be sort of a sort of crazy thing to wrap your head around, but basically JavaScript scans the contents of a function for var statements before it runs the function. Any variable initialized with a var statement inside the function will become local to the function, even if the variable is “used” before it is initialized. Changing the order of the two lines inside the function shows this readily:

var foo = 'hello';

(function() {
  console.log(foo); // undefined!
  var foo = foo || 'world';
})();

Some submissions thought I wanted access to the external foo inside the closure — not an unreasonable interpretation of the question.

var foo = 'hello';

(function(f) {
  var foo = f || 'world';
  console.log(foo);
})(foo);

Anyway, there were lots of right-enough answers, but anyone who talked about hoisting definitely caught my eye.

Question 3: Working with Objects and Prototypes

This question was looking for really basic understanding of prototypes. It also was written to be a tad hard to follow, lumping all the questions into a single paragraph, to simulate a not-atypical client request.

// 3: given the following code, how would you override the value of the 
// bar property for the variable foo without affecting the value of the 
// bar property for the variable bim? how would you affect the value of 
// the bar property for both foo and bim? how would you add a method to 
// foo and bim to console.log the value of each object's bar property? how 
// would you tell if the object's bar property had been overridden for the 
// particular object?
var Thinger = function() {
  return this;
};

Thinger.prototype = {
  bar : 'baz'
};

var foo = new Thinger(),
    bim = new Thinger();

The good submissions broke the question down into separate comments, and then showed the answers:

// override the bar prop for foo w/o affecting bim
foo.bar = 'new value';

// change the bar prop for both foo and bim
// (*if* it hasn't been overridden locally!)
Thinger.prototype.bar = 'another new value';

// we could delete foo.bar now and it would get
// the prototype value instead
// delete foo.bar;

// add a method to foo and bim to log bar
Thinger.prototype.logger = function() {
  console.log(this.bar);
};

// check if bar has been overridden
foo.hasOwnProperty('bar'); // true
bim.hasOwnProperty('bar'); // false

Question 4: Iterating over Objects

This one was pretty basic:

// 4: given the following code, and assuming that each defined object has 
// a 'destroy' method, how would you destroy all of the objects contained 
// in the myObjects object?
var myObjects = {
  thinger : new myApp.Thinger(),
  gizmo : new myApp.Gizmo(),
  widget : new myApp.Widget()
};

Really I just wanted to see people iterate over an object without the use of a helper like jQuery.each. The hasOwnProperty check may seem like overkill, but I was glad when people didn’t leave it out. Adding in the delete statement was another nice touch, though not strictly required by the question.

for (var obj in myObjects) {
  if (myObjects.hasOwnProperty(obj)) {
    myObjects[obj].destroy();
    delete myObjects[obj];
  }
}

Question 5: Solving Deceptively Simple Problems

This question was probably the most fun, because even though it was a dead-simple task, the answers were all over the map. This was the question:

// 5: given the following array, create an array that contains the 
// contents of each array item repeated three times, with a space between 
// each item. so, for example, if an array item is 'foo' then the new 
// array should contain an array item 'foo foo foo'. (you can assume the 
// library of your choice is available)
var myArray = [ 'foo', 'bar', 'baz' ];

Rather than going through the different answers one at a time, I’m just going to tell you to visit this JSPerf test page to see some of the variations, and their relative performance.

This brings up a good question tweeted by Ryan Florence:

T | F – In #JavaScript, 90% of the time we do stuff only a few times, maybe hundreds. Therefore, 90% of the time Readability > Performance.

I tend to come down on the side of readability and compression over straight-up perf, precisely because we’re rarely doing anything that’s actually that intensive. On the other hand, we should avoid doing things that are outright stupid; where the line gets drawn depends a lot, I think, on experience.

Here’s the thing, though: Something that seems like a gratuitous and obscure optimization to a less experienced developer might seem completely readable and obvious to a more experienced developer. How to balance this? Can comments bridge the gap? Should the gap be bridged? I dunno.

Question 6: Basic jQuery Best Practices and DRY

I see way too much code in real life that looks like this question.

// 6: how could you improve the following code?
$(document).ready(function() {
  $('.foo #bar').css('color', 'red');
  $('.foo #bar').css('border', '1px solid blue');
  $('.foo #bar').text('new text!');
  $('.foo #bar').click(function() {
    $(this).attr('title', 'new title');
    $(this).width('100px');
  });

  $('.foo #bar').click();
});

There are a slew of things wrong in this tiny snippet. First and foremost, making the same selection repeatedly suggests that the author fundamentally doesn’t understand what their code is doing, or the expense they’re incurring in doing it; the selection should be made once, and then the selection should be cached and/or the methods should be chained.

While it wasn’t imperative, submitters did well to point out that CSS changes should be made via class names instead of hard-coded CSS in JavaScript; they also did well to put the click handler in a named function. Finally, while there may be cases where an ID selector needs to be prefixed by a class, i.e. .foo #bar, I appreciated it if people questioned this.

$(document).ready(function() {
  var handleClick = function(el) {
        el.attr('title', 'new title')
          .width('100px');
      },

      bar = $('#bar')
        // ideally: use a class for this
        .css({
          color : 'red',
          border : '1px solid blue'
        })
        .text('new text!')
        .click(function(e) {
          handleClick($(e.target));
        });

  handleClick(bar);
});

Question 7: Asynchronicity

This is a pretty newby thing, but I wanted to make sure people understood the basic concept of async requests — that is, you can’t set the value of a variable inside an XHR’s callback and expect that value to be available immediately.

(function() {
  var foo;

  dojo.xhrGet({
    url : 'foo.php',
    load : function(resp) {
      foo = resp.foo;
    }
  });

  if (foo) {
    // run this important code
  }
})();

Fixing this just involves waiting for the XHR to complete before running the code that expects foo to be set. (Alternately, you could make the request run synchronously by setting sync : true in the XHR config object.)

There was one other issue with this code as well: Dojo needs to know the response should be handled as JSON, else it will handle it as text. If a submitter missed this, I didn’t hold it against them — Pete Higgins actually had to point it out to me :) That said, it would become pretty obvious pretty quickly in real code.

(function() {
  dojo.xhrGet({
    url : 'foo.php',
    handleAs : 'json'
  })
  .addCallback(function(resp) {
    if (resp && resp.foo) {
      // do stuff
    }
  });
})();

Note that the callback function could also be specified in the XHR config object using the load property; Dojo’s XHRs are great in that while you can specify everything in a config object, you can also attach callbacks to the return value of the XHR methods. You should read more about this because it is very pleasant.

Question 8: DRY

Repetitive code is dumb.

// 8: how could you rewrite the following code to make it shorter?
(function(d, $){
  $('li.foo a').attr('title', 'i am foo');
  $('li.bar a').attr('title', 'i am bar');
  $('li.baz a').attr('title', 'i am baz');
  $('li.bop a').attr('title', 'i am bop');
})(dojo, dojo.query);

How far you want to go with DRYing this out is debatable, but to me this cries out for improvement. Here’s what I’d do:

(function(d, $){
  d.forEach(['foo', 'bar', 'baz', 'bop'], function(c) {
    $('li.' + c + ' a').attr('title', 'i am ' + c);
  });
})(dojo, dojo.query);

I’d be lying if I didn’t mention that I also wanted to show here how easy it is to make Dojo look like jQuery. After all, it’s just JavaScript, right?

Question 9: DOM Manipulation Best Practices & DRY

I thought it was well-known that we don’t append 202 things to the DOM one at a time; the good news is, most people did know this. The bad news is, some people did not.

In addition to doing 202 appends, this code also does 202 selections. To top it off, the iterator i is global because we didn’t prefix it with var.

// 9: how would you improve the following code?
for (i = 0; i <= 100; i++) {
  $('#thinger').append(
    '<p><span class="thinger">i am thinger ' + i + '</span></p>'
  );
  $('#gizmo').append(
    '<p><span class="gizmo">i am gizmo ' + i + '</span></p>'
  );
}

Here’s a fix:

var thingerDom = [], gizmoDom = [],
    tpl = '<p><span class="%s">i am %s %i</span></p>',
    tplFn = function(str, i) {
      return tpl.replace(/%s/g, str).replace(/%i/g, i);
    },
    i;

for (i = 0; i <= 100; i++) {
  thingerDom.push(tplFn('thinger', i));
  gizmoDom.push(tplFn('gizmo', i));
}

$('#thinger').append(thingerDom.join(''));
$('#gizmo').append(gizmoDom.join(''));

There’s more that could be done here to DRY this out a bit more, but the fix addresses the main problem of excessive DOM manipulation.

Question 10: Loose Typing

Numbers in JavaScript suck, especially when the user enters them.

// 10: a user enters their desired tip into a text box; the baseTotal, 
// tax, and fee values are provided by the application. what are some 
// potential issues with the following function for calculating the total?
function calculateTotal(baseTotal, tip, tax, fee) {
  return baseTotal + tip + tax + fee;
}

How you’d actually deal with this problem would probably depend on the business logic of your application; you may be well-advised to convert everything to integers instead of trying to deal with decimals, because math with floats in JavaScript can have issues.

What I wanted to see in submissions, though, was an awareness that the tip would come to us as a string, and we couldn’t just add it to the other arguments and expect a useful result. I was more interested in the discussion of this problem, and other problems that could arise, but here’s at least the beginning of a solution:

function calculateTotal(baseTotal, tip, tax, fee) {
  // convert the tip to a number using base 10;
  // allow for a NaN result from parseFloat
  tip = parseFloat(tip) || 0;

  // don't allow a negative tip
  if (tip < 0) { tip = 0; }

  return baseTotal + tip + tax + fee;
}

Question 11: Array Mapping

For this question, I was looking for code that used a map method to return an array by running a function on each item in the array. Some people used a forEach method to iterate over the array instead, and then push the results to a new array they created. I guess this is OK, but it’s not my preference.

// 11: given the following data, write code that returns an array 
// containing the name of each item, followed by a comma-separated list of
// the item's extras, if it has any. e.g. 
//
//    [ "Salad (Chicken, Steak, Shrimp)", ... ]
//
// (you can assume the library of your choice is available)
var menuItems = [
  {
    id : 1,
    name : 'Salad',
    extras : [
      'Chicken', 'Steak', 'Shrimp'
    ]
  },
  {
    id : 2,
    name : 'Potato',
    extras : [
      'Bacon', 'Sour Cream', 'Shrimp'
    ]
  },
  {
    id : 3,
    name : 'Sandwich',
    extras : [
      'Turkey', 'Bacon'
    ]
  },
  {
    id : 4,
    name : 'Bread'
  }
];

Here’s an answer:

var newArray = dojo.map(menuItems, function(item) {
  var ret = item.name;
  if (item.extras && item.extras.length) {
    ret += '(' + item.extras.join(', ') + ')';
  }
  return ret;
});

Bonus 1: Functional Programming 101

This was a late addition, courtesy of Andrew Hedges, so not everyone saw it:

// BONUS: write code such that the following alerts "Hello World"
say('Hello')('World');

I wanted to see people understand that functions could return other functions, and that the returned function has access to the scope of the wrapper function:

var say = function(first) {
  return function(second) {
    alert(first + ' ' + second);
  }
};

Some people got pleasantly carried away with this question; check out this JSFiddle from Colin Snover.

Bonus 2: Attention to Detail

This last bonus was riddled with errors, including some that I made accidentally when I wrote it at 2 a.m.

// BONUS: what is the faulty logic in the following code? 
// how would you fix it?
var date = new Date(),
    day = date.getDate(),
    month = date.getMonth(),
    dates = [];

for (var i = 0; i <= 5; i++) {
  dates.push(month + '/' + (day + i));
}

console.log('The next five days are ', dates.join(', '));

Here’s what you should see:

  • The for loop will return 6 dates, not 5.
  • The method for calculating the date for each successive date could end up with nonexistent dates (32, 33, etc.), and it doesn’t change the month when it should.
  • The getMonth method on the date object returns a zero-indexed month.

Here’s a fix:

(function() {
  var date = new Date(),
      otherDate = new Date(),
      day = date.getDate(),
      future = 5,
      dates = [],
      i;

  for (i = 1; i <= future; i++) {
    otherDate.setDate(day + i);
    newMonth = otherDate.getMonth() + 1;
    newDay = otherDate.getDate();
    dates.push(newMonth + '/' + newDay);
  }
})();

Postscript

I want to be really clear that I’m not some super-awesome and infallible JavaScript developer, and more to the point, there was a time in the not-too-distant past where I would have failed my own quiz miserably. While my main goal in putting this together was to find some skilled developers to help me out, I also wanted to provide a tool for exposing up-and-coming developers to some slightly more advanced concepts of JavaScript. I hope that, whatever your skill level, you found it to be at least entertaining, and at best, useful. I also hope you’ll forgive any gross errors I’ve made in the answers above, though I tried really hard to test them all.

Post-Postscript

  • To the person who complained that I included code from a library other than jQuery, and who helpfully illustrated their point with the graph that shows jQuery is obviously better than anything that ever was: I’m not sure whether to laugh or cry or just be really snarky. I’m looking for JavaScript developers. If you don’t see the value in knowing more than just jQuery, and if you can’t find your way through relatively trivial non-jQuery code, you need not apply.
  • If you found the questions vague and requiring a lot of assumptions or guesswork, well, welcome to consulting — if such things make you uncomfortable, we’re not going to be a good fit. I was looking for people to tell me what they know, to impress me, to point out where they saw holes in the questions, to take initiative. This was not a multiple choice test; it was an interview.

License

If you think the quiz would be useful to you as you’re looking for a JavaScript developer to call your own, it’s licensed under the WTFPL, which you should read just because it’s funny.

Filed under  //  javascript   performance   refactoring  
Comments (29)
Posted

Making sense of dojo.when: A simple XHR caching example

Posted

Right before Dojo 1.5 came out, the Sitepen blog had a great post about the improvements 1.5 would bring for dojo.Deferred. The part that really caught my eye was dojo.when, a method that lets you pass a value to a function whether that value is available now or as the result of some asynchronous operation. Either way, you get a “promise” that when the value is available, the function you provided will run.

This is one of those things that was super-neat when I read about it, but it took me a while to incorporate it into my code — it’s only in the last couple of weeks that I’ve had that wonderful moment when I’ve said “oh, I could totally use dojo.when for that!” Moments like these make me very happy.

It’s pretty common that an application makes an Ajax request for some data, and then caches that data so the request won’t have to happen again; the pattern might look something like this:

var myCache = {};

function getSomeStuff(stuffId) {
  if (myCache[stuffId]) {
    handleResponse(myCache[stuffId]);
    return;
  }

  dojo.xhrGet({
    url : 'foo.php',
    content : { id : stuffId },
    load : function(response) {
      myCache[stuffId] = response;
      handleResponse(response);
    }
  });
}

Here we have a function that takes an ID; the function looks in the cache to see if there’s a value stored for the ID, and if so, it passes the stored value to a handleResponse function. If not, it does an XHR to get the data; when the XHR succeeds, it stores the data in the cache and, again, passes the value to the handleResponse function.

There’s nothing strictly wrong with this, but I discovered that some neat abstraction opportunities became more clear when I switched to using dojo.when instead:

var myCache = {};

function getSomeStuff(stuffId) {
  dojo.when(
    myCache[stuffId] || dojo.xhrGet({
      url : 'foo.php',
      content : { id : stuffId },
      load : function(response) {
        myCache[stuffId] = response;
      }
    }),
    handleResponse
  );
}

Now we’re telling our getSomeStuff function to look for a cached value; if it finds one, dojo.when will immediately pass that value to the handleResponse function. If it doesn’t find one, it will run the XHR, and dojo.when will magically pass the XHR’s response to the handleResponse function instead. This is hot.

This works because dojo.xhrGet returns a “promise” object with a then method. dojo.when looks to see whether it got a promise object as its first argument; if so, it uses the then method of the promise object to attach the callback provided as the second argument to dojo.when. If not, it simply calls the callback immediately on the first argument. The real magic is actually in dojo.Deferred, not in dojo.when itself. Since all of Dojo’s XHR methods return a dojo.Deferred promise, dojo.when will “just work.”

I found that I was going through my application and ripping out instances of the old code, replacing it with the new. And then I had that “oh sh*t I’m copying and pasting, aren’t I …” moment, and saw my way to an abstraction.

In my application, I was actually caching the responses using the URL from which I’d requested them, which works out to be a perfectly unique ID for the data. (This particular part may or may not work in your application.) My abstraction was an essentially drop-in replacement for dojo.xhrGet calls:

var cache = {};

function cacheableXhrGet(settings) {
  var url = settings.url,
      req = cache[url] || 
            dojo.xhrGet(dojo.mixin({
              // override the load handler
              load : function(resp) {
                cache[url] = resp;
              }
            }, settings));

  dojo.when(req, settings.load);
  return req;
}

I can pass a settings object to cacheableXhrGet that looks exactly like the object I’d pass to dojo.xhrGet, but replace the load function before actually passing it to dojo.xhrGet. But before the XHR even has a chance to get set up, I check my cache for a stored response; if I find one, I store it in the req variable, but otherwise I store the XHR there.

In either case, the function defined at settings.load gets the proper response value via dojo.when. For bonus points, I then return either the cached value or the XHR — which means other code can use the return value of cacheableXhrGet for its own dojo.when. How neat is that?

Conclusion

Promises and deferred’s are a really pleasant tool to have in your JavaScript arsenal once you get the hang of them, and dojo.when seems like a great place to start understanding them.

Out of the box, Dojo makes use of deferreds for all of its XHR functionality, meaning that you can pass around the return value of any Dojo XHR method and do fun things you can’t do with jQuery’s $.ajax, like add more callbacks to a request after you’ve set it up.

I’ve just recently started realizing when I could incorporate dojo.Deferred functionality into my own code — again, now that I’ve got the hang of it, I’m pretty sure it’s going to dramatically change how I write asynchronous code.

Disclaimer: This post contains sample code for illustration purposes. In reality it’s all namespaced and these naked functions are actually methods in classes and stuff, and the real code doesn’t even look much like the code you see here. I’ve also completely ignored questions of when to clear or invalidate the cache. You’ve been warned.

Filed under  //  deferreds   dojo   dojo.when   promises  
Comments (4)
Posted

Code Org, Take 2: Structuring JavaScript Applications

Posted

It’s hard to believe it was not even a year ago that I spoke at the jQuery Conference in Boston about code organization. At the time, I’d been thinking for a few months about how to improve my own code and give it more structure, and how to give advice to others about how to do the same.

My talk was about code organization, but really I was talking about how you might organize a single piece of functionality; I didn’t even begin to answer the larger question of how to structure a bona fide JavaScript application. Really, that question is almost perpendicular to the one I was tackling at the time; it’s a question of strategy, not tactics.

A year later, I’d like to share my thoughts on how I’m answering it these days.

First Things First: What’s a JavaScript Application?

GMail is a JavaScript application; this Posterous blog, while it makes use of JavaScript a bit, is probably not, but the admin interface most certainly is. The line can be frustratingly fuzzy, but at the same time it’s pretty important to realize when you’ve crossed it.

To me, the defining characteristic of a JavaScript application is that the heavy lifting of manipulating and displaying data falls to the browser, with most communication with the server happening via XHR.

If you find yourself in application land, welcome. Now what?

My Building Blocks

My approach to organizing an application is really just an MVC variant, so I don’t want to sound as though I’ve discovered something novel or new. However, there are a couple of things to note: For one, the term “controller” has a couple of different meanings to me, as explained below; for two, there are two distinct flavors of “views,” though I’m not sure exactly how important the distinction is.

I also want to be super-clear that I don’t tout this as The One and Only Way; it is just a way that has worked for me, a way that I evolve and adapt with every project I work on, and a way that I’ve run by a few people and they haven’t laughed at me. My point in dissecting it isn’t that you’ll try to follow it word-for-word; rather, I hope it might get you thinking about JavaScript applications beyond the DOM.

Models

There’s nothing particularly unique to models in a JavaScript application. They are responsible for fetching and storing application data and maintaining its integrity in the browser. They fetch data, store data, and provide an API for other application components to get access to that data. There may be more than one destination for the data: memory, the server, or some type of local storage. But if it has to do with managing data, it’s the model’s job.

Models stay out of the way when it comes to displaying data or responding (at least directly) to user interaction. Those tasks are left up to other pieces of the application, as we’ll see below.

Example

A simple search application would likely have a search results model, responsible for receiving the current search term, fetching the data for the term, and broadcasting it to the rest of the application. It might also allow for manipulating individual search results, such as indicating that a particular result was a favorite or a dud, though that task might also fall to an individual search result model depending on the needs.

Widgets and Data Views

Views comprise HTML (generally in the form of client-side templates) and CSS for a component, and are generally accompanied by a view controller (the JavaScript related to interacting with the vew; see below for an explanation). The HTML for a view consists of a single parent node with an arbitrary internal structure; the parent node will optionally have one or more classes on it that can be used to target CSS.

There are two flavors of views, in my mind: widgets, which are responsible for supporting user interaction with the application but don’t render any application data; and data views that are responsible for displaying and allowing interaction with application data.

Data views are instantiated with the initial data required to populate them; then, their view controllers listen for messages from other pieces of the application to tell them when new or updated data needs to be rendered.

Examples

A basic search input box would be considered a widget — when it is created, it doesn’t need any application data in order to render properly. The widget is strictly responsible for allowing the basic interaction of typing a search term and hitting enter; that is, it’s not responsible for actually performing the search.

A search results list is an example of a data view; it renders application data and, potentially, allows for interaction with it. Again, though, it’s not responsible for performing the search; it just renders data and then allows for interaction with it.

View Controllers

View controllers manage interaction with a data view or widget — interaction by the user, and interaction with the rest of the application. They are responsible for binding and handling events, for broadcasting user interactions with the widget to the rest of the application, and for listening to other pieces of the application to tell them they have new data to render.

View controllers never handle server communication directly; their role is solely to provide a user interface to the application. When something interesting happens to a view or widget, the view controller announces it. When new data is available for a view, the view controller should know how to handle it. But, again, the view controller itself should focus on providing a user interface, not on interfacing with the server.

Examples

The view controller for a search box might listen for the user to focus on the search box, hiding placeholder text for the input. Then, it might listen for the user to hit Enter inside the search box; when that happens, it would broadcast to the rest of the application that the user had submitted a search, along with the term that was searched.

The view controller for a search results list might listen for another piece of the application to announce that new search results are available to be displayed. If the results were for the currently displayed search term, it could add them to the list; if they were for a new term, the results list could empty itself and display the new results.

Application-Level Controllers

Application-level controllers are the glue of an application. Loosely, there may be one per “page” of the application, or one per feature. For example, an application that includes a search feature and a checkout feature might have a controller for each feature, even though the checkout feature might spread across multiple pages.

These controllers are responsible for getting the models and views/widgets for a feature in place and talking to each other. So, a controller might first make sure the required models are in place, then tell them to fetch the appropriate data; once the data is available, the controller would instantiate the views for displaying the data. Finally, the controller would broker future interactions between the views and the models.

Examples

On a search results page, a user might click a Favorite button on a search result. The search results list’s view controller would handle the click, broadcasting a message about the user’s action to the rest of the application. The controller would observe this message and pass it, along with any other relevant information, to the search results model, which would in turn pass the information to the server, or store it locally.

Notes on Enablers

I’ve glossed over a few implementation details that are somewhat tangential to the organization question, but I want to touch on them briefly:

Pubsub and Friends

I didn’t want to get too specific about how all of this “announcing” and “broadcasting” and “listening” happens, because there are lots of ways to accomplish it. One could use pubsub, custom events, or any number of other solutions. I don’t think the actual implementation is important, though personally I lean heavily on pubsub — what is important is the notion of broadcasting and listening for announcements that something has happened, allowing other components of the application to react appropriately.

Templating

If you aren’t using a toolkit with templating built in (or, heck, even if you are), I’ve kind of fallen in love with mustache.js lately. It’s a great client-side templating companion, making it dead-simple to turn data into markup without ending up with templates that look more like JavaScript than HTML.

Figuring out how to maintain templates can be tricky — do you store them in your page’s markup? Do you maintain them as separate files requested via XHR and then cached? Or do you put them in your JavaScript? Dojo’s dojo.cache() method provides a handy way to keep your templates in separate files and load them via XHR, while interning them into your JavaScript for you if you use Dojo’s build tool. I like this.

Attaching Events to Views

Another shameless Dojo plug: dijit.\_Templated provides some serious hotness when it comes to attaching events to views. Read up on dojoAttachPoint and dojoAttachEvent; together with dijit._Widget’s connect and subscribe methods, which provide automatic cleanup for you, there’s some real power here, which has me writing hardly any selector-based code these days.

File Structure

I hesitate to make any particular recommendations here, because the needs of an application can vary widely. However, I tend to have a directory each for models, views (for view controllers and templates), and controllers (for application-level controllers). Those directories — especially the views directory — may contain subdirectories, for instance if there’s more than one view for a certain type of data.

Why Go To All This Trouble, Again?

So this is the part where you might say “OMG, srsly, what happened to ‘get some elements, do something with them?!?’” Let me be clear, that approach may be entirely appropriate for your particular needs; I’m not here to convince you otherwise.

But: if your application is complex enough to warrant considering an approach like this, I’ve found that in the long run it actually simplifies my code by cleanly separating concerns and providing a decent roadmap for building new features. I can build and test a solid model for some Thinger, and then use that model throughout my code; I can build and test a user interface component for editing a Thinger long before the data exists to support it. I can map “pages” of my application to application-level controllers, providing a high-level view of what’s happening where.

Best of all, paths to code reuse become clear and entanglements become fewer when I keep this division of responsibilities in mind as I code. A search results data view, for example, can be made to accept search results from any model that provides them in the proper format; a search can be initiated and the results displayed without depending on a user entering text into a search input widget.

Dividing the responsibilities into well-defined sections leads to components that are truly pluggable, often in ways you may not have even imagined when you wrote them. In an application that evolves over time, it’s hard to overstate the benefits of this.

In Conclusion & A Plea

If you find yourself working on a JavaScript application, I can’t recommend enough that you consider, at length, what underlying structure makes the most sense; it’s almost inevitably more complex than you can manage via the DOM alone. Again, my answer isn’t the right answer, it’s just an answer, but I hope it helps you start thinking about what the right answer might be for your project.

If you’re interested in this stuff, I’d encourage you to check out JavaScriptMVC, if only to see how they approach these problems; Cujo.js is another framework, built on top of Dojo, that aims to enter this space, but you’ll have to wait until mid-September to see it.

Finally: If you have your own thoughts to share about how to approach these large application questions, I’m pleading with you to write your own blog post(s) about them. If you have other reference material on the topic, I beg you to share it. As more and more people transition from simple JavaScript enhancements to non-trivial applications, the need for education is huge.

Filed under  //  javascript   large-applications  
Comments (8)
Posted

Dear conference organizer

Posted

Thank you for the invitation to speak at your conference, and your offer to pay my hotel and airfare in exchange. I notice that you are selling tickets for upwards of $1,500, in addition to hotel and travel costs. In exchange, you are hosting a three-day event, meals, a USB drive, a shirt, and a binder.

We need to talk, and not about the binder.

TXJS was a one-day conference with approximately a $30k budget -- some tickets cost a whopping $29, and the most expensive tickets were $109 -- and yet it managed to offer exactly what you're offering to me to its out-of-town speakers: hotel and travel. It had two nights of open-bar parties; a shuttle bus to get people to and from the amazing, eclectic, venue; catered breakfast and lunch; an open bar at the event itself; and a speaker and VIP brunch the following day. It also had some of the best JavaScript minds in the business on stage and in the audience, drawing people from around the country and beyond. 

I hope you can appreciate the disconnect here. It's not so much that I want to get paid -- I have gladly and eagerly spoken at lower-cost conferences without even getting my hotel compensated -- as the fact that I know now that conferences just don't have to cost that much. When they do, it's hard to get on board with lending my name for free when the conference stands to have revenue of hundreds of thousands of dollars and I know I only needed revenue tens of thousands to put on an amazing event. 

As a self-employed consultant, speaking at a conference has, at best, intangible benefit to me, and non-trivial costs. You and many other organizers can seem as though you assume the benefit is obvious, and the costs negligible. In fact agreeing to a speaking engagement -- one that requires two non-working days for travel, a non-working day for speaking, and whatever prep time is required to actually put together my talk -- is a tough decision regardless of how much tickets cost, especially for someone without corporate backing or a product to promote. Call me selfish, but I need to be very clear that there's a benefit to me. A high-dollar conference likely to attract almost exclusively corporate-backed attendees with at best a peripheral relationship to my area of expertise is exactly the kind of conference where it's not clear what the benefit will be. Being away from home and work for three days, even in a nice hotel, just doesn't count. 

--

Finally: To all of the TXJS speakers, we heart you unbelievably and stand in awe that when we asked you to present at an event that had never existed before, each and every one of you said yes without batting an eye. I hope you know that we know that even with the open bar and the parties and the shuttle and the venue and all of it, we couldn't have had a conference without you. 

Filed under  //  conferences  
Comments (9)
Posted

Scaffolding a Buildable Dojo Application

Posted

Note: I’ve created a GitHub repo that served as a sort of scratch pad for this article; you may want to clone it and follow along. If you do, though, please note that it does NOT include the Dojo downloads; you’ll need to download the files indicated in Step 1 and then place the extracted directories as indicated in Step 2 if you want any of the build stuff to work.

One of the most confusing things about using a toolkit like Dojo can be getting it set up properly to take advantage of all of its features, such as its package management system. Sure, you can use a CDN-hosted version of the toolkit, but one of the major reasons I like Dojo is because of the dependency management and build tools it offers, and it’s difficult to really take advantage of these when you’re only using the library from the CDN.

If you skip the build step, Dojo’s modular nature means you’ll end up downloading a whole lot of files, and making a whole lot of HTTP requests in the process. It’s fine for development and general messing around, or if your project just requires a few files, but for the kinds of projects where I think Dojo really shines, you’re going to want to do your own build.

My goal in this article is to show how I set up my files to make this easier. This is by no means the only way to do it, nor even necessarily a best way to do it — just one way to do it that you can hopefully follow along with to get you started.

Step 1: Getting Dojo

I like to download two different Dojo packages:

I generally don’t maintain these files in a project’s version control system, but I do document that downloading and extracting them is part of setting up a new development instance. Your exact strategy here may vary, but I do encourage at least the JavaScript developers on a project to have both on hand. The former will be used during development; the latter will be used for reference, and to perform the build step.

Step 2: Where Do I Put It?

For the sake of this article, let’s assume that all of your public, static files are served from a directory www/. Here’s how I might structure things from there:

  • www/
    • dojo-toolkit/ (extracted from toolkit release)
    • dojo-sdk/ (extracted from SDK release)
    • myApp/

Easy enough. Next, we need to point to Dojo in our HTML.

<script src="dojo-toolkit/dojo/dojo.js"></script>

At this point, we could start writing JavaScript that uses Dojo anywhere after that script tag, but that wouldn’t be very exciting, smart, or sustainable. Instead, let’s create an initial JavaScript file in our myApp/ directory, called base.js.

dojo.provide('myApp.base');

console.log('you found me!');

We could just include this file in our page using a script tag, but again, not very sustainable — next thing you know we’ll have 30 script tags in our page, and the whole point here was to take advantage of dependency management and building, right? We can tell Dojo to find and load our file for us, but first, we have to give Dojo a hint as to where to look.

Step 3: Pointing Dojo in the Right Direction

The one “rule” imposed upon you by using Dojo’s package system is a simple mapping of dot-notation strings to file locations. If you say dojo.require("a.b.c.d.e.f.g"), Dojo will go looking for a file at a/b/c/d/e/f/g.js. By default, Dojo assumes that the directory a/ is a sibling of the directory that contains dojo.js, but you can adjust the location of any namespace.

There are a few different ways to do this; my personal preference is to declare the global djConfig variable, but any of the ways mentioned at the link above is obviously valid. If you go the djConfig variable route, just be sure to declare the variable before you include dojo.js in your page if you want Dojo to pay attention to it.

Here’s what it would look like for our little app:

<script>
var djConfig = {
  modulePaths : {
    'myApp' : '../../myApp'
  }
};
</script>

What’s up with the ../../ stuff? As mentioned, Dojo by default assumes that any namespace, such as our myApp namespace, is a sibling of the dojo/ directory by default, just like dijit/ and dojox/ are. Problem is, our myApp/ directory doesn’t follow this convention. Namespace locations are specified relative to the location of dojo.js itself — in our case, dojo.js is located at dojo-toolkit/dojo/dojo.js, and so relative to that, our myApp/ directory is exactly where we said it was: at ../../myApp.

Why don’t we just make the myApp/ directory a sibling of the dojo/ directory, and skip this whole part? We could, but keeping it external to Dojo itself makes managing those Dojo downloads — especially within a version control system — a whole lot easier. This way, we can keep our application code entirely separate from Dojo, and add or remove or upgrade Dojo at will without having to worry about our application files. This makes me happy; if it doesn’t make you happy, feel free to put your application code wherever you’d like :)

Step 4: Hooking it Up

Now that Dojo knows where to find our stuff, we can tell it to start looking. We’ve already included Dojo on our page. Now, instead of adding more script tags to our HTML, we can start to take advantage of Dojo’s dependency management system:

<script>
dojo.require('myApp.base');
</script>

This is the only other script tag you’re going to need to add to your page; from here on out, everything can be managed inside the JavaScript itself.

Step 5: Writing Your App

This step is mostly up to you. Dojo provides great tools for building large applications — a solid base library with CSS-based node selection, inheritance, pubsub, effects, events, and various other hotness — and it also has a great UI toolkit in Dijit, including abstracted base UI functionality in dijit._Widget and dijit._Templated. It provides patterns, but no real prescriptions, so I can’t show you how to build a great Dojo app in three paragraphs or less.

However, let’s talk a bit more about this dependency management stuff, because by the end of this article, I want you to be able to leverage that dependency management stuff to create production-ready files, regardless of how you build your app.

So, we have our myApp/base.js file, and so far it contains this:

dojo.provide('myApp.base');

console.log('you found me!');

Let’s assume our app does more than log to the console:

dojo.provide('myApp.base');

// specify the file's dependencies
dojo.require('myApp.Settings');
dojo.require('myApp.Thinger');
dojo.require('myApp.Doohickey');

// run code when the dependencies are met
// and the DOM is ready
dojo.ready(function() {
  if (myApp.Settings.isThisThingOn) {
    new myApp.Thinger({ things : [ 'a', 'b', 'c' ]})
      .placeAt(dojo.body());
    new myApp.Doohickey({ awesome : true })
      .placeAt(dojo.body());
  }
});

Now we’ve said, in code, that our app requires three other pieces: Settings, Thinger, and Doohickey. Dojo looks at those dojo.require() statements, and decides that it’s going to try to fetch myApp/Settings.js, myApp/Thinger.js and myapp/Doohickey.js to satisfy those dependencies. Once it finds them, we can use them.

The details of our application here aren’t super-important (in fact they’re entirely made-up); what’s important is to understand that we’ve specified a dependency, and once it is met, some other code runs.

Chances are that some of our modules have some dependencies of their own. Let’s take a look at the make-believe contents of myApp/Thinger.js:

dojo.provide('myApp.Thinger');

dojo.require('dijit._Widget');
dojo.require('dijit._Templated');

dojo.declare('myApp.Thinger', [ dijit._Widget, dijit._Templated ], {
  // template for Thinger
  templateString : '<ul></ul>',

  // what to do after a new Thinger 
  // is on the page
  postCreate : function() {
    // create a new list item for each item in things
    dojo.forEach(this.things, function(thing) {
      dojo.place('<li>' + thing + '</li>', this.domNode, 'last');
    }, this);
  }
});

Why look! We’ve specified yet more dependencies while keeping our code modular and abstracting away common pieces of behavior. Nifty, eh? Meanwhile, our HTML still only has the single dojo.require() statement in it, because all of our dependencies are expressed in our code, where they belong.

Step 6: Build It

We’ve seen how to express these dependencies, but if you’ve been paying attention to Firebug or your development tool of choice, you’ve noticed there are still a ton of HTTP requests happening. What, exactly, have we gained here?

Remember how we downloaded that 19mb SDK? Besides the fact that it has uncompressed, commented versions of every single Dojo file, it also includes the build tool, and now we’re going to put it to use.

The commands for running a build are, sadly, not something I keep in my brain, so I like to make a tiny shell script in my application directory that looks like this:

cd ../dojo-sdk/util/buildscripts
./build.sh profileFile=../../../myApp/myApp.profile.js releaseDir=../../../release

Could this be prettier? Probably, but it does the trick. It puts us in the buildscripts directory, then runs the build.sh file that’s there with the provided arguments: the location of the profile file for the build, and the location we’d like the build to end up. Both of these locations are relative to the dojo-sdk/util/buildscripts/build.sh file, so keep that in mind if you decided you didn’t like how I organized my files and did something different.

Now all we have to do is create that profile file. You can look in dojo-sdk/util/buildscripts/profiles/ for some examples, but here’s a good start:

dependencies = {
  stripConsole : 'all',
  action : 'clean,release',
  optimize : 'shrinksafe',
  releaseName : 'myApp',
  localeList : 'en-us',

  layers: [
    {
      name: "../myApp/base.js",
      resourceName : "myApp.base",
      dependencies: [
        "myApp.base"
      ]
    }
  ],

  prefixes: [
    [ "dijit", "../dijit" ], 
    [ "dojox", "../dojox" ],
    [ "myApp", "../../myApp" ]
  ]
}

OK, seriously, what is up with these relative paths? I will tell you right now that the first time I saw this it was a complete mindf*ck, and the only way I can put together a build for a new project in 10 minutes or less is by referring to a profile file for an old project (which is part of the reason I’m writing this post). Let’s take a look at what’s going on here, though:

  • The dependencies object is the top level object describing the profile. Initially, you can set all of the command-line arguments here as members, e.g.: stripConsole to remove any console.* calls in code, a required action parameter, and so on. (Note that we could also specify the releaseDir argument here if we wanted; I tend to keep it in the shell script, but not for a particularly good reason.)
  • The layers array contains a list of all of the production-ready “layers” you want the build to create. In this case, we’re telling the build to create one layer that contains all of our application code (in a minute we’ll look at how to specify multiple layers for serious hotness).
    • The name property of each layer tells the build tool where the created layer should be placed relative to dojo.js in the destination directory.
    • The resourceName property tells the build tool how we plan to refer to this layer in our code. For the sake of this article, we’re going to refer to it in our code exactly how we’re already referring to it in our code.
    • The dependencies property is an array of modules that this layer depends on. In this case, the layer sort of depends on itself, causing the build to physically replace the original myApp/base.js file with the result of all the dependencies.
  • The prefixes array is sort of like the modulePaths specification in djConfig, which makes sense because the build tool can’t see the djConfig settings. It tells the build tool where it’s going to find files for a given namespace. Any namespace that you use in your code needs to be specified here. If you aren’t using any DojoX modules, feel free to omit the “dojox” prefix to avoid copying the unused code into your built tree.

So! Let’s cd into our myApp/ directory, where we’ve created our little build script and myApp.profile.js, and run that build script.

[ … about 30 seconds later … ]

Sweet. Now we have a release/ directory next to our myApp/ directory, and it looks like this:

  • release
    • myApp
      • dijit
      • dojo
      • dojox
      • myApp

We can see exactly what the build did by looking at release/myApp/dojo/build.txt — it shows that it created a dojo.js file for us, as well as the myApp/base.js file we asked it to create, complete with all of our dependencies.

Now, to use our built files, we need to do two things:

  • Remove our djConfig settings; they’re not necessary now that the built myApp/ is a sibling of the dojo/ directory.
  • Change the script tag in our HTML that includes dojo.js to point at release/myApp/dojo/dojo.js.

We can do this by hand, but once we have this working, we’d be smart to do it with some sort of switch in our application that decides whether we’re in development or production mode and outputs the right code accordingly.

Step 7: Building it Better

Imagine, if you will, that the myApp.Doohickey module is only needed on one page of your application, and that page is used rarely. Not only that, the module has a ton of dependencies that aren’t shared by any other modules, and it’s seriously stupid to be taking the time to load all that code when it’s unlikely it’s going to be needed. Our current profile file doesn’t make any allowances for this, but two adjustments can separate this functionality from the built files of the core application.

First, let’s give the directive in the profile file to build a separate layer for myApp.Doohickey. It won’t work without myApp.base, so we specify that as a layer dependency:

dependencies = {
  stripConsole : 'all',
  action : 'clean,release',
  optimize : 'shrinksafe',
  releaseName : 'myApp',
  localeList : 'en-us',

  layers: [
    {
      name: "../myApp/base.js",
      resourceName : "myApp.base",
      dependencies: [
        "myApp.base"
      ]
    },

    {
      name: "../myApp/Doohickey.js",
      resourceName : "myApp.Doohickey",
      dependencies : [
        "myApp.Doohickey"
      ],
      layerDependencies : [
        "../myApp/base.js"
      ]
    }
  ],

  prefixes: [
          [ "dijit", "../dijit" ], 
          [ "dojox", "../dojox" ],
          [ "myApp", "../../myApp" ]
  ]
}

Second, we need to make sure the build tool doesn’t see dojo.require('myApp.Doohickey') and decide it needs to include it in the base layer. This may seem a little weird, but the build tool basically just parses files to figure out which other files they require, and it does this by looking for dojo.require() statements. Hiding the Doohickey requirement from the build tool while keeping it in place for development is easy:

dojo.provide('myApp.base');

dojo.require('myApp.Settings');
dojo.require('myApp.Thinger');

dojo.addOnLoad(function() {
  new myApp.Thinger({ things : [ 'a', 'b', 'c' ]})
    .placeAt(dojo.body());
});

if (pageNeedsDoohickey) {
  dojo["require"]('myApp.Doohickey');
  dojo.addOnLoad(function() {
    new myApp.Doohickey({ awesome : true })
      .placeAt(dojo.body());
  });
}

Step 8

There is no step 8. There is step 1 through 7, which is way more than “put this script tag (and 20 others!) on your page and GO”, I’ll give you that. But take heart: not long ago, these steps had me pulling my hair out, and now I can write a blog post about them (with some much-appreciated review from Ken and Pete). I hope they help you understand the steps you’d take to start taking advantage of the power of Dojo’s package system, and I promise that once you’ve done it once or twice, it won’t seem like the roadblock it does at first, especially because the payoff in the long run is huge.

Further Reading

Comments (5)
Posted