From 9616ea92b8d97786644ef4562a55505bc3f4de67 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 16 Aug 2012 13:15:28 -0600 Subject: [PATCH] separated from localStorage --- README.md | 16 ++++--- lib/index.js | 108 ++++++++++++++++++++++++++++++++++++++++++++ lib/localStorage.js | 54 ---------------------- lib/package.json | 10 ++-- tests/test.js | 65 +++++++++++++++++--------- 5 files changed, 166 insertions(+), 87 deletions(-) create mode 100644 lib/index.js delete mode 100644 lib/localStorage.js diff --git a/README.md b/README.md index f7dd256..01f2065 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -localStorage +DOMStorage === -An inefficient, but as W3C-compliant as possible using only pure JavaScript, `localStorage` implementation. +An inefficient, but as W3C-compliant as possible using only pure JavaScript, `DOMStorage` implementation. Purpose ---- @@ -11,7 +11,9 @@ This is meant for the purpose of being able to run unit-tests and such for brows Usage ---- - var localStorage = require('localStorage') + var Storage = require('dom-storage') + , localStorage = new Storage('./db.json') // in-file + , sessionStorage = new Storage() // in-memory , myValue = { foo: 'bar', baz: 'quux' } ; @@ -48,9 +50,9 @@ Tests localStorage.clear(); 0 === localStorage.length; -TODO / Bugs +Notes --- - * Does not persist. - * could use `fs.readFileSync` at load and an occasional `fs.writeFile` to write-out localStorage.json - * Doesn't not emit `Storage` events + * db is read in synchronously + * No callback when db is saved + * Doesn't not emit `Storage` events (not sure how to do) diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..275a97d --- /dev/null +++ b/lib/index.js @@ -0,0 +1,108 @@ +/*jshint node:true es5:true laxcomma:true laxbreak:true*/ +// http://www.rajdeepd.com/articles/chrome/localstrg/LocalStorageSample.htm + +// NOTE: +// this varies from actual localStorage in some subtle ways + +// also, there is no persistence +// TODO persist +(function () { + "use strict"; + + var fs = require('fs') + ; + + function Storage(path) { + var db + ; + + Object.defineProperty(this, '___priv_bk___', { + value: { + path: path + } + , writable: false + , enumerable: false + }); + + try { + db = JSON.parse(fs.readFileSync(path)); + } catch(e) { + db = {}; + } + + Object.keys(db).forEach(function (key) { + this[key] = db[key]; + }, this); + } + + Storage.prototype.getItem = function (key) { + if (key in this) { + return String(this[key]); + } + return null; + }; + + Storage.prototype.setItem = function (key, val) { + this[key] = String(val); + this.___save___(); + }; + + Storage.prototype.removeItem = function (key) { + delete this[key]; + this.___save___(); + }; + + Storage.prototype.clear = function () { + var self = this; + // filters out prototype keys + Object.keys(self).forEach(function (key) { + self[key] = undefined; + delete self[key]; + }); + }; + + Storage.prototype.key = function (i) { + i = i || 0; + return Object.keys(this)[i]; + }; + + Storage.prototype.__defineGetter__('length', function () { + return Object.keys(this).length; + }); + + Storage.prototype.___save___ = function () { + var self = this + ; + + if (!this.___priv_bk___.path) { + return; + } + + if (this.___priv_bk___.lock) { + this.___priv_bk___.wait = true; + return; + } + + this.___priv_bk___.lock = true; + fs.writeFile(this.___priv_bk___.path, JSON.stringify(this), 'utf8', function (e) { + self.___priv_bk___.lock = false; + if (e) { + return; + } + if (self.___priv_bk___.wait) { + self.___priv_bk___.wait = false; + self.___save___(); + } + }); + }; + + Object.defineProperty(Storage, 'create', { + value: function (path) { + return new Storage(path); + } + , writable: false + , enumerable: false + }); + + module.exports = Storage; +}()); diff --git a/lib/localStorage.js b/lib/localStorage.js deleted file mode 100644 index 4a74dff..0000000 --- a/lib/localStorage.js +++ /dev/null @@ -1,54 +0,0 @@ -// http://www.rajdeepd.com/articles/chrome/localstrg/LocalStorageSample.htm - -// NOTE: -// this varies from actual localStorage in some subtle ways - -// also, there is no persistence -// TODO persist -(function () { - "use strict"; - - var db; - - function LocalStorage() { - } - db = LocalStorage; - - db.prototype.getItem = function (key) { - if (key in this) { - return String(this[key]); - } - return null; - }; - - db.prototype.setItem = function (key, val) { - this[key] = String(val); - }; - - db.prototype.removeItem = function (key) { - delete this[key]; - }; - - db.prototype.clear = function () { - var self = this; - Object.keys(self).forEach(function (key) { - self[key] = undefined; - delete self[key]; - }); - }; - - db.prototype.key = function (i) { - i = i || 0; - return Object.keys(this)[i]; - }; - - db.prototype.__defineGetter__('length', function () { - return Object.keys(this).length; - }); - - if (global.localStorage) { - module.exports = localStorage; - } else { - module.exports = new LocalStorage(); - } -}()); diff --git a/lib/package.json b/lib/package.json index 2d96f56..8bba199 100644 --- a/lib/package.json +++ b/lib/package.json @@ -1,16 +1,16 @@ { "author": "AJ ONeal (http://coolaj86.info)", - "name": "localStorage", - "description": "W3C localStorage for Node.JS", + "name": "dom-storage", + "description": "W3C DOM Storage (localStorage and sessionStorage) for Node.JS", "version": "1.0.0", "repository": { "type": "git", - "url": "git://github.com/coolaj86/node-localStorage.git" + "url": "git://github.com/coolaj86/node-browser-compat.git" }, "engines": { - "node": ">= v0.2.0" + "node": "*" }, - "main": "localStorage.js", + "main": "index.js", "dependencies": {}, "devDependencies": {} } diff --git a/tests/test.js b/tests/test.js index 07d8330..a3df772 100644 --- a/tests/test.js +++ b/tests/test.js @@ -1,34 +1,57 @@ +/*jshint node:true es5:true laxcomma:true laxbreak:true*/ (function () { "use strict"; var assert = require('assert') - , localStorage = require('localStorage') + , fs = require('fs') + , Storage = require('dom-storage') + , dbPath = './db.json' ; - // can't make assuptions about key positioning - localStorage.setItem('a', 1); - assert.strictEqual(localStorage.key(0), 'a'); + function runTest(storage) { + assert.strictEqual(0, Object.keys(storage).length); + assert.strictEqual(0, storage.length); - localStorage.setItem('b', '2'); - assert.strictEqual(localStorage.getItem('a'), '1'); - assert.strictEqual(localStorage.getItem('b'), '2'); - assert.strictEqual(localStorage.length, 2); + // can't make assuptions about key positioning + storage.setItem('a', 1); + assert.strictEqual(storage.key(0), 'a'); - assert.strictEqual(localStorage['c'], undefined); - assert.strictEqual(localStorage.getItem('c'), null); + storage.setItem('b', '2'); + assert.strictEqual(storage.getItem('a'), '1'); + assert.strictEqual(storage.getItem('b'), '2'); + assert.strictEqual(storage.length, 2); - localStorage.setItem('c'); - assert.strictEqual(localStorage.getItem('c'), "undefined"); - assert.strictEqual(localStorage.length, 3); + assert.strictEqual(storage['c'], undefined); + assert.strictEqual(storage.getItem('c'), null); - localStorage.removeItem('c'); - assert.strictEqual(localStorage.getItem('c'), null); - assert.strictEqual(localStorage.length, 2); + storage.setItem('c'); + assert.strictEqual(storage.getItem('c'), "undefined"); + assert.strictEqual(storage.length, 3); - localStorage.clear(); - assert.strictEqual(localStorage.getItem('a'), null); - assert.strictEqual(localStorage.getItem('b'), null); - assert.strictEqual(localStorage.length, 0); + storage.removeItem('c'); + assert.strictEqual(storage.getItem('c'), null); + assert.strictEqual(storage.length, 2); - console.log('All tests passed'); + storage.clear(); + assert.strictEqual(storage.getItem('a'), null); + assert.strictEqual(storage.getItem('b'), null); + assert.strictEqual(storage.length, 0); + } + + function runAll() { + var localStorage = new Storage(dbPath) + , sessionStorage = new Storage() + ; + + runTest(sessionStorage); + runTest(localStorage); + + localStorage.setItem('a', 1); + setTimeout(function () { + assert.deepEqual({ a: 1 }, JSON.parse(fs.readFileSync(dbPath))); + console.log('All tests passed'); + }, 100); + } + + fs.unlink(dbPath, runAll); }());