Wednesday, 4 April 2012

Announcing Lineage

Note: As of late 2015, I don't use Lineage anymore, there's no need. I use the class features of ES2015 (aka "ES6") instead. If my target is a browser, for now I transpile with Babel (no need when working in NodeJS, the latest Node uses the latest V8 which has solid support for ES2015).

Just a brief post to announce my latest mini-project, Lineage. It's a small, simple toolkit for creating JavaScript constructor functions and their prototypes ("classes," if you will) in a straight-forward and concise way. From the project home page:

  • Lineage's API lets you define prototypes with a very concise syntax, while still encouraging you to create functions with real names (rather than anonymous functions); this helps your tools help you (debuggers show function names in call stacks, for example).
  • Lineage provides a highly efficient mechanism for "supercalls" (calling into the parent prototype's versions of methods from an instance using a derived prototype).
  • Lineage's API encourages and supports use of the module pattern for each constructor and its prototype.
  • Lineage is small, <3k compressed (gzips to <1,500 bytes, a quarter of which is the MIT license) — because it doesn't try to reinvent inheritance, it just simplifies access to the power of JavaScript's prototypical inheritance.

Here's an example of defining a constructor called Thing with a spiffy function on the prototype:

var Thing = Lineage.define(function(p) {
p.spiffy = function() {
console.log("I'm a spiffy thing!");
};
});

...or if like me you prefer your functions to have names:

var Thing = Lineage.define("Thing", function(p) {
p.spiffy = Thing_spiffy;
function Thing_spiffy() {
console.log("I'm a spiffy thing!");
}
});

Now, with such a trivial example, that doesn't offer you much on top of the raw equivalent:

var Thing = (function() {
function Thing() {
}
Thing.prototype.spiffy = Thing_spiffy;
function Thing_spiffy() {
console.log("I'm a spiffy thing!");
}
return Thing;
})();

...and that's the point, Lineage works with JavaScript's natural inheritance, it doesn't try to reinvent things. But when you get into inheritance, and in particular start making supercalls, Lineage reduces the code markedly while ensuring everything is hooked up properly, and does so in a way that's easy to use correctly, without retyping a lot of boilerplate code.

Rather than loading up this post with code examples, I'll point you to the progressive series of examples on the Lineage Comparison with Plain JavaScript page for more.

I'm using Lineage in projects now, and so far I'm really liking the simplicity of it. I hope you will too! If you do play with it, please send comments (positive and negative!), the feedback is very welcome.

Happy coding!

Tuesday, 6 March 2012

Adding language choice to FogBugz's code snippets

Surprisingly, the "code snippet" widget used by the FogBugz wiki feature doesn't support telling the pretty-printer (they're using Google's google-code-prettify script) what language the text is in. Since the script can't always tell what language the code is in, this is a problem. And apparently, I'm the first one to ask about this. Wow.

The answer is to write a BugMonkey script and install it in your FogBugz installation. Here's my first take at it, dashed off fairly quickly but apparently functional:

name:          Add language support to code snippet pretty-printing in FogBugz
description: Fixes code snipeet pretty-printing in FogBugz by adding the ability to specify a language.
author: T.J. Crowder [tj at crowder software dot com]
version: 1.0.0.0

js:

// Written by T.J. Crowder [tj at crowder software dot com]
// Licensed under the Creative Commons Attribution License 2.0 (UK)
// http://creativecommons.org/licenses/by/2.0/uk/
//
// At the beginning of each code snippet, you can optionally include a line
// defining the language, in the form:
//
// lang_xyz:
//
// This must be the first line.
//
// The script below will find these, extract the "xyz" from it, remove it and
// any line break following it, and add "lang-xyz" to the `pre` element.
// If any matches were found, when done `prettyPrint` is called to reformat
// the elements.
//
// Example:
//
// lang_sql:
// -- A comment
// CREATE TABLE [Foo] (
// [Bar] NVARCHAR(MAX)
// )
//
// ...renders without the first line, with the class "lang-sql" on the element
// so the pretty-printer knows what the language is.
//
// This code may be fairly fragile, depending on the precise workings of the pretty
// printer. It would be better BY FAR if FogBugz updated the code snippet widget to
// support specifying the language.
//
// Many thanks to Ben McCormack and Michel de Ruiter for pointing me in the right
// direction here: http://fogbugz.stackexchange.com/questions/10065
(function($) {
// Our handler
function handlePrettyLanguages() {
var changes = false;
$("pre.prettyprint").each(function() {
var pre, firstElement, span, match, lang, nextpun, nextbr;

firstElement = this.firstChild;
while (firstElement && firstElement.nodeType !== 1) {
firstElement = firstElement.nextSibling;
}
if (firstElement && firstElement.tagName === 'SPAN') {
pre = $(this);
span = $(firstElement);
match = /^\s*lang_([A-Za-z0-9_]+)\s*$/.exec(span.text());
if (match && match[1]) {
lang = match[1];
nextpun = span.next("span.pun");
if (!nextpun[0]) {
nextpun = span;
}
nextbr = nextpun.next("br");
if (!nextbr[0]) {
nextbr = nextpun.next().children().first();
if (nextbr[0] && nextbr[0].tagName !== "BR") {
nextbr = $();
}
}
pre.addClass("lang-" + lang);
span.remove();
if (nextpun !== span) {
nextpun.remove();
}
nextbr.remove();
changes = true;
}
}
});
if (changes) {
prettyPrint();
}
}

// Hook it up on page ready and when BugViewChange events occur
$(handlePrettyLanguages);
$(window).on('BugViewChange', handlePrettyLanguages);
})(jQuery);

You install that via My Settings | Customizations.

Happy pretty printing!

Thursday, 1 March 2012

Match everything...except!

Micro-post:

I was truly shocked to find today that in JavaScript regular expressions, . (the decimal point) doesn't do what I thought it did. I thought . meant "match any character." You too? Yeah. But it doesn't. Specifically, . doesn't match line terminators (so, \r, \n, \u2028, and \u2029). From Section 15.10.2.8:

The production Atom :: . evaluates as follows:

  1. Let A be the set of all characters except LineTerminator.
  2. Call CharacterSetMatcher(A, false) and return its Matcher result.

...which if you spend really quite a long time looking tells you that . matches anything but line terminators.

Maybe I'm just parading my ignorance here, but I would have thought that absent the "multiline" flag or something, . matched everything. Nope. If you want to do that, use [\s\S] (e.g., everything that either is or isn't whitespace).

