Skip to content

Monthly Archives: April 2008

Minimizing Code Paths in Asychronous Code

Have you ever written a function that looks like this?

[code language="javascript"]
function requestProductDetails(id, k) {
var value = gProductDetailsCache[id];
if (value)
k(value)
else
ajax.get('/product/'+id, function(data) {
gProductDetailsCache[id] = data;
k(data);
});
}
[/code]

requestProductDetails calls its callback with the product details, which are stored in a cache. Since it might need to request this information from the server, it has to “return” it by passing it to a callback; in order to present a uniform API whether or not the product is cached, it “returns” the data this way whether it came from the cache or not.

A Mock for {set,clear}{Timeout,Interval}

Here’s a potential JSSpec spec for Sequentially.trickle.map:

[code language="javascript"]
describe('Sequentially.trickle.map', {
'should apply to all the elements': function() {
Sequentially.trickle.map(
['a', 'b', 'c'],
function(x) { return x + 1 },
1,
function(result) {
value_of(result.join(',')).should_be('a1,b1,c1');
});
});
}
});
[/code]

This doesn’t work. The problem is that Sequentially.trickle.map is asynchronous (it defers most of its computation — including the invocation of the callback — via setTimeout). This means that should_be isn’t called until after the spec has returned. If it succeeds, this isn’t a problem, but if it fails, JSSpec can’t associate it with the failing spec — worse, JSSpec will have already have marked it successful.

Conquering the Busy Cursor with Sequentially

What’s wrong with this function? (Hint: it’s meant to execute periodically on a JavaScript page.)

[code language="javascript"]
function updateExpirationText() {
var now = new Date;
products.forEach(function(item) {
var expiresDate = item.expiresDate || Date.parse(item.expires),
remaining = expiresDate - now,
text = remaining < 0 ? 'expired' : msToDuration(remaining);
$('item-' + item.id + ' .time-remaining').text(remaining);
});
}
[/code]

It’s a trick question. Maybe nothing’s wrong. But if products can get very long, or if the msToDuration is very slow, you’ve locked up the UI for a long time. At best, this makes for sluggish response; at worst, the page that contains this will trigger a “script running slowly” error, and the user will likely abort all the JavaScript on the page.

DB Content Rails Plugin

The DB Content Rails plugin adds tasks to save and restore database content.

Usage

[code]
-- dump the development database to db/archive/development-content.sql.gz
rake db:content:dump

-- load the dumped database, and apply any necessary migrations
$ rake db:content:load

-- dump the production database to db/archive/production-content.sql.gz
$ RAILS_ENV=production rake db:content:dump

-- save the development database to db/archive/{timestamp}.sql.gz
$ rake db:content:save

-- save the (compressed) database to my-data.sql.gz
$ rake db:content:save FILE=my-data.sql.gz

JCON: Ruby Gem for JSON type conformance

JCON (the JavaScript Conformance gem) tests JSON values against ECMAScript 4.0-style type definitions
(PDF) such as string?, (int, boolean), or [string, (int, boolean), {x:double, y:double}?].

Usage

[code language="ruby"]
type = JCON::parse "[string, int]"
type.contains?(['a', 1]) # => true
type.contains?(['a', 'b']) # => false
type.contains?(['a', 1, 2]) # => true
[/code]

JCON also defines an RSpec matcher, conforms_to_js:

Three Small JavaScript Libraries

Three small libraries, that I carry with me from project to project:

Fluently — Construction Kit for Chainable Methods

With Fluently, you can do this:

[code language="javascript"]
var o = Fluently.make(function(define) {
define('fn1', function() {console.info('called fn1')});
define('fn2', function() {console.info('called fn2')});
define('fn3', function() {return 3});
});
[/code]

to define an object with chained methods, that can be invoked thus:

[code language="javascript"]
o.fn1().fn2() // calls fn1 and then fn2
o.fn2().fn1() // calls fn2 and then fn1

JavaScript Fu Rails Plugin

JavaScript Fu extends Rails with a few facilities to better integrate JavaScript into Rails development:

1. The notes and statistics rake tasks compass JavaScript files in the public/javascript directory:

[code language="bash"]
$ rake notes
public/javascripts/controls.js:
* [782] [TODO] improve sanity check
[/code]

[code language="bash"]
$ rake stats
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
[...]
| JavaScript | 7287 | 6322 | 0 | 0 | 0 | 0 |
[...]
[/code]

2. The call_js RSpec matcher asserts that a string or response contains a script tag, that contains JavaScript that calls the named function or method:

FlashBridge: proxying Flash <-> OpenLaszlo

I’ve updated my OpenLaszlo utility grab-bag to make browser <-> applet communication even easier. How easy?

Proxies

Put this in your browser JavaScript:

[code language="javascript"]
var gObject = {
f: function() { console.info('gObject.f', arguments) },
g: function() { console.info('gObject.g', arguments) }
};
[/code]

And this in an OpenLaszlo applet:

[code language="javascript"]
var gObject = FlashBridge.createRemoteProxy('gObject', ['f', 'g']);
gObject.f(1, 2);
gObject.g(3);
[/code]

When you run the applet code, it prints this to the browser console: