isn't quite ashamed enough to present

jr conlin's ink stained banana

:: Checking Your Toolbox

i'll admit it. i'm actually rather impressed by the work going into node.js. There are new libraries and methodologies rolling out every week or so that address some basic shortcoming in a clever and interesting way.

Granted, there's no way in hell that i'd use only it to build a large scale, maintainable system, but i'm still impressed.

Now, before you angrily click on the comment link to write your screed about how i could be so blindingly ignorant, let me just say the following: "If you only use Javascript and Node.js to build a large scale system, you suck as an engineer."

There, that should help, shouldn't it?

Allow me to explain using something that's imposing itself on me. Recently, my wife (and to a slightly lesser extent, i) decided to have our bathrooms redone. i live in a 50+ year old ranch that was originally meant to be a "starter" home. That means it was built reasonably quicky and cheaply following one of four floorplans that probably took some architectural student a weekend to craft. There are some shortcomings that we've been addressing, but by and large, we're still very happy with the house.

It also means that we've hired construction folks to come in and work their various crafts in order to make an attractive, usable set of features that will last at least another 50 years without the same incidents i've already repaired.

In order to perform those destruction/construction tasks, they've brought in a number of tools. Some tools have served multiple purposes (like the impact drill doubles as a screwdriver and socket driver), others are fairly tightly constrained (like the tile cutter which only wet cuts tile). There has been some duplication of tools (hand saws vs. sawsall), and some tools were used in a somewhat disposable way. The project could have used just one set of tools and it might have worked, but somehow i don't believe it would have been terribly efficient to use a Dremel for everything.

That's kind of how i view programming. Each system has it's benefits and issues. Javascript is great because it is reasonably light weight and forces you to think in terms of small, cascading tasks. Python offers a fantastic glue for various libraries while also providing reasonably performant dynamic scripting. Java is godawful fast for threaded handling of IO. Perl is fabulous for handling text content and PHP is nifty as a simple view template language.

All of these tools can do more than that. Some folks have built incredibly complex solutions based off of using just one of these tools and other folks hate them for it. In practice.

Recently, i built a weekend goofball project that uses PHP as an API and View, Javascript as a client language and Sqlite as the data store. i'd have preferred using Python as the API, but my hosting provider makes that a pain in the butt, so i went the easier route for now. Eventually, i'll probably swap the API out for a python lib because that itch will be too big to scratch. If this project were to suddenly grow huge with tremendous demand, i can profile the slow bits and rework things without having to rewrite all of it.

i could have written it all in Javascript using Node.js on the backend and DHTML on the client, but… why? In order to do that, i'd have to set up a lot of extra things i don't need, fight a lot of issues i didn't have, and work on a lot of things that are not "build the goofball project".

i write code to solve problems. There are some that write code to answer challenges, and good for them. i try to keep up with the challenge folks because they may discover a new approach that works better than what i may know of, but generally, they don't. i'd like to think that occasionally the challenge folks might be interested in the "problem" side, but i know better than to demand that.

A good engineer knows what to do to keep an engine running. A good designer knows how to make something new. Both have their places, but know which place you feel most compelled by, and which one will get you paid.

:: Goodbye Note

One of the folks i work with is saying goodbye. It happens from time to time, and in this era of self-publication, he posted his reasons on his blog a while ago. It's rather charming and i know how he feels, because i felt the same way.

Then i realized the truth.

One of the comments that always makes me giggle a little are statements like "i'm afraid that i'm going to wake up in X years and realize i've been doing the same job and still haven't accomplished anything of lasting value." i can tell you that after nearly thirty years, i've done three different jobs and haven't accomplished anything of lasting value. i've done some impressive things, worked on a lot of cool projects, and i'd like to believe i've made the world a better place. None of this, however is of any lasting value.

i will be dead in less than fifty years. Within another decade or so i'd expect only a handful of individuals will remember my name. i have no legacy to carry my memory, nor fame or fortune to impress those that come after. i have yet to discover a cure for cancer, faster than light travel, or a guaranteed method to get a parking spot in San Francisco. No continent bears my name, nor insect, nor disease, nor truism. i will take my place in history next to the likes of Mynard Green Foot who raised sheep in the 11th Century and exists today purely in my depression fueled imagination.