Happy coding!

Tuesday, 28 February 2012

Creating a function with a true name defined at runtime

I used to have an article here showing how to dynamically create functions with true names. Except it didn't work properly on Firefox and there's a much better way (which does work properly on Firefox) described in this article by Marcos Cáceres. It looks like this:

var name = "foo"; 
var func = new Function(
     "return function " + name + "(){ alert('sweet!')}"
)();

Yes, the Function constructor is basically a call to eval, so you'd only do this with input you control well. But when you need to do it, this is how.

Nice one, Marcos!

Sunday, 26 February 2012

Google Code - Error retrieving directory contents

I dropped by one of my Google Code projects, a trivial little jQuery placeholder functionality shim called place5, and was quite surprised to see, on the Source tab, the message "Error retrieving directory contents". Now, I have backups (and you do too, of anything you host externally, right?), so I wasn't panicked or anything, but I was still...jarred.

Some searching around revealed that people get this when they've changed their source control system (e.g., from mercurial to git, or subversion to mercurial, etc.). I hadn't changed my source control system, but looking at the Source tab again I saw that it was showing an empty git tree (with the error), and I thought: "Was I using git for this?"

Answer: No, I wasn't. I'd been using subversion. So what the...? So off to Administer, and under Source | Repository Type it said "default" (which appears to [now] be git). Setting it back to subversion corrected the error and restored source browsing.

I mention this for two reasons: Firstly, I figure I won't be the only one, so I thought I'd write this down in case someone runs into the same problem and finds this post. But secondly, it highlights a general principle: It's very useful to have your settings system have the concept of a "default" choice, so if you update defaults, people start seeing the new thing. But when you update a default that's going to break things for people, it's pretty important to change the setting for anyone using "default" to the new, specific choice for the old default. Because breaking things unnecessarily is doubleplus ungood, ya know?

Tuesday, 14 February 2012

Getting rid of "What's Hot"

Google have finally made it possible to get the "What's Hot" rubbish out of your main Google+ stream. Here's how:

  1. Go to Google+.
  2. Click "What's Hot" on the left-hand side.
  3. At the top of the page, there's a subtle little slider (basically an O on a line). Move it all the way to the left.

(If you don't see a slider at the top of your What's Hot page, just wait a day or two for the rollout to come to a server near you.)

That's it, you're done. Yay!

Friday, 10 February 2012

`forEach` and runtime cost

Just a tiny one today, mostly to write this down somewhere:

As you all know, ECMAScript5 adds forEach to arrays, where you supply a function that gets called for each element in the array. There are lots of benefits to this, not least variable scoping on the index and value variables and a bit less typing, but don't all of those function calls add up to a significant runtime cost?

No, they don't.

I got curious about it (I have a tendency to micro-optimize, which I'm trying to break myself of), so I tested it on the slowest (desktop) browser currently in use: IE6. Specifically, IE6 running in an old Windows 2000 VM I have lying around. I tested the performance of looping through a 100-entry array both with and without calling a function. Without the function call, IE6 looped through the array ~4,500 times in a second (that's 450,000 loop iterations/second). With the function, it managed ~2,000 times a second (200,000 loop iterations/second). So while that's a big relative difference (56% slower!), in real terms it falls into fergedaboudit territory: 2.78 microseconds of overhead. You heard, me, microseconds. That's 0.00278 milliseconds.

Now, I'm not going to say there aren't places where it could matter. I am going to say that they're going to be extraordinarily rare, I'd wager I'll never run into a situation where it makes a difference and nor will you. Whatever else you're doing in the loop body is totally going to wash out the function calls. Really.

Happy coding!