Permalink
Browse files

Better API.

  • Loading branch information...
1 parent 8351cea commit e9cc78c5878962bf78b9e440190d9c15914ea44b @protz committed Jan 11, 2011
Showing with 94 additions and 49 deletions.
  1. +80 −37 SimpleStorage.js
  2. +2 −0 tests/run.sh
  3. +12 −12 tests/test_SimpleStorage.js
View
@@ -34,7 +34,7 @@
*
* ***** END LICENSE BLOCK ***** */
-var EXPORTED_SYMBOLS = ['SimpleStorage', 'spin', 'kWorkDone']
+var EXPORTED_SYMBOLS = ['SimpleStorage']
const Ci = Components.interfaces;
const Cc = Components.classes;
@@ -45,23 +45,57 @@ Cu.import("resource://conversations/log.js");
let Log = setupLogging("Conversations.SimpleStorage");
Log.debug("Simple Storage loaded.");
-let JSON = Cc["@mozilla.org/dom/json;1"]
- .createInstance(Ci.nsIJSON);
let gStorageService = Cc["@mozilla.org/storage/service;1"]
.getService(Ci.mozIStorageService);
let gDbFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIProperties)
.get("ProfD", Ci.nsIFile);
gDbFile.append("simple_storage.sqlite");
+const kWorkDone = 42;
+
+let SimpleStorage = {
+ createCpsStyle: function _SimpleStorage_createCps (aTblName) {
+ return new SimpleStorageCps(aTblName);
+ },
+
+ createIteratorStyle: function _SimpleStorage_createForIterator (aTblName) {
+ let cps = new SimpleStorageCps(aTblName);
+ return new SimpleStorageIterator(cps);
+ },
+
+ createPromisesStyle: function _SimpleStorage_createPromisesStyle (aTblName) {
+ let cps = new SimpleStorageCps(aTblName);
+ return new SimpleStoragePromises(cps);
+ },
+
+ kWorkDone: kWorkDone,
+
+ spin: function _SimpleStorage_spin(f) {
+ let iterator = f();
+ // Note: As a point of interest, calling send(undefined) is equivalent
+ // to calling next(). However, starting a newborn generator with any value
+ // other than undefined when calling send() will result in a TypeError
+ // exception.
+ (function send(r) {
+ let asyncFunction = iterator.send(r);
+ if (asyncFunction !== kWorkDone) {
+ asyncFunction(function (r) {
+ send(r);
+ });
+ }
+ })();
+ },
+};
+
/**
* Open a new storage instance.
* @param {String} aTblName A unique identifier that tells who you are. Other
* clients of SimpleStorage will also provide their own identifier. You can use
* the GUID of your extension, for instance.
* @return A new SimpleStorage instance.
*/
-function SimpleStorage(aTblName) {
+function SimpleStorageCps(aTblName) {
// Will also create the file if it does not exist
this.dbConnection = gStorageService.openDatabase(gDbFile);
if (!this.dbConnection.tableExists(aTblName))
@@ -71,7 +105,7 @@ function SimpleStorage(aTblName) {
this.tableName = aTblName;
}
-SimpleStorage.prototype = {
+SimpleStorageCps.prototype = {
/**
* Find the data associated to the given key.
* @param {String} aKey The key used to identify your data.
@@ -106,7 +140,7 @@ SimpleStorage.prototype = {
if (results.length > 1) {
Log.assert(false, "Multiple rows for the same primary key? That's impossible!");
} else if (results.length == 1) {
- k(JSON.decode(results[0]).value);
+ k(JSON.parse(results[0]).value);
} else if (results.length == 0) {
k(null);
}
@@ -115,10 +149,6 @@ SimpleStorage.prototype = {
});
},
- sGet: function _SimpleStorage_sGet (aKey) (function (finish) {
- this.get(aKey, function (result) finish(result));
- }).bind(this),
-
/**
* Store data for the given key. It will erase any previous binding if any,
* and you'll lose the data previously associated with that key.
@@ -136,7 +166,7 @@ SimpleStorage.prototype = {
;
let statement = this.dbConnection.createStatement(query.replace("#1", this.tableName));
statement.params.key = aKey;
- statement.params.value = JSON.encode({ value: aValue });
+ statement.params.value = JSON.stringify({ value: aValue });
statement.executeAsync({
handleResult: function(aResultSet) {
},
@@ -158,10 +188,6 @@ SimpleStorage.prototype = {
}).bind(this));
},
- sSet: function _SimpleStorage_sSet (aKey, aValue) (function (finish) {
- this.set(aKey, aValue, function (result) finish(result));
- }).bind(this),
-
/**
* Check whether there is data associated to the given key.
* @param {String} aKey The key.
@@ -173,10 +199,6 @@ SimpleStorage.prototype = {
});
},
- sHas: function _SimpleStorage_sHas (aKey) (function (finish) {
- this.has(aKey, function (result) finish(result));
- }).bind(this),
-
/**
* Remove data associated with the given key.
* @param {String} aKey The key.
@@ -212,26 +234,47 @@ SimpleStorage.prototype = {
}
}).bind(this));
},
+}
- sRemove: function _SimpleStorage_sRemove (aKey) (function (finish) {
- this.remove(aKey, function (result) finish(result));
- }).bind(this),
+/**
+ * This is another version of the API that offers the appearance of a
+ * synchronous API. Basically, a call to get now returns a function that takes
+ * one argument, that is, the function that it is expected to call to restart
+ * the original computation.
+ * You are to use it like this:
+ *
+ * SimpleStorage.spin(function anon () {
+ * let r = yield get("myKey");
+ * // do stuff with r
+ * });
+ *
+ * What happens is the anon function is suspended as soon as it yields. If we
+ * call f the function returned by get("myKey"), then spin is the driver that
+ * will run f, and is expected to call the argument it's passed one it's done.
+ * That argument is a function that takes the result that is to be returned
+ * when it restarts the anon function (the value that ends up being r).
+ * This is exactly what our little wrappers below do.
+ */
+function SimpleStorageIterator(aSimpleStorage) {
+ this.ss = aSimpleStorage;
}
-const kWorkDone = 42;
+SimpleStorageIterator.prototype = {
+
+ get: function _SimpleStorage_get (aKey) (function (finish) {
+ this.get(aKey, function (result) finish(result));
+ }).bind(this.ss),
+
+ set: function _SimpleStorage_set (aKey, aValue) (function (finish) {
+ this.set(aKey, aValue, function (result) finish(result));
+ }).bind(this.ss),
+
+ has: function _SimpleStorage_has (aKey) (function (finish) {
+ this.has(aKey, function (result) finish(result));
+ }).bind(this.ss),
+
+ remove: function _SimpleStorage_remove (aKey) (function (finish) {
+ this.remove(aKey, function (result) finish(result));
+ }).bind(this.ss),
-function spin(f) {
- let iterator = f();
- // Note: As a point of interest, calling send(undefined) is equivalent
- // to calling next(). However, starting a newborn generator with any value
- // other than undefined when calling send() will result in a TypeError
- // exception.
- (function send(r) {
- let asyncFunction = iterator.send(r);
- if (asyncFunction !== kWorkDone) {
- asyncFunction(function (r) {
- send(r);
- });
- }
- })();
}
View
@@ -15,6 +15,8 @@ run_all() {
# head.js will check this variable to figure out where our fake profile
# directory should point to
export XPCSHELL_TEST_PROFILE_DIR=$PROFD
+ # Should help
+ export MOZ_REPORT_ALL_JS_EXCEPTIONS=1
# Make it available in the modules/ directory xpcshell will "see"
\cp ../SimpleStorage.js $TB_OBJDIR/mozilla/dist/bin/modules/
# Fake a directory structure similar to that of conversations so that the
@@ -48,42 +48,42 @@ Cu.import("resource:///modules/SimpleStorage.js");
let remainingThreads = 0;
function test_sync_api () {
- spin(function () {
- let ss = new SimpleStorage("test");
+ SimpleStorage.spin(function () {
+ let ss = SimpleStorage.createIteratorStyle("test");
let r;
- r = yield ss.sHas("myKey");
+ r = yield ss.has("myKey");
do_check_false(r);
- r = yield ss.sSet("myKey", "myVal");
+ r = yield ss.set("myKey", "myVal");
do_check_true(r); // Value was added
- r = yield ss.sGet("myKey");
+ r = yield ss.get("myKey");
do_check_true(r == "myVal");
let o = { k1: "v1", k2: "v2" };
- r = yield ss.sSet("myKey", o);
+ r = yield ss.set("myKey", o);
do_check_false(r); // Value was updated in-place
- r = yield ss.sGet("myKey");
+ r = yield ss.get("myKey");
for each (key in Object.keys(r))
do_check_true(r[key] == o[key]);
for each (key in Object.keys(o))
do_check_true(r[key] == o[key]);
// Test nested async actions. Not recommended for the casual user because of
// the very specific order of finish vs. yield kWorkDone.
- yield (function (finish) (spin (function () {
- r = yield ss.sRemove("myKey");
+ yield (function (finish) (SimpleStorage.spin (function () {
+ r = yield ss.remove("myKey");
do_check_true(r);
- r = yield ss.sRemove("myKey");
+ r = yield ss.remove("myKey");
do_check_false(r);
finish();
yield kWorkDone; // Remember, nothing is executed past that line
})));
- r = yield ss.sHas("myKey");
+ r = yield ss.has("myKey");
do_check_false(r);
dump("\033[01;34m--- async api test is over\033[00m\n");
remainingThreads--;
- yield kWorkDone;
+ yield SimpleStorage.kWorkDone;
});
}

0 comments on commit e9cc78c

Please sign in to comment.