And i'm ok with that.

Turns out that one can go quite mad when one strives to beat out the other eight billion currently living human beings, the previous 7 billion or so that walked before, and the potential trillions to come. So i strive to make things better for the few lives that i can. It's a more manageable goal that doesn't end in a nuclear arms race or divorce court.

i've come to realize that progress comes through reimplementing ideas, but in a slightly better form. This is also called evolution, and it has an impressively long history. It's also one of the reasons i laugh at companies that forbid anyone from examining what they're doing. They're setting up their code as an evolutionary dead end.

i take joy from little things. i have a career i'm happy with, working for a company that is doing good things and even have the ability to make little improvements for someone else. Perhaps something i do will be evolved into something that will change the world for the better. Then again, there's an equal chance that something i do will lead to the complete sterilization of the planet, but then i wasn't the guy that said "You know, i think we need a moon."

It's nice to see vibrant optimism in someone, even if it means that they've not had the crushing wave of disappointment and bitter resolution to carry on quite yet. It gives me hope for the future.

The one full of mild depression, sad realizations, and meager moments of joy.

And the occasional divorce inspired, nuclear planet sterilizations.

:: Idiots Guide to Mozilla Sync API

i've been informed that Sync is due for a change and that what you read here may not be outdated in a few months. (grumble.)
Be forewarned.

Recently, i needed a way to sync data for my Firefox add-on between clients. Fortunately, there's a nifty way to do that built into the current versions of Firefox called Sync. Unfortunately, while the documentation is great if you want to learn how sync operates, it's kinda poop if you want to quickly get started using Sync.

Well, "Some see problems, others see opportunities" so as my first go to fix that problem, i'm going to write down how i got Sync working.

The Guts

