documented new features
This commit is contained in:
parent
da17e4e05f
commit
3ddf2be63d
45
README.md
45
README.md
|
@ -21,26 +21,39 @@ Made fo for Node.JS and Ender.JS (browser-side).
|
|||
|
||||
var localStorage = require('localStorage')
|
||||
, JsonStorage = require('json-storage')
|
||||
, db = JsonStorage(localStorage, 'my-app-prefix')
|
||||
, store = JsonStorage.create(localStorage, 'my-widget-namespace')
|
||||
, myValue = {
|
||||
foo: "bar"
|
||||
, baz: "quux"
|
||||
}
|
||||
;
|
||||
|
||||
db.set('myKey', myValue);
|
||||
myValue = db.get('myKey');
|
||||
store.set('myKey', myValue);
|
||||
myValue = store.get('myKey');
|
||||
|
||||
API
|
||||
===
|
||||
|
||||
* JsonStorage(DOMStorage, 'application-prefix') // optional prefix
|
||||
* get(key)
|
||||
* set(key, value)
|
||||
* remove(key)
|
||||
* clear()
|
||||
* keys()
|
||||
* size()
|
||||
* `create(DOMStorage, namespace)`
|
||||
* `DOMStorage` should be globalStorage, sessionStorage, or localStorage
|
||||
* `namespace` is optional string which allows multiple non-conflicting storage containers
|
||||
* `get(key)`
|
||||
* `set(key, value)`
|
||||
* `remove(key)`
|
||||
* `clear()`
|
||||
* `keys()`
|
||||
* `size()`
|
||||
* `toJSON()`
|
||||
* `JSON.stringify(store)`
|
||||
|
||||
Upgrading from localStorage and 1.0.x to 1.1.x
|
||||
===
|
||||
|
||||
1.1.x automatically attempts to upgrade your DOMStorage to use namespaces in backwards-compatible way.
|
||||
|
||||
However, you can prevent this behaviour:
|
||||
|
||||
localStorage.getItem('_json-storage-namespaced_', true);
|
||||
|
||||
null vs undefined in JSON
|
||||
===
|
||||
|
@ -52,22 +65,22 @@ they're simply to inform you of a few 'gotchas' inherent in JSON / DOMStorage co
|
|||
If they do, you're probably doing something wrong in the first place.
|
||||
|
||||
|
||||
It is not valid to set `undefined` in JSON. So setting a key to `undefined` will remove it from the db.
|
||||
It is not valid to set `undefined` in JSON. So setting a key to `undefined` will remove it from the store.
|
||||
|
||||
This means that `db.set('x')` is the same as `db.remove('x')`.
|
||||
This means that `store.set('x')` is the same as `store.remove('x')`.
|
||||
|
||||
To save `undefined`, use `null` instead.
|
||||
|
||||
|
||||
Note that both values that exist as `null` and values that don't exist at all will return `null`.
|
||||
|
||||
db.set('existing-key', null);
|
||||
null === db.get('existing-key');
|
||||
null === db.get('non-existant-key');
|
||||
store.set('existing-key', null);
|
||||
null === store.get('existing-key');
|
||||
null === store.get('non-existant-key');
|
||||
|
||||
|
||||
The special case of `null` as `"null"`, aka `"\"null\""`:
|
||||
|
||||
`null`, and `"null"` both parse as `null` the "object", instead of one being the string (which would be `"\"null\""`).
|
||||
|
||||
Objects containing `null`, however, parse as expected `{ "foo": null, "bar": "null" }` will parse as `foo` being `null` but `bar` being `"null"`, much unlike the value `"null"` being parsed on its own.
|
||||
Objects containing `null`, however, parse as expected `{ "foo": null, "bar": "null" }` will parse as `foo` being `null` but `bar` being `"null"`, much unlike the value `"null"` being parsed on its own.
|
||||
|
|
127
lib/index.js
127
lib/index.js
|
@ -1,7 +1,9 @@
|
|||
(function () {
|
||||
"use strict";
|
||||
|
||||
var db;
|
||||
var Store
|
||||
, delim = ':'
|
||||
;
|
||||
|
||||
function Stringify(obj) {
|
||||
var str;
|
||||
|
@ -23,57 +25,122 @@
|
|||
return obj;
|
||||
}
|
||||
|
||||
function JsonStorage(w3cStorage) {
|
||||
this.db = w3cStorage;
|
||||
this.keysAreDirty = true;
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/[-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
|
||||
}
|
||||
db = JsonStorage;
|
||||
|
||||
function upgradeStorage(jss, w3cs) {
|
||||
var i
|
||||
, key
|
||||
, val
|
||||
, json = {}
|
||||
;
|
||||
|
||||
if (jss._store.getItem('_json-storage-namespaced_', true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we can't modify the db while were reading or
|
||||
// the keys will shift all over the place
|
||||
for (i = 0; i < w3cs.length; i += 1) {
|
||||
key = w3cs.key(i);
|
||||
val = w3cs.getItem(key);
|
||||
json[key] = val;
|
||||
}
|
||||
w3cs.clear();
|
||||
|
||||
Object.keys(json).forEach(function (key) {
|
||||
jss.set(key, json[key]);
|
||||
});
|
||||
|
||||
jss._store.setItem('_json-storage-namespaced_', true);
|
||||
}
|
||||
|
||||
function JsonStorage(w3cStorage, namespace) {
|
||||
// called without new or create
|
||||
// global will be undefined
|
||||
if (!this) {
|
||||
return new JsonStorage(w3cStorage, namespace);
|
||||
}
|
||||
|
||||
// if we didn't always add at least the delimeter
|
||||
// then if a keyname with the delim, it would be more
|
||||
// complicated to figure it out
|
||||
this._namespace = delim;
|
||||
this._namespace += (namespace || 'jss');
|
||||
|
||||
this._store = w3cStorage;
|
||||
this._keysAreDirty = true;
|
||||
if (!this._store.getItem('_json-storage-namespaced_')) {
|
||||
upgradeStorage(this, w3cStorage);
|
||||
}
|
||||
}
|
||||
Store = JsonStorage;
|
||||
|
||||
db.prototype.clear = function () {
|
||||
this.keysAreDirty = true;
|
||||
this.keys().forEach(function (uuid) {
|
||||
this.remove(uuid);
|
||||
Store.prototype.clear = function () {
|
||||
this._keysAreDirty = true;
|
||||
this.keys().forEach(function (key) {
|
||||
this.remove(key);
|
||||
}, this);
|
||||
};
|
||||
|
||||
db.prototype.remove = function (uuid) {
|
||||
this.keysAreDirty = true;
|
||||
this.db.removeItem(uuid);
|
||||
Store.prototype.remove = function (key) {
|
||||
this._keysAreDirty = true;
|
||||
this._store.removeItem(key + this._namespace);
|
||||
};
|
||||
|
||||
db.prototype.get = function (uuid) {
|
||||
return Parse(this.db.getItem(uuid));
|
||||
Store.prototype.get = function (key) {
|
||||
return Parse(this._store.getItem(key + this._namespace));
|
||||
};
|
||||
|
||||
db.prototype.set = function (uuid, val) {
|
||||
this.keysAreDirty = true;
|
||||
return this.db.setItem(uuid, Stringify(val));
|
||||
Store.prototype.set = function (key, val) {
|
||||
this._keysAreDirty = true;
|
||||
return this._store.setItem(key + this._namespace, Stringify(val));
|
||||
};
|
||||
|
||||
db.prototype.keys = function () {
|
||||
Store.prototype.keys = function () {
|
||||
var i
|
||||
, key
|
||||
, delimAt
|
||||
;
|
||||
|
||||
if (!this.keysAreDirty) {
|
||||
return this.__keys.concat([]);
|
||||
if (!this._keysAreDirty) {
|
||||
return this._keys.concat([]);
|
||||
}
|
||||
|
||||
this.__keys = [];
|
||||
for (i = 0; i < this.db.length; i += 1) {
|
||||
this.__keys.push(this.db.key(i));
|
||||
this._keys = [];
|
||||
for (i = 0; i < this._store.length; i += 1) {
|
||||
key = this._store.key(i) || '';
|
||||
|
||||
delimAt = key.lastIndexOf(this._namespace);
|
||||
// test if this key belongs to this widget
|
||||
if (-1 !== delimAt) {
|
||||
this._keys.push(key.substr(0, delimAt));
|
||||
}
|
||||
}
|
||||
this.keysAreDirty = false;
|
||||
this._keysAreDirty = false;
|
||||
|
||||
return this.__keys.concat([]);
|
||||
return this._keys.concat([]);
|
||||
};
|
||||
|
||||
db.prototype.size = function () {
|
||||
return this.db.length;
|
||||
Store.prototype.size = function () {
|
||||
return this._store.length;
|
||||
};
|
||||
|
||||
function create(w3cStorage) {
|
||||
return new JsonStorage(w3cStorage);
|
||||
Store.prototype.toJSON = function () {
|
||||
var json = {}
|
||||
;
|
||||
|
||||
this._keys.forEach(function (key) {
|
||||
json[key] = this.get(key);
|
||||
}, this);
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
Store.create = function (w3cStorage, namespace) {
|
||||
return new JsonStorage(w3cStorage, namespace);
|
||||
}
|
||||
|
||||
module.exports = create;
|
||||
module.exports = Store;
|
||||
}());
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "json-storage",
|
||||
"description": "A wrapper for storage engines which use the W3C Storage API",
|
||||
"keywords": ["ender", "localStorage", "sessionStorage", "globalStorage", "Storage"],
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/coolaj86/json-storage-js.git"
|
||||
|
@ -12,6 +12,7 @@
|
|||
"node": ">= v0.2.0"
|
||||
},
|
||||
"main": "index",
|
||||
"browserDependencies": {},
|
||||
"dependencies": {},
|
||||
"devDependencies": {}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
;
|
||||
|
||||
assert.equal(null, db.get('x'));
|
||||
assert.deepEqual([], db.keys());
|
||||
assert.deepEqual([], db.keys(), 'found keys in empty db: ' + JSON.stringify(db.keys()));
|
||||
db.clear();
|
||||
assert.equal(null, db.get('x'));
|
||||
|
||||
|
@ -23,5 +23,9 @@
|
|||
db.clear();
|
||||
assert.deepEqual([], db.keys());
|
||||
|
||||
db.set('a', 'b');
|
||||
assert.deepEqual(['a'], db.keys());
|
||||
assert.deepEqual({ 'a': 'b' }, db.toJSON());
|
||||
|
||||
console.log("Done! All tests pass.");
|
||||
}());
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
(function () {
|
||||
"use strict";
|
||||
|
||||
var localStorage = require('localStorage')
|
||||
, jsonStorage = require('../lib/')
|
||||
, assert = require('assert')
|
||||
, store0 = jsonStorage.create(localStorage, '0')
|
||||
, store1 = jsonStorage.create(localStorage, '1')
|
||||
;
|
||||
|
||||
store0.set('foo', 'bar');
|
||||
store1.set('foo', 'baz');
|
||||
|
||||
assert.strictEqual('bar', store0.get('foo'));
|
||||
assert.strictEqual('baz', store1.get('foo'));
|
||||
|
||||
store0.remove('foo');
|
||||
|
||||
assert.strictEqual(null, store0.get('foo'));
|
||||
assert.strictEqual('baz', store1.get('foo'), 'after removing a value from store1, store0 lost it as well');
|
||||
|
||||
store0.clear();
|
||||
|
||||
assert.strictEqual(null, store0.get('foo'));
|
||||
assert.strictEqual('baz', store1.get('foo'), 'after clearing store0, store1 lost values');
|
||||
|
||||
console.log('[PASS] no assertions failed');
|
||||
}());
|
Loading…
Reference in New Issue