I’ve been working on yet another JavaScript API for accessing preferences. My goals for it are simplicity, intuitiveness, power, and perhaps performance. I’m also interested in learning whether freeing it from the restrictions of XPCOM can make it better than existing APIs.
The Basics
It’s a JavaScript module, so you start by importing it from somewhere:
Components.utils.import("resource://somewhere/Preferences.js");
Getting and setting prefs is easy:
let foo = Preferences.get("extensions.test.foo");
Preferences.set("extensions.test.foo", foo);
As with FUEL‘s preferences API, datatypes are auto-detected, and you can pass a default value that the API will return if the pref doesn’t have a value:
let foo = Preferences.get("extensions.test.nonexistent", "default value");
// foo == "default value"
Unlike FUEL, which returns null in the same situation, the module doesn’t return a value when you get a nonexistent pref without specifying a default value:
let foo = Preferences.get("extensions.test.nonexistent");
// typeof foo == "undefined"
(Although the preferences service doesn’t currently store null values, other interfaces like nsIVariant and nsIContentPrefService and embedded storage engines like SQLite distinguish between the null value and “doesn’t have a value,” as does JavaScript, so it seems more consistent and robust to do so here as well.)
Look Ma, No XPCOM
Because we aren’t using XPCOM, we can include some interesting API features. First, as you may have noticed already, the interface doesn’t require you to create a branch just to get a pref, but you can create one if you want to via an intuitive syntax:
let testBranch = new Preferences("extensions.test.");
// Preferences.get("extensions.test.foo") == testBranch.get("foo")
The get method uses polymorphism to enable you to retrieve multiple values in a single call, and, with JS 1.7’s destructuring assignment, you can assign those values to individual variables:
let [foo, bar, baz] = testBranch.get(["foo", "bar", "baz"]);
And set lets you update multiple prefs in one call (although they still get updated individually on the backend, so each change results in a separate notification):
testBranch.set({ foo: 1, bar: "awesome", baz: true });
Performance?
Getting prefs via the module is several times slower than getting them directly from the preferences service, but it’s much faster than using FUEL, and we can make the module just as fast as the direct approach by making it cache values (at some unknown set and memory cost):
Nevertheless I wonder if it’s worth the added complexity and other iatrogenic costs of caching, given that preferences generally don’t get accessed very frequently, and all of these methods are fast enough for small numbers of accesses.
Everything Else
I haven’t yet built the rest of the API (has, reset/clear, locking, adding and removing observers, etc.). Is it worth doing so? Is this API better enough than FUEL’s or simply direct access to the XPCOM preferences service? And are there other improvements we can make given that we aren’t limited to the language features XPCOM supports?
(To try it out, download the Preferences and/or CachingPreferences modules.)
Update: the latest version of the module is available at http://hg.mozdev.org/jsmodules/file/tip/Preferences.js. That link will stay up-to-date with changes to the module.