Ok, before we start delving into code, let's take a moment to talk about what's going on. Sync is a tool that securely exchanges data between two clients. That means you can use it to exchange data between anything you own that can run Firefox. (There are a couple of other browsers out there that can also play along, but for simplicity, let's stick to just firefox for now). It does this by stuffing your data into a well known chunk (marshalling), encrypting it, and sending it via a well known server.

Note, that the server isn't doing much more than relaying your data. That's because it's encrypted and can't really do anything with it right now. (Mozilla is working on making a more "durable" storage tool, but it's not there yet. This is why Mozilla STRONGLY RECOMMENDS you don't think of Sync as a backup service. It is, kind of like the shelf at the ATM is somewhere you can put your wallet. It's useful while the main bit is used, but not a really good long term idea.

Ok, so we've got records that are being exchanged. Those records have a little bit of information associated with them that's useful for syncing, but otherwise they're very simple. Those records go into a "Storage" object that just collects and manages the records, are watched by a "Tracker", and the storage elements get exchanged by an "Engine" that does the actual exchange. The engine watches both for local changes and remote requests for updates. It's worth noting that the exchange isn't instant, but near enough for most usages (e.g. within a few seconds).

Got it? The Tracker notices a change, and asks the Storage element to update the associated Records, then calls the Engine to deliver them.

There, now you understand Sync.

Spelunking

Now that you get Sync (from a tool point of view, at least), i can point out where some of the code lives. You don't need to look at this, but it might be useful if you like to play along at home.
First off, here's where the sync code lives. Most of what you want is in the sync/modules branch.

If you want to see the code i built, you can find the latest version here.

The ground up…

The addon i'm modifying is built off of the Addons SDK. The nice thing is that this means the addon is restartless, the bad thing is that it means i have to do some extra work in order to get things running. In this case, i need to call into Mozilla Core code. To do that, though, you just need to call:

const {Cu} = require('chrome'); // get the Components.utils hook
Cu.import('resource://services-sync/engines.js');
Cu.import('resource://services-sync/record.js');
Cu.import('resource://services-sync/main.js');
Cu.import('resource://services-sync/util.js');

This automagically drags a host of objects into the current namespace. Chances are, if you're wondering where an object is defined, it's within one of these files.

Now, let's work from the ground up, that means building a Record object. Like i said before, a Record is just a marshalling container. The only real restriction is that it should be JSON storable (so fancy JS pointer hacks or methods are not really a good idea).

So, something simple:
function FooRecord(moduleName, recordId) {
CryptoWrapper.call(this, moduleName, recordId);
}
FooRecord.protoType = {
__proto__: CryptoWrapper.prototype; // subclass from CryptoWrapper
_logName: "Record.FNCrypto; // What to use for the log messages
};
Utils.deferGetSet(FooRecord, "cleartext", ["value"]);

As you can see, this subclasses CryptoWrapper (via janky JS subclassing), and then calls a utility function to autobuild the Getter/Setter method, which will store FooRecord.value into "this.cleartext.value". Ok, you probably didn't see that. You'll have to take my word for it. Now, if you had a very complex item where you may not want to exchange every bit of info, you could define a bunch of items in that (where "value" is, and probably with a bit more descriptive labels). This way, you'd send just the bits you need so that things are pleasantly zippy. Since my records are small and not really something you'd want to split up anyway, i opted for a single value.

Now that we have a Record, we need something that can hold them. Let's define the Store. In many respects, this is another Controller layer, in that it calls to whatever you're using to actually store your data (the Model). In this case, think of DB as the model store. (DB is the persistent storage that is available to Add-ons, and is pretty cool too.)


function FooStore(moduleName) {
Store.call(this, moduleName);
}
FNSyncStore.prototype = {
__proto__: Store.prototype,
self: this,
itemExists: function(recordId) {
return DB[recordId] != undefined;
},
createRecord: function(recordId, moduleName) {
var record = new FNSyncRecord(moduleName, recordId);
if(DB[recordId]) {
/* Again, if we had multiple fields, make sure you set them here.
*/
record.keyBundle = DB[recordId];
return record;
}
return undefined;
},
changeItemID: function(oldId, newId) {
DB[newId] = DB[oldId];
delete DB[oldId];
},
getAllIDs: function() {
/* It's important that this return an Object (a Dict/Hash)*/
var recordIds = {};
for (var key in DB) {
/* Only return keys that are actually pointing to values.
* DB is a JS object, so there can be all kinds of cruft in there.
*/
if (key.indexOf('key:') === 0) {
/* The value stored is arbitrary. Only the key name is important.
*/
recordIds[key]=true;
}
}
return recordIds;
},
wipe: function() {
for (var i in self.getAllIDs()){
delete DB[i];
}
},
/* These are meta function calls to normalize data sets
* between this machine and some other.
*/
create: function(record) {
DB[record.id] = record.payload;
},
update: function(record) {
DB[record.id] = record.payload;
},
remove: function(record) {
delete DB[record.id];
}
}

Again, very simple, but a few caveats in there. One thing that can be confusing is that "createRecord" is different than "create". Create Record is more of a "write" function, in that it's called when a record has changed and it needs to be written out to some other device. "Create" is the opposite, that's where a new record needs to be imported onto the local machine. That's pretty much the role of Store.

Ok, we've got the transfer record, we've got the Storage/controller stuff, now we need to look at the Tracker to spot local changes. That's the role of Tracker.
function FNSyncTracker(moduleName){
Tracker.call(this, moduleName);
trackerInstance = this;
}
FNSyncTracker.prototype = {
__proto__: Tracker.prototype,
track: function(recordId){
/* Add the record to the list of items that have changed.
*/
this.addChangedID(recordId);
/* Any dirty records need to be propagated as soon as possible.
* thus the immediate "100"
*/
this.score = 100;
}
}

Really. That's about it. Basically, once data has changed on your side, you indicate to the Tracker "Hey, this changed!". You'll also want to set the score for how important it is to sync this data (0 == ignore this, 100 == OMFG THIS NEEDS TO BE DONE LIKE AN HOUR AGO!). You can set observers to help do this, or just call directly. i took the second option because i know exactly when i need to update and it's kind of important to do it after new record creation.

And finally, we're at the top level, the Engine:

function FooEngine() {
// Defining "moduleName" here so that it's easy to figure out where
// it comes from. This is case insensitive.
var moduleName = "Foo";
Weave.SyncEngine.call(this, moduleName);
// turn the engine on
this.enabled = true;
}
FNSyncEngine.prototype = {
__proto__: Weave.SyncEngine.prototype,
_storeObj: FooStore,
_recordObj: FooRecord,
_trackerObj: FooTracker,
version: 1
};

Hey, so that's where the Module name comes from! Module name is used for a number of things, including logging and tracking. Unfortunately, it doesn't show up on the list of active sync engines on the options panel, yet. It does mean that it has to be a fairly unique, yet human readable string, so "Foo" is probably sub-optimal. The other thing you'll need to do is make sure that the engine is enabled (this caught me hard).

You'll also need to define links to the objects you've built, and the version you're using.

And, you're done!

That's it. Granted, you'll want to test this, and debug it, and all the other nonsense, but from a code sense, that's all you'll really need.

By the way, since i mentioned debugging, i want to chime in on the built in tools for Aurora and Nightly. Sadly, there's still no step-debugger, but here's a stupid trick that is INCREDIBLY useful:

  1. go to about:config
  2. Open a Web Console [Ctrl+Shift+K]

You now have a web console that can run Javascript with the Browser Chrome. That means that you can run things like Components.util.import('resource://services-sync/utils.js'); Utils.sha1('foo'); and get a pretty SHA1 hash of foo on your screen. And yeah, you can do the same trick with any other Mozilla core function you want to play with.

So, now you know, and knowing is half the battle.
The other half involves blue and red lasers and guys with stupid nicknames.

:: The x500 DRINKing Game

In a previous life, i worked for a company that was building an x500 MTA. For those not in a fetal position beneath their desk at the very mention of that phrase, an MTA is a Mail Transfer Agent. It's what's responsible for getting your complaint about the new Timeline to zuck@facebook.com. x500 was the "Improved" mail addressing format that didn't presume a user at a site, but instead qualified it by a list of things that described an individual, from least specific to most (e.g.
Country: US
Province: California
City: Mountain View
Company: Widgetco
Floor: 3
FamilyName: Doe
SurName: John
DNAFingerprint: ATTACAG...

etc.)

(i'll beg your forgiveness if the field names are not correct. The therapy helps blot that crap from my mind.)

What's important here is that x500 allows fairly arbitrary fields to be inserted into a given record. The MTA only has to pay attention to the ones that it recognizes, so you could build one that only responds to, say, "Company, FamilyName" if you're a place with a dozen, unrelated employees. The rest of the spec is all optional. This lead to all sorts of interesting abuses, including one of the things i was working on that involved storing a cryptographic key inside of the record that tied back to the authorizing provider, allowing for a verification path to the central root authority, meaning that your headers were often far larger than the actual content of the message and that had to be magically stuffed through a 300baud modem in a timely manner.

Still, flexibility was a strong selling point for x500. So much so, that the example included the frivolous category of "FavoriteDrink", and provided a helpful collection of alcoholic concoctions for the various fictional individuals to imbibe. HaHa, what merriment shall be had!

Only, it was in a spec, so that it became codified.

Now, a few decades later, i fully expect that there are more than a few unit test cases for determining validity of records based on "drink", and some very confused intern wondering what the hell that field has to do with figuring out how to partition their machines. i have been that intern. It was not fun.

This is why i tend to be a little cautious when folks talk about "loose data definitions" in applications, unless they REALLY MEAN IT. As in, they define methods for how data can be stored, but do not require specific elements to be defined. The application is therefore required to determine if a record can be used or not. That tends to shoot a lot of holes into peoples designs, and i'm ok with that. You can't realistically expect anyone else to use something where you hand wave over something as important as "The Data You Use".

Well, unless you're also providing all the alcohol that they'll need to deal with that stuff.

Blogs of note
personal Christopher Conlin USMC memoirs of hydrogen guy rhapsodic.org Henriette's Herbal Blog
geek ultramookie

Powered by WordPress
Hosted on Dreamhost.