Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
2e4745b189 | |||
21098984f1 | |||
b2fdf0aba2 | |||
7b459f1f87 | |||
3a2ada1ce0 | |||
a881915cfe | |||
5e9e9d8a41 | |||
780b7583a1 | |||
c4efbba4da | |||
6f7b83ea15 | |||
1a9a10c24e | |||
9cdcdf98d9 |
119
README.md
Normal file
119
README.md
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
JsonStorage
|
||||||
|
====
|
||||||
|
|
||||||
|
A light, sensible abstraction for DOMStorage (such as localStorage).
|
||||||
|
|
||||||
|
Installation
|
||||||
|
===
|
||||||
|
|
||||||
|
Bower (Browser)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bower install json-storage
|
||||||
|
# or
|
||||||
|
wget https://git.coolaj86.com/coolaj86/json-storage.js/raw/branch/master/json-storage.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Node.JS (Server)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install -S localStorage json-storage
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage
|
||||||
|
===
|
||||||
|
|
||||||
|
Made for Node.js and Bower (browser-side).
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var localStorage = require('localStorage')
|
||||||
|
, JsonStorage = require('json-storage').JsonStorage
|
||||||
|
, store = JsonStorage.create(localStorage, 'my-widget-namespace', { stringify: true })
|
||||||
|
, myValue = {
|
||||||
|
foo: "bar"
|
||||||
|
, baz: "quux"
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
store.set('myKey', myValue);
|
||||||
|
myValue = store.get('myKey');
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTE: When using with Node and the `localStorage` module,
|
||||||
|
you may wish to pass the `{ stringify: false }` option to prevent double stringification.
|
||||||
|
|
||||||
|
API
|
||||||
|
===
|
||||||
|
|
||||||
|
* `JsonStorage.create(DOMStorage, namespace, opts)`
|
||||||
|
* `DOMStorage` should be globalStorage, sessionStorage, or localStorage. Defaults to window.localStorage if set to `null`.
|
||||||
|
* `namespace` is optional string which allows multiple non-conflicting storage containers. For example you could pass two widgets different storage containers and not worry about naming conflicts:
|
||||||
|
* `Gizmos.create(JsonStorage.create(null, 'my-gizmos'))`
|
||||||
|
* `Gadgets.create(JsonStorage.create(null, 'my-gadgets'))`
|
||||||
|
* Namespacing can be turned off by explicitly setting `false`
|
||||||
|
* `Gadgets.create(JsonStorage.create(null, false))`
|
||||||
|
* `opts`
|
||||||
|
* `stringify` set to `false` in `node` to avoid double stringifying
|
||||||
|
* `store.get(key)`
|
||||||
|
* `store.set(key, value)`
|
||||||
|
* `store.remove(key)`
|
||||||
|
* `store.clear()`
|
||||||
|
* `store.keys()`
|
||||||
|
* `store.size()`
|
||||||
|
* `store.toJSON()`
|
||||||
|
* `JSON.stringify(store)`
|
||||||
|
|
||||||
|
**NOTE**: You cannot omit optional parameters. Use `null` if you want accepts the defaults for some things and provide a values for others. For example: `JsonStorage.create(null, null, { stringify: false })`
|
||||||
|
|
||||||
|
JSON / DOMStorage Conversion Gotchas
|
||||||
|
===
|
||||||
|
|
||||||
|
These notes do not reflect a bugs or defects in this library,
|
||||||
|
they're simply to inform you of a few 'gotchas' inherent in JSON / DOMStorage conversion.
|
||||||
|
|
||||||
|
99.999% of the time these gotchas shouldn't effect you in any way.
|
||||||
|
If they do, you're probably doing something wrong in the first place.
|
||||||
|
|
||||||
|
|
||||||
|
### `undefined` vs `null`
|
||||||
|
|
||||||
|
It is not valid to set `undefined` in JSON. So setting a key to `undefined` will remove it from the store.
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
store.set('existing-key', null);
|
||||||
|
null === store.get('existing-key');
|
||||||
|
null === store.get('non-existant-key');
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### `null` vs `"null"`
|
||||||
|
|
||||||
|
The special case of `null` as `"null"`, aka `"\"null\""`:
|
||||||
|
|
||||||
|
```
|
||||||
|
typeof null // object
|
||||||
|
typeof "null" // string
|
||||||
|
typeof "\"null\"" // string
|
||||||
|
```
|
||||||
|
|
||||||
|
`null`, and `"null"` both parse as `null` the "object", instead of one being the string (which would be `"\"null\""`).
|
||||||
|
|
||||||
|
```
|
||||||
|
JSON.parse(null) // null (object)
|
||||||
|
JSON.parse("null") // null (object)
|
||||||
|
JSON.parse("\"null\"") // 'null' (string)
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
```
|
||||||
|
JSON.parse('{ "foo": null }') // { foo: null }
|
||||||
|
JSON.parse('{ "foo": "null" }') // { foo: 'null' }
|
||||||
|
```
|
28
bower.json
Normal file
28
bower.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "json-storage",
|
||||||
|
"version": "2.0.1",
|
||||||
|
"homepage": "https://github.com/coolaj86/json-storage-js",
|
||||||
|
"authors": [
|
||||||
|
"AJ ONeal <coolaj86@gmail.com>"
|
||||||
|
],
|
||||||
|
"description": "A wrapper for storage engines which use the W3C Storage API",
|
||||||
|
"main": "json-storage.js",
|
||||||
|
"keywords": [
|
||||||
|
"dom",
|
||||||
|
"storage",
|
||||||
|
"json",
|
||||||
|
"w3c",
|
||||||
|
"localStorage",
|
||||||
|
"sessionStorage",
|
||||||
|
"globalStorage",
|
||||||
|
"Storage"
|
||||||
|
],
|
||||||
|
"license": "Apache2",
|
||||||
|
"ignore": [
|
||||||
|
"**/.*",
|
||||||
|
"node_modules",
|
||||||
|
"bower_components",
|
||||||
|
"test",
|
||||||
|
"tests"
|
||||||
|
]
|
||||||
|
}
|
@ -1,282 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var localStorage = require('localStorage')
|
|
||||||
, JsonStorage = require('json-storage')
|
|
||||||
, jsonStorage = JsonStorage(localStorage)
|
|
||||||
, db = jsonStorage
|
|
||||||
, uuidgen = require('node-uuid')
|
|
||||||
, schemamap = {}
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
||||||
function create(schema) {
|
|
||||||
var models = {};
|
|
||||||
|
|
||||||
function createModel(lsmodel, prefix, submodels, uuid) {
|
|
||||||
//
|
|
||||||
// New Model
|
|
||||||
//
|
|
||||||
function LModel(obj) {
|
|
||||||
var lmodel = this;
|
|
||||||
|
|
||||||
// Add any already-appended submodels
|
|
||||||
// TODO DRY this up
|
|
||||||
submodels.forEach(function (subname) {
|
|
||||||
var sub_ids = '_' + subname + '_ids'
|
|
||||||
, subs = obj[subname]
|
|
||||||
, suuid = schemamap[subname].uuid || 'uuid';
|
|
||||||
|
|
||||||
// Just in case the object is already created
|
|
||||||
if ('function' === typeof obj.__lookupGetter__(subname)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (false === Array.isArray(subs)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj[sub_ids] = [];
|
|
||||||
lmodel[sub_ids] = obj[sub_ids];
|
|
||||||
|
|
||||||
subs.forEach(function (sub) {
|
|
||||||
if (!sub[suuid]) {
|
|
||||||
sub[suuid] = uuidgen();
|
|
||||||
}
|
|
||||||
models[subname].set(sub[suuid], sub);
|
|
||||||
lmodel[sub_ids].push(sub[suuid]);
|
|
||||||
});
|
|
||||||
|
|
||||||
obj[subname] = undefined;
|
|
||||||
delete obj[subname];
|
|
||||||
});
|
|
||||||
|
|
||||||
// Copy object to this
|
|
||||||
Object.keys(obj).forEach(function (k) {
|
|
||||||
lmodel[k] = obj[k];
|
|
||||||
});
|
|
||||||
|
|
||||||
// create uuid if it has none
|
|
||||||
if (undefined === obj[uuid]) {
|
|
||||||
obj[uuid] = uuidgen();
|
|
||||||
lmodel[uuid] = obj[uuid];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A simple save method
|
|
||||||
LModel.prototype.save = function () {
|
|
||||||
lsmodel.set(this[uuid], this);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// getters for submodels
|
|
||||||
submodels.forEach(function (subname) {
|
|
||||||
var sub_ids = '_' + subname + '_ids';
|
|
||||||
|
|
||||||
LModel.prototype.__defineGetter__(subname, function () {
|
|
||||||
// not all submodels exist at "compile-time"
|
|
||||||
var submodel = models[subname]
|
|
||||||
, lmodel = this
|
|
||||||
, subs
|
|
||||||
, suuid = schemamap[subname].uuid || 'uuid';
|
|
||||||
|
|
||||||
lmodel[sub_ids] = lmodel[sub_ids] || [];
|
|
||||||
|
|
||||||
subs = submodel.some(lmodel[sub_ids]);
|
|
||||||
|
|
||||||
// modify collection, but leave as array with forEach
|
|
||||||
// XXX maybe create prototype with forEach
|
|
||||||
subs.add = function (obj) {
|
|
||||||
subs.push(obj);
|
|
||||||
if (undefined === obj[suuid]) {
|
|
||||||
submodel.add(obj);
|
|
||||||
} else {
|
|
||||||
submodel.set(obj[suuid], obj);
|
|
||||||
}
|
|
||||||
lmodel[sub_ids].push(obj[suuid]);
|
|
||||||
lmodel.save();
|
|
||||||
};
|
|
||||||
|
|
||||||
return subs;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return LModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// A database abstraction
|
|
||||||
function Model(model) {
|
|
||||||
var lsmodel = this
|
|
||||||
, prefix = model.name
|
|
||||||
, submodels = model.has_many || []
|
|
||||||
, uuid = model.uuid || 'uuid'
|
|
||||||
, LModel = createModel(lsmodel, prefix, submodels, uuid);
|
|
||||||
|
|
||||||
lsmodel.create = function (a, b, c) {
|
|
||||||
return new LModel(a, b, c)
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// clear
|
|
||||||
lsmodel.clear = function () {
|
|
||||||
return db.set(prefix + '-lsm', []);
|
|
||||||
};
|
|
||||||
|
|
||||||
// all keys
|
|
||||||
lsmodel.keys = function () {
|
|
||||||
return db.get(prefix + '-lsm');
|
|
||||||
};
|
|
||||||
|
|
||||||
// all items
|
|
||||||
lsmodel.all = function () {
|
|
||||||
var items = [];
|
|
||||||
lsmodel.keys().forEach(function (uuid) {
|
|
||||||
var item = lsmodel.get(uuid)
|
|
||||||
// TODO this should be a useless check
|
|
||||||
if (null === item) {
|
|
||||||
lsmodel.remove(uuid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
items.push(item);
|
|
||||||
});
|
|
||||||
return items;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO query accepts objects
|
|
||||||
lsmodel.query = function (obj) {
|
|
||||||
//
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
// pass in your filter function
|
|
||||||
lsmodel.filter = function (test) {
|
|
||||||
var all = lsmodel.all(),
|
|
||||||
results = [];
|
|
||||||
|
|
||||||
all.forEach(function (one) {
|
|
||||||
if (!test(one)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
results.push(one);
|
|
||||||
});
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
lsmodel.some = function (uuids) {
|
|
||||||
var results = [];
|
|
||||||
|
|
||||||
uuids.forEach(function (uuid) {
|
|
||||||
var one = lsmodel.get(uuid);
|
|
||||||
// push null elements to keep array indices
|
|
||||||
results.push(one);
|
|
||||||
});
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO query object
|
|
||||||
lsmodel.get = function (uuid) {
|
|
||||||
var item = db.get(uuid);
|
|
||||||
if (null === item) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new LModel(item);
|
|
||||||
};
|
|
||||||
|
|
||||||
lsmodel.set = function (uuid, val) {
|
|
||||||
var keys;
|
|
||||||
|
|
||||||
if (null === val || undefined === val) {
|
|
||||||
return db.remove(uuid);
|
|
||||||
}
|
|
||||||
keys = db.get(prefix + '-lsm') || [];
|
|
||||||
|
|
||||||
// add the key if it didn't exist
|
|
||||||
if (-1 === keys.indexOf(uuid)) {
|
|
||||||
keys.push(uuid);
|
|
||||||
db.set(prefix + '-lsm', keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
submodels.forEach(function (mod) {
|
|
||||||
var children = val[mod] || [];
|
|
||||||
|
|
||||||
// TODO decouple or create new objects
|
|
||||||
children.forEach(function (child) {
|
|
||||||
var e;
|
|
||||||
if (null === child) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ('string' === typeof child) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ('string' !== typeof child.uuid) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO other[mod].set(uuid, child);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
/*
|
|
||||||
console.log('\n\n val');
|
|
||||||
console.log(val);
|
|
||||||
console.log('\n\n');
|
|
||||||
*/
|
|
||||||
db.set(uuid, val)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remove an item from the table list and the db
|
|
||||||
lsmodel.remove = function (uuid) {
|
|
||||||
var keys = db.get(prefix + '-lsm')
|
|
||||||
, i;
|
|
||||||
|
|
||||||
keys.forEach(function (key, j) {
|
|
||||||
if (key === uuid) {
|
|
||||||
i = j;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (undefined === i) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
db.remove(uuid);
|
|
||||||
keys.splice(i,1);
|
|
||||||
db.set(prefix + '-lsm', keys);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remove all objects from the table list and db
|
|
||||||
lsmodel.clear = function () {
|
|
||||||
lsmodel.keys().forEach(function (uuid) {
|
|
||||||
db.remove(uuid);
|
|
||||||
});
|
|
||||||
db.set(prefix + '-lsm', []);
|
|
||||||
};
|
|
||||||
|
|
||||||
lsmodel.add = function (val) {
|
|
||||||
var obj = lsmodel.create(val);
|
|
||||||
obj.save();
|
|
||||||
return obj;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO compare versions of schema
|
|
||||||
db.set('schema-lsm', schema);
|
|
||||||
|
|
||||||
schema.forEach(function (scheme) {
|
|
||||||
var table = scheme.name
|
|
||||||
, x = db.get(table + '-lsm')
|
|
||||||
;
|
|
||||||
|
|
||||||
// Pre-create the "models" tableshould always have
|
|
||||||
schemamap[scheme.name] = scheme;
|
|
||||||
if (!Array.isArray(x)) {
|
|
||||||
db.set(table + '-lsm', []);
|
|
||||||
}
|
|
||||||
|
|
||||||
models[scheme.name] = new Model(scheme);
|
|
||||||
});
|
|
||||||
|
|
||||||
return models;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = create;
|
|
||||||
}());
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"author": "AJ ONeal <coolaj86@gmail.com> (http://coolaj86.info)",
|
|
||||||
"name": "json-storage-model",
|
|
||||||
"description": "An abstraction for models to be stored in json-storage",
|
|
||||||
"keywords": ["ender", "model", "json-storage", "localStorage", "sessionStorage", "globalStorage", "Storage"],
|
|
||||||
"version": "0.9.1",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "git://github.com/coolaj86/json-storage-js.git"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= v0.2.0"
|
|
||||||
},
|
|
||||||
"main": "index",
|
|
||||||
"dependencies": {
|
|
||||||
"json-storage": ">= 1.0.0"
|
|
||||||
, "node-uuid": ">= 0.0.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {}
|
|
||||||
}
|
|
@ -1,247 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var Model = require('json-storage-model')
|
|
||||||
, assert = require('assert')
|
|
||||||
, schema
|
|
||||||
, lsmAll = {}
|
|
||||||
, Returns
|
|
||||||
, Batches;
|
|
||||||
|
|
||||||
schema = [
|
|
||||||
{
|
|
||||||
name: "returns",
|
|
||||||
has_many: ['batches'],
|
|
||||||
uuid: "key"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "batches",
|
|
||||||
has_many: ['products']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "products"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
function setup() {
|
|
||||||
lsmAll = new Model(schema);
|
|
||||||
//console.log(lsmAll);
|
|
||||||
Returns = lsmAll.returns;
|
|
||||||
Batches = lsmAll.batches;
|
|
||||||
}
|
|
||||||
|
|
||||||
function empty() {
|
|
||||||
assert.deepEqual([], Returns.keys());
|
|
||||||
assert.deepEqual([], Returns.all());
|
|
||||||
assert.deepEqual([], Returns.some([]));
|
|
||||||
assert.deepEqual([null], Returns.some(['x']));
|
|
||||||
assert.deepEqual([null, null], Returns.some(['x','y']));
|
|
||||||
assert.deepEqual(null, Returns.get('nada'));
|
|
||||||
Returns.clear();
|
|
||||||
assert.deepEqual([], Returns.keys());
|
|
||||||
assert.deepEqual([], Returns.all());
|
|
||||||
assert.deepEqual([], Returns.some([]));
|
|
||||||
assert.deepEqual([null], Returns.some(['x']));
|
|
||||||
assert.deepEqual([null, null], Returns.some(['x','y']));
|
|
||||||
assert.deepEqual(null, Returns.get('nada'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function errors() {
|
|
||||||
var errcount = 0;
|
|
||||||
// TODO make throw
|
|
||||||
try {
|
|
||||||
Returns.get();
|
|
||||||
} catch(e) {
|
|
||||||
errcount += 1;
|
|
||||||
}
|
|
||||||
//assert.equal(1, errcount);
|
|
||||||
console.log('skipped `throw error on get(undefined)`');
|
|
||||||
|
|
||||||
try {
|
|
||||||
Returns.some();
|
|
||||||
} catch(e) {
|
|
||||||
errcount += 1;
|
|
||||||
}
|
|
||||||
assert.equal(1, errcount);
|
|
||||||
|
|
||||||
Returns.some([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWithoutKey() {
|
|
||||||
var ret = Returns.create({
|
|
||||||
memo: "031811-IHC",
|
|
||||||
})
|
|
||||||
, ret2;
|
|
||||||
|
|
||||||
assert.ok(ret.key);
|
|
||||||
assert.equal("031811-IHC", ret.memo);
|
|
||||||
|
|
||||||
ret2 = Returns.get(ret.key);
|
|
||||||
assert.equal(null, ret2);
|
|
||||||
|
|
||||||
ret.test1 = true;
|
|
||||||
ret.save();
|
|
||||||
|
|
||||||
ret2 = Returns.get(ret.key);
|
|
||||||
assert.equal("031811-IHC", ret2.memo);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createWithKey() {
|
|
||||||
var ret = Returns.create({
|
|
||||||
key: "ajs-test",
|
|
||||||
memo: "031811-IHC",
|
|
||||||
})
|
|
||||||
, ret2;
|
|
||||||
|
|
||||||
ret2 = Returns.get(ret.key);
|
|
||||||
assert.ok(!ret2);
|
|
||||||
|
|
||||||
assert.equal("ajs-test", ret.key);
|
|
||||||
ret.save();
|
|
||||||
|
|
||||||
ret2 = Returns.get(ret.key);
|
|
||||||
assert.ok(ret2.memo);
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertRemove() {
|
|
||||||
var keys = []
|
|
||||||
, all = []
|
|
||||||
, ids = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
|
||||||
, k;
|
|
||||||
|
|
||||||
Returns.clear();
|
|
||||||
|
|
||||||
// add
|
|
||||||
function populate() {
|
|
||||||
all = [];
|
|
||||||
keys = [];
|
|
||||||
ids.forEach(function (id, i) {
|
|
||||||
// this is private logic
|
|
||||||
all.push({ key: id, _batches_ids: [] });
|
|
||||||
keys.push(all[i].key);
|
|
||||||
Returns.create(all[i]).save();
|
|
||||||
});
|
|
||||||
assert.deepEqual(all, Returns.all());
|
|
||||||
assert.deepEqual(keys, Returns.keys());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from first
|
|
||||||
populate();
|
|
||||||
for (k = 0; k < ids.length; k += 1) {
|
|
||||||
Returns.remove(ids[k]);
|
|
||||||
}
|
|
||||||
assert.deepEqual([], Returns.all());
|
|
||||||
assert.deepEqual([], Returns.keys());
|
|
||||||
|
|
||||||
|
|
||||||
// Remove from last
|
|
||||||
populate();
|
|
||||||
for (k = ids.length - 1; k >= 0; k -= 1) {
|
|
||||||
Returns.remove(ids[k]);
|
|
||||||
}
|
|
||||||
assert.deepEqual([], Returns.all());
|
|
||||||
assert.deepEqual([], Returns.keys());
|
|
||||||
|
|
||||||
// Remove from middle
|
|
||||||
populate();
|
|
||||||
ids.sort(function () { return 0.5 - Math.random(); });
|
|
||||||
for (k = ids.length - 1; k >= 0; k -= 1) {
|
|
||||||
Returns.remove(ids[k]);
|
|
||||||
}
|
|
||||||
assert.deepEqual([], Returns.all());
|
|
||||||
assert.deepEqual([], Returns.keys());
|
|
||||||
}
|
|
||||||
|
|
||||||
// testing all, keys, some, filter
|
|
||||||
function all() {
|
|
||||||
var keys = [],
|
|
||||||
all = [],
|
|
||||||
some,
|
|
||||||
ids = [];
|
|
||||||
|
|
||||||
Returns.clear();
|
|
||||||
assert.deepEqual(all, Returns.all());
|
|
||||||
assert.deepEqual(keys, Returns.keys());
|
|
||||||
|
|
||||||
all.push({ key: "one", memo: "3131-mtn", _batches_ids: [] });
|
|
||||||
keys.push(all[0].key);
|
|
||||||
Returns.create(all[0]).save();
|
|
||||||
|
|
||||||
all.push({ key: "two", memo: "3232-hlt", _batches_ids: [] });
|
|
||||||
keys.push(all[1].key);
|
|
||||||
Returns.create(all[1]).save();
|
|
||||||
|
|
||||||
all.push({ key: "three", memo: "4123-hbc", _batches_ids: [] });
|
|
||||||
keys.push(all[2].key);
|
|
||||||
Returns.create(all[2]).save();
|
|
||||||
|
|
||||||
all.push({ key: "four", memo: "4441-dtf", _batches_ids: [] });
|
|
||||||
keys.push(all[3].key);
|
|
||||||
Returns.create(all[3]).save();
|
|
||||||
|
|
||||||
all.push({ key: "five", memo: "4412-abn", _batches_ids: [] });
|
|
||||||
keys.push(all[4].key);
|
|
||||||
Returns.create(all[4]).save();
|
|
||||||
|
|
||||||
some = Returns.filter(function (ret) {
|
|
||||||
return /^4\d/.test(ret.memo);
|
|
||||||
});
|
|
||||||
assert.equal(3, some.length);
|
|
||||||
|
|
||||||
some.forEach(function (one) {
|
|
||||||
ids.push(one.key);
|
|
||||||
});
|
|
||||||
assert.deepEqual(some, Returns.some(ids));
|
|
||||||
|
|
||||||
assert.deepEqual(all, Returns.all());
|
|
||||||
assert.deepEqual(keys, Returns.keys());
|
|
||||||
assert.deepEqual(all.slice(1,3), Returns.some(["two", "three"]));
|
|
||||||
|
|
||||||
assert.deepEqual(keys, Returns.keys());
|
|
||||||
|
|
||||||
console.log('skipping not-implemented `query`');
|
|
||||||
}
|
|
||||||
|
|
||||||
function relate() {
|
|
||||||
var batches = [
|
|
||||||
{
|
|
||||||
uuid: "a",
|
|
||||||
name: "chuck",
|
|
||||||
_products_ids: []
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "daryl",
|
|
||||||
_products_ids: []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
, ret = Returns.create({})
|
|
||||||
, batch;
|
|
||||||
|
|
||||||
// Add relation
|
|
||||||
ret.save();
|
|
||||||
ret = Returns.get(ret.key);
|
|
||||||
ret.batches.add(batches[0]);
|
|
||||||
assert.deepEqual(Batches.all()[0], batches[0]);
|
|
||||||
assert.deepEqual(ret.batches[0], batches[0]);
|
|
||||||
ret.save();
|
|
||||||
|
|
||||||
// create with an existing relation
|
|
||||||
ret = Returns.create({ batches: batches });
|
|
||||||
batches[1].uuid = ret.batches[1].uuid;
|
|
||||||
|
|
||||||
console.log('skipping assert which requires visual inspection');
|
|
||||||
//assert.deepEqual(batches, ret.batches);
|
|
||||||
//console.log(Batches.all());
|
|
||||||
}
|
|
||||||
|
|
||||||
setup();
|
|
||||||
empty();
|
|
||||||
errors();
|
|
||||||
createWithoutKey();
|
|
||||||
createWithKey();
|
|
||||||
insertRemove();
|
|
||||||
all();
|
|
||||||
relate();
|
|
||||||
|
|
||||||
console.log("All tests passed");
|
|
||||||
}());
|
|
@ -34,6 +34,12 @@
|
|||||||
return new JsonStorage(w3cStorage, namespace, opts);
|
return new JsonStorage(w3cStorage, namespace, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!w3cStorage) {
|
||||||
|
w3cStorage = window.localStorage;
|
||||||
|
} else if ('function' !== typeof w3cStorage.getItem) {
|
||||||
|
throw new Error('You must supply a W3C DOM Storage mechanism such as window.localStorage or window.sessionStorage');
|
||||||
|
}
|
||||||
|
|
||||||
me._opts = opts || {};
|
me._opts = opts || {};
|
||||||
if (false === me._opts.stringify) {
|
if (false === me._opts.stringify) {
|
||||||
me._stringify = false;
|
me._stringify = false;
|
||||||
@ -46,6 +52,9 @@
|
|||||||
// complicated to figure it out
|
// complicated to figure it out
|
||||||
this._namespace = delim;
|
this._namespace = delim;
|
||||||
this._namespace += (namespace || 'jss');
|
this._namespace += (namespace || 'jss');
|
||||||
|
if (false === namespace) {
|
||||||
|
this._namespace = '';
|
||||||
|
}
|
||||||
|
|
||||||
this._store = w3cStorage;
|
this._store = w3cStorage;
|
||||||
this._keysAreDirty = true;
|
this._keysAreDirty = true;
|
||||||
@ -101,7 +110,7 @@
|
|||||||
|
|
||||||
delimAt = key.lastIndexOf(this._namespace);
|
delimAt = key.lastIndexOf(this._namespace);
|
||||||
// test if this key belongs to this widget
|
// test if this key belongs to this widget
|
||||||
if (-1 !== delimAt) {
|
if (!this._namespace || (-1 !== delimAt)) {
|
||||||
this._keys.push(key.substr(0, delimAt));
|
this._keys.push(key.substr(0, delimAt));
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,176 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var Future = require('future')
|
|
||||||
, __id_ = 'id'
|
|
||||||
, nameToString = function () {
|
|
||||||
return this.name;
|
|
||||||
};
|
|
||||||
|
|
||||||
function RowType(table) {
|
|
||||||
var name = table.name,
|
|
||||||
columns = table.columns,
|
|
||||||
klass;
|
|
||||||
|
|
||||||
klass = function (id, arr) {
|
|
||||||
/*
|
|
||||||
// Extending Array doesn't work very well
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
arr.forEach(function (item, i) {
|
|
||||||
self[i] = item;
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
if (!(__id_ in this)) {
|
|
||||||
this[__id_] = id;
|
|
||||||
}
|
|
||||||
this.arr = arr;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO make more directive-like
|
|
||||||
// TODO create a delegates_to directive
|
|
||||||
klass.prototype.toString = table.toString || nameToString;
|
|
||||||
|
|
||||||
columns.forEach(function (column, i) {
|
|
||||||
klass.prototype.__defineGetter__(column, function () {
|
|
||||||
return this.arr[i];
|
|
||||||
//return this[i];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return klass;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createIndexes(tables) {
|
|
||||||
Object.keys(tables).forEach(function (name) {
|
|
||||||
var table = tables[name],
|
|
||||||
index,
|
|
||||||
krow;
|
|
||||||
|
|
||||||
if (!table.columns) {
|
|
||||||
console.log("missing columns");
|
|
||||||
console.log(table);
|
|
||||||
}
|
|
||||||
|
|
||||||
table.klass = RowType(table);
|
|
||||||
|
|
||||||
index = table.indexed = {};
|
|
||||||
|
|
||||||
table.rows.forEach(function (row, z) {
|
|
||||||
// TODO no need to index
|
|
||||||
var krow = new table.klass(z, row);
|
|
||||||
//krow.id = z;
|
|
||||||
index[krow[__id_]] = krow;
|
|
||||||
table.rows[z] = krow;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createRelationships(tables) {
|
|
||||||
Object.keys(tables).forEach(function (name) {
|
|
||||||
var table = tables[name],
|
|
||||||
relationships,
|
|
||||||
relHandlers = {};
|
|
||||||
|
|
||||||
// TODO create a delegates_to directive
|
|
||||||
relHandlers.belongs_to = function (relname) {
|
|
||||||
// TODO abstract name mutation
|
|
||||||
var relation = tables[relname + 's'],
|
|
||||||
relname_s = relname + '_id';
|
|
||||||
|
|
||||||
table.klass.prototype.__defineGetter__(relname, function () {
|
|
||||||
var fid = this[relname_s];
|
|
||||||
|
|
||||||
return relation.indexed[fid];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//relationships = table.relationships && table.relationships.belongs_to;
|
|
||||||
//relationships = relationships || [];
|
|
||||||
|
|
||||||
relationships = table.relationships || {};
|
|
||||||
|
|
||||||
Object.keys(relationships).forEach(function (relType) {
|
|
||||||
if (!relHandlers[relType]) {
|
|
||||||
console.log('relationship type "' + relType + '" is not supported');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
relationships[relType].forEach(relHandlers[relType]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function createRelate(tables) {
|
|
||||||
var future = Future()
|
|
||||||
;
|
|
||||||
|
|
||||||
createIndexes(tables);
|
|
||||||
createRelationships(tables);
|
|
||||||
//testRelationships(tables);
|
|
||||||
//console.log(Object.keys(data));
|
|
||||||
|
|
||||||
future.fulfill(null, tables);
|
|
||||||
|
|
||||||
return future.passable();
|
|
||||||
}
|
|
||||||
|
|
||||||
var Future = require('future')
|
|
||||||
, Join = require('Join')
|
|
||||||
;
|
|
||||||
|
|
||||||
function createGet(pathname, tables) {
|
|
||||||
var future = Future()
|
|
||||||
, join = Join()
|
|
||||||
, request = require('ahr2')
|
|
||||||
, db = {}
|
|
||||||
, errs = []
|
|
||||||
;
|
|
||||||
|
|
||||||
tables.forEach(function (tablename) {
|
|
||||||
var resource = pathname + '/' + tablename + ".json"
|
|
||||||
, req = request.get(resource)
|
|
||||||
;
|
|
||||||
|
|
||||||
req.when(function (err, xhr, data) {
|
|
||||||
if (err) {
|
|
||||||
console.log('error tables-get');
|
|
||||||
console.log(err);
|
|
||||||
console.log(err.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (global.Buffer && data instanceof global.Buffer) {
|
|
||||||
data = data.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO need to refactor AHR and fix JSON / text handling
|
|
||||||
try {
|
|
||||||
data = JSON.parse(data.toString());
|
|
||||||
} catch (e) {
|
|
||||||
// ignore, it was probably already parsed
|
|
||||||
}
|
|
||||||
// follow convention
|
|
||||||
tablename = tablename.replace('-','_');
|
|
||||||
db[tablename] = data;
|
|
||||||
});
|
|
||||||
|
|
||||||
join.add(req);
|
|
||||||
});
|
|
||||||
|
|
||||||
join.when(function () {
|
|
||||||
// TODO add timeout as error
|
|
||||||
if (0 == errs.length) {
|
|
||||||
errs = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
future.fulfill(errs, db);
|
|
||||||
});
|
|
||||||
|
|
||||||
return future.passable();
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
get: createGet
|
|
||||||
, relate: createRelate
|
|
||||||
};
|
|
||||||
}());
|
|
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"author": "AJ ONeal <coolaj86@gmail.com> (http://coolaj86.info)"
|
|
||||||
, "name": "json-tables"
|
|
||||||
, "description": "An abstraction for models to be stored in json-storage"
|
|
||||||
, "keywords": ["ender", "orm", "sql", "model", "json-storage", "localStorage", "sessionStorage", "globalStorage", "Storage"]
|
|
||||||
, "version": "0.7.1"
|
|
||||||
, "repository": {
|
|
||||||
"type": "git"
|
|
||||||
, "url": "git://github.com/coolaj86/json-storage-js.git"
|
|
||||||
}
|
|
||||||
, "engines": {
|
|
||||||
"node": ">= v0.2.0"
|
|
||||||
}
|
|
||||||
, "main": "index"
|
|
||||||
, "dependencies": {
|
|
||||||
"json-storage-model": ">= 0.9.0"
|
|
||||||
, "json-storage": ">= 1.0.0"
|
|
||||||
, "node-uuid": ">= 0.0.0"
|
|
||||||
}
|
|
||||||
, "devDependencies": {}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
JsonStorage
|
|
||||||
====
|
|
||||||
|
|
||||||
A light, sensible abstraction for DOMStorage (such as localStorage).
|
|
||||||
|
|
||||||
Installation
|
|
||||||
===
|
|
||||||
|
|
||||||
Bower (Browser)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bower install json-storage
|
|
||||||
# or
|
|
||||||
wget https://raw2.github.com/coolaj86/json-storage-js/master/lib/json-storage.js
|
|
||||||
```
|
|
||||||
|
|
||||||
Node.JS (Server)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install localStorage json-storage
|
|
||||||
```
|
|
||||||
|
|
||||||
Usage
|
|
||||||
===
|
|
||||||
|
|
||||||
Made for Node.js and Bower (browser-side).
|
|
||||||
|
|
||||||
var localStorage = require('localStorage')
|
|
||||||
, JsonStorage = require('json-storage').JsonStorage
|
|
||||||
, store = JsonStorage.create(localStorage, 'my-widget-namespace', { stringify: false })
|
|
||||||
, myValue = {
|
|
||||||
foo: "bar"
|
|
||||||
, baz: "quux"
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
store.set('myKey', myValue);
|
|
||||||
myValue = store.get('myKey');
|
|
||||||
|
|
||||||
NOTE: When using with Node and the `localStorage` module,
|
|
||||||
you may wish to pass the `{ stringify: false }` option to prevent double stringification.
|
|
||||||
|
|
||||||
API
|
|
||||||
===
|
|
||||||
|
|
||||||
* `JsonStorage.create(DOMStorage, namespace)`
|
|
||||||
* `DOMStorage` should be globalStorage, sessionStorage, or localStorage
|
|
||||||
* `namespace` is optional string which allows multiple non-conflicting storage containers
|
|
||||||
* `store.get(key)`
|
|
||||||
* `store.set(key, value)`
|
|
||||||
* `store.remove(key)`
|
|
||||||
* `store.clear()`
|
|
||||||
* `store.keys()`
|
|
||||||
* `store.size()`
|
|
||||||
* `store.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
|
|
||||||
===
|
|
||||||
|
|
||||||
These notes do not reflect a bugs or defects in this library,
|
|
||||||
they're simply to inform you of a few 'gotchas' inherent in JSON / DOMStorage conversion.
|
|
||||||
|
|
||||||
99.999% of the time these gotchas shouldn't effect you in any way.
|
|
||||||
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 store.
|
|
||||||
|
|
||||||
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`.
|
|
||||||
|
|
||||||
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.
|
|
@ -3,10 +3,10 @@
|
|||||||
"name": "json-storage",
|
"name": "json-storage",
|
||||||
"description": "A wrapper for storage engines which use the W3C Storage API",
|
"description": "A wrapper for storage engines which use the W3C Storage API",
|
||||||
"keywords": ["dom", "storage", "json", "w3c", "localStorage", "sessionStorage", "globalStorage", "Storage"],
|
"keywords": ["dom", "storage", "json", "w3c", "localStorage", "sessionStorage", "globalStorage", "Storage"],
|
||||||
"version": "2.0.0",
|
"version": "2.1.2",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/coolaj86/json-storage-js.git"
|
"url": "git://git.coolaj86.com/coolaj86/json-storage.js.git"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= v0.2.0"
|
"node": ">= v0.2.0"
|
Loading…
x
Reference in New Issue
Block a user