Archive for July, 2007

i need a mountaineer

Tuesday, July 31st, 2007

This is maybe just idiosyncratic of me, but I think the last three links in this MeFi post are absolutely hilarious. I don’t even know the songs. (OK, actually the last one isn’t *as* funny. But go for the Christina Aguilera and the one with Björk, fer sure.)

simple unstackable one-way Cocoa-like bindings for Javascript

Monday, July 30th, 2007

It’s funny how everyday English terms are co-opted to mean specific things in various specialties. You wind up saying bizarre yet perfectly justifiable things like “unstackable Cocoa-like bindings.” Cocoa? Java? Beverages? Snowboards? Huh? I guess if your bindings are like cocoa, they wouldn’t stack very well now would they?

So using closures and Javascript’s Object.watch method, it’s pretty easy to set up Cocoa-style property bindings in Javascript.

The general idea—the model-view or model-view-controller design pattern—is that you have a data model, and you have an independent view object that represents a property in the data model. When the property in the data model changes, you generally always want the view object to redraw itself. If you’re willing to let your data model know about your specific views, you can just have the data model tickle the view directly, but it’s better to isolate the model and views. Models shouldn’t care how they’re being presented, and views shouldn’t care where their data is coming from.

Cocoa has some nifty binding technology to help with this. In short, Cocoa bindings give all NSObjects the ability to bind the value of one object’s member to the value of another object’s member. This is particularly nice in interface design, since Interface Builder allows you to set up bindings in the view layout itself. There is also a programmatic way to set up bindings in the code, using the message:

[ bind:toObject:withKeyPath:options: ]

So yesterday I was working on version 2 of my iPhone tip calculator app and decided, for the sake of exercising Javascript, to make it as traditionally app-like as possible, with a model, and views, and reducing its operation to just the result of a set of objects talking to each other; the whole thing.

Anyway, here’s how to get simple, unstackable, one-way Cocoa-like bindings for your Javascript objects:

Object.prototype.bind = function(myProperty, toObject, withProperty, callback) {
	if (!this.bindings) {
		this.bindings = new Object;
	}

	var boundObj = this;
	this.bindings[myProperty] = {
		object: toObject,
		property: withProperty,
		enforce: function(prop, oldValue, newValue) {
			if (myProperty != ”) {
				boundObj[myProperty] = newValue;
			}
			if (callback) {
				callback(prop, oldValue, newValue);
			}
			return newValue;
		}
	}
	toObject.watch(withProperty, this.bindings[myProperty].enforce);
}

Object.prototype.unbind = function(myProperty) {
	this.bindings[myProperty].object.unwatch(this.bindings[myProperty].property);
}

So let’s say you have a model object with a “bill total” property, model.mBillTotal, and you have a view object of class CurrencyDisplay, which formats and renders a number as currency. The view object, call it billTotalDisplay, keeps track of its own value in a property mValue. You can now do this in some appropriate place (ie, a “controller”):

billTotalDisplay.bind('mValue', model, 'mBillTotal');

When anything causes the model’s bill total to change, the view displaying that total will be updated automagically. OK, actually, that’s not true: the view’s mValue property will be updated automagically, but without further action, the view won’t be redrawn onscreen.

Let’s say that CurrencyDisplay views have a method called render which draws the display to the screen. You could use the binding’s callback argument to trigger the view’s redraw like this:

billTotalDisplay.bind('mValue', model, 'mBillTotal', billTotalDisplay.render);

But I would prefer to keep that sort of thing in-house with respect to the view. The controller shouldn’t necessarily care what the display does with its value; it only needs to know that this particular view’s value depends on this particular part of the data model. We can use Object.watch to trigger render() when the view’s value changes. In CurrencyDisplay’s constructor:

CurrencyDisplay = function() {
	...
	var self = this;
	this.watch('mValue', function(property, oldValue, newValue) {
		self.render();
		return newValue;
	});
	...
}

Now, anytime a CurrencyDisplay’s mValue changes, its render() will be invoked automagically. Anytime the model’s bill total changes, the right CurrencyDisplay’s mValue will be updated automagically. So, there you have it: update the model, the view redraws, and all you have to do is set up the binding in the controller. Simple one-way Cocoa-like bindings.

Note the line var self = this; above and var boundObj = this; in Object.prototype.bind. These are necessary to access the CurrencyDisplay or bound object from within the anonymous functions we pass to watch. When those functions are invoked, this refers to the functions themselves, and not to the CurrencyDisplay or bound object. On the other hand, the functions create closures around the scope of their creation, so self and boundObj will retain their value within the anonymous function, even when they’re only called much later, at runtime. Sexy.

Also note the big limitation on this: You can’t stack bindings. Calling watch() on a property replaces any previous “watchpoint.” In this example, only one display can depend on the value of model.mBillTotal at a time. Bummer.

“toys can change the world”

Thursday, July 26th, 2007

Will Wright demos Spore on TED.

I think he’s right about the importance of long-term thinking and also about the importance of toys.

bug spray and sunblock and sweat and awe

Monday, July 23rd, 2007

Went backpacking this weekend to the most beautiful place I’ve yet seen in Colorado. The bowl enclosing Crater Lake, just west of the continental divide in Indian Peaks Wilderness, was patterned after some rich gray-green dream. It’s lush and wet but has that alpine purity of air you only find at altitude. I’m guessing that storms from the west are lifted and forced to dump their moisture here: in late July it’s a near-rainforest just below treeline. Moss and ferns and pines grow anywhere they can get half a toe-hold. The lake sits at the bottom of a cathedral cirque of peaks, a “crater” all edges and teeth and evening-glowing spires. Waterfalls spill into the lake from the melting snowfields above and fill the bowl with their constant distant soundwash.

I think it’s that I get tired of the arid Colorado climate. Little verdant gems like this make me all lovey-dovey for the western landscape again.

The trail up to the lake is wonderful, too. It’s called Cascade Creek Trail, and for good reason: there are several waterfalls along the eponymous creek, and they are occasionally absurdly perfect. It does what good trails do: give you a little narrative of landscape visuals. It foreshadows a bit at the beginning, revealing the distant glaciated peaks near your destination. Then it hides them, but offers other treats along the way: wildflower meadows, aspen stands, the waterfalls, alpine tundra glowing green along the top of your canyon. The water from the creek is sweet and cold.* As you get higher, the sculptured cirque comes into view as the vegetation becomes more verdant and the temperature drops. A storm rumbled and darkened the sky as we approached. There are a series of small climaxes when some vista of clouds and rock and sunlight orchestrate themselves in some spectacular and obviously rehearsed way (how else could it be so perfect?). Then you reach the lake, and the climactic end of the narrative. Green and gray and lake-mirrored rocky spires and all that. Yay!

At which point it started raining and we ran around like idiots trying to find a free campsite, then rushed to pitch the tents. And then we took a nap for a couple of hours while it rained.

I posted unprocessed photos to my “lazy” flickr account. Will probably take a few of the keepers and clean them up a bit at some point.

* Yes, dad, we filtered it.