databank
Version:
Abstraction layer for JSON storage
502 lines (460 loc) • 13.5 kB
JavaScript
// memory.js
//
// In-memory storage of data
//
// Copyright 2011,2012 E14N https://e14n.com/
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var Step = require('step'),
databank = require('../databank'),
si = require('setimmediate'),
Databank = databank.Databank,
DatabankError = databank.DatabankError,
AlreadyExistsError = databank.AlreadyExistsError,
NoSuchThingError = databank.NoSuchThingError,
AlreadyConnectedError = databank.AlreadyConnectedError,
NotConnectedError = databank.NotConnectedError,
NoSuchItemError = databank.NoSuchItemError;
var MemoryDatabank = function(params) {
this.schema = params.schema || {};
this.data = params.data || {};
this.connected = false;
};
MemoryDatabank.prototype = new Databank();
MemoryDatabank.prototype.freeze = function(value) {
var dup, i;
switch (typeof value) {
case 'number':
return value;
break;
case 'object':
if (value instanceof Array) {
dup = new Array(value.length);
for (i = 0; i < value.length; i++) {
dup[i] = this.freeze(value[i]);
}
return dup;
}
// FALL THROUGH
default:
return JSON.stringify(value);
}
};
MemoryDatabank.prototype.melt = function(value) {
var dup, i;
switch (typeof value) {
case 'string':
return JSON.parse(value);
break;
case 'number':
return value;
break;
case 'object':
if (value instanceof Array) {
dup = new Array(value.length);
for (i = 0; i < value.length; i++) {
dup[i] = this.melt(value[i]);
}
return dup;
}
// FALL THROUGH
default:
throw new DatabankError("melt() argument not string, number, or array.");
}
};
MemoryDatabank.prototype.connect = function(params, onCompletion) {
var bank = this;
if (bank.connected) {
onCompletion(new AlreadyConnectedError());
return;
}
setImmediate(function() {
var type;
bank.types = {};
for (type in bank.schema) {
bank.types[type] = {};
}
bank.connected = true;
Step(
function() {
var key, group = this.group();
for (type in bank.data) {
for (key in bank.data[type]) {
bank.create(type, key, bank.data[type][key], group());
}
}
},
function(err, created) {
if (err) {
onCompletion(err);
} else {
onCompletion(null);
}
}
);
});
};
MemoryDatabank.prototype.disconnect = function(onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
delete bank.types;
// Always succeed
bank.connected = false;
onCompletion(null);
});
};
MemoryDatabank.prototype.create = function(type, id, value, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (id in bank.types[type]) {
onCompletion(new AlreadyExistsError(type, id), null);
} else {
bank.types[type][id] = bank.freeze(value);
onCompletion(null, value);
}
});
};
MemoryDatabank.prototype.update = function(type, id, value, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
onCompletion(new NoSuchThingError(type, id), null);
} else {
bank.types[type][id] = bank.freeze(value);
onCompletion(null, value);
}
});
};
MemoryDatabank.prototype.read = function(type, id, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
var value;
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
onCompletion(new NoSuchThingError(type, id), null);
} else {
value = bank.melt(bank.types[type][id]);
onCompletion(null, value);
}
});
};
MemoryDatabank.prototype.del = function(type, id, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
onCompletion(new NoSuchThingError(type, id));
} else {
delete bank.types[type][id];
onCompletion(null);
}
});
};
MemoryDatabank.prototype.save = function(type, id, value, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
bank.types[type][id] = bank.freeze(value);
onCompletion(null, value);
});
};
MemoryDatabank.prototype.search = function(type, criteria, onResult, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
// We have no indices, so search == scan
bank.scan(type,
function(value) {
if (bank.matchesCriteria(value, criteria)) {
onResult(value);
}
},
function(err) {
onCompletion(err);
});
};
MemoryDatabank.prototype.scan = function(type, onResult, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
var id, value;
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
for (id in bank.types[type]) {
value = bank.melt(bank.types[type][id]);
onResult(value);
}
onCompletion(null);
});
};
MemoryDatabank.prototype.readAll = function(type, ids, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
var i, id, value, map = {};
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
for (i = 0; i < ids.length; i++) {
id = ids[i];
if (!(id in bank.types[type])) {
map[id] = null;
} else {
map[id] = bank.melt(bank.types[type][id]);
}
}
onCompletion(null, map);
});
};
MemoryDatabank.prototype.incrBy = function(type, id, n, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
bank.types[type][id] = n;
onCompletion(null, n);
} else {
bank.types[type][id] += n;
onCompletion(null, bank.types[type][id]);
}
});
};
MemoryDatabank.prototype.append = function(type, id, toAppend, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
bank.types[type][id] = [bank.freeze(toAppend)];
} else {
bank.types[type][id].push(bank.freeze(toAppend));
}
onCompletion(null);
});
};
MemoryDatabank.prototype.prepend = function(type, id, toAppend, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
bank.types[type][id] = [bank.freeze(toAppend)];
} else {
bank.types[type][id].unshift(bank.freeze(toAppend));
}
onCompletion(null);
});
};
MemoryDatabank.prototype.item = function(type, id, index, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
onCompletion(new NoSuchThingError(type, id), null);
} else if (!(index in bank.types[type][id])) {
onCompletion(new DatabankError("Bad index: " + index), null);
} else {
onCompletion(null, bank.melt(bank.types[type][id][index]));
}
});
};
MemoryDatabank.prototype.slice = function(type, id, start, length, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
onCompletion(new NoSuchThingError(type, id), null);
} else {
onCompletion(null, bank.melt(bank.types[type][id].slice(start, length)));
}
});
};
MemoryDatabank.prototype.indexOf = function(type, id, item, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
onCompletion(new NoSuchThingError(type, id), null);
} else {
onCompletion(null, bank.types[type][id].indexOf(bank.freeze(item)));
}
});
};
MemoryDatabank.prototype.remove = function(type, id, item, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
var i;
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
onCompletion(new NoSuchThingError(type, id), null);
} else {
i = bank.types[type][id].indexOf(bank.freeze(item));
// Only remove if it's not already there
if (i !== -1) {
bank.types[type][id].splice(i, 1);
}
onCompletion(null);
}
});
};
MemoryDatabank.prototype.length = function(type, id, onCompletion) {
var bank = this;
if (!bank.connected) {
onCompletion(new NotConnectedError());
return;
}
setImmediate(function() {
var i;
if (!bank.types) {
bank.types = {};
}
if (!(type in bank.types)) {
bank.types[type] = {};
}
if (!(id in bank.types[type])) {
onCompletion(new NoSuchThingError(type, id), null);
} else if (!Array.isArray(bank.types[type][id])) {
onCompletion(new WrongTypeError(type, id, "array"));
} else {
onCompletion(null, bank.types[type][id].length);
}
});
};
module.exports = MemoryDatabank;