Created
January 1, 2012 21:22
-
-
Save arextar/1548381 to your computer and use it in GitHub Desktop.
A nodejs module for persisting, querying, and manipulating data
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
exports.store = function(stay){ | |
stay = require("./node-stay").store(stay); | |
var type = function(a){ | |
var t = typeof a; | |
return t === 'object' ? {}.toString.call(a).slice(8, -1).toLowerCase() : t; | |
}, | |
get = stay.get, set = stay.set, del = stay.del, exists = stay.exists | |
, it = {object:1, array:1} | |
function deepMap(obj, fn, pre){ | |
var ret = {}; | |
for(var x in obj){ | |
if(type(obj[x]) === "object"){ | |
ret[x] = deepMap(obj[x], fn, pre + "." + x); | |
} | |
else | |
{ | |
ret[x] = fn(obj[x], pre + "." + x); | |
} | |
} | |
return ret; | |
} | |
function flatten(obj, parent, pre){ | |
for(var x in obj){ | |
if(type(obj[x]) === "object"){ | |
flatten(obj[x], parent, (pre ? pre + "." : "") + x); | |
} | |
else | |
{ | |
parent[(pre ? pre + "." : "") + x] = obj[x]; | |
} | |
} | |
} | |
function Model(name, scheme, data){ | |
this.name = name; | |
this.scheme = scheme; | |
flatten(scheme, this.flat = {}, ""); | |
var d = {}, x; | |
flatten(data, d, ""); | |
for(x in d) this.set(x, d[x]); | |
} | |
function coerce(what, typ){ | |
return typ ? (type(typ) === 'string' ? storage.types[typ] : typ)(what) : what; | |
} | |
Model.prototype = { | |
get: function(prop){ | |
if(prop){ | |
return coerce(get(this.name + "." + prop), this.flat[prop]); | |
} | |
return deepMap(this.scheme, function(type, prop){ | |
return exists(prop) ? coerce(get(prop), type) : null; | |
}, this.name) | |
}, | |
set: function(prop, val){ | |
if(val === null){ | |
del(this.name + "." + prop); | |
} | |
else if(type(val) === 'object') | |
{ | |
deepMap(val, function(val, prop){ | |
set(prop, val); | |
}, this.name + "." + prop) | |
} | |
else if(type(prop) === 'object'){ | |
deepMap(prop, function(val, prop){ | |
set(prop, val); | |
}, this.name) | |
} | |
else | |
{ | |
set(this.name + "." + prop, val); | |
} | |
}, | |
has: function(prop){ | |
return exists(this.name + "." + prop); | |
}, | |
def: function(prop, val){ | |
this.has(prop) || this.set(prop, val); | |
}, | |
incr: function(prop, by){ | |
this.set(prop, this.get(prop) + (by || 1)); | |
}, | |
decr: function(prop,by){ | |
this.incr(prop, by?-by:-1); | |
}, | |
remove: function(prop){ | |
if(prop){ | |
del(this.name + "." + prop); | |
} | |
else | |
{ | |
var name = this.name + ".", x; | |
for(x in this.flat){ | |
del(name + x); | |
} | |
} | |
}, | |
rename: function(name){ | |
var n = this.name + ".", na = name + ".", x; | |
for(x in this.flat){ | |
set(na + x, get(n + x)); | |
} | |
this.remove(); | |
this.name = name; | |
} | |
} | |
function Collection(name, scheme){ | |
this.model = scheme; | |
Model.call(this, name, {length:"int"}, {}) | |
this.def("length", 0); | |
} | |
var proto = Collection.prototype = new Model, modelProto = Model.prototype; | |
proto.get = function(prop){ | |
if(!prop){ | |
for(var ret = [], l = this.size(), x = 0; x < l; x++){ | |
ret[x] = modelProto.get.call({name:this.name + "." + x, scheme: this.model}) | |
} | |
return ret; | |
} | |
return modelProto.get.call(this, prop) | |
} | |
proto.at = function(ind){ | |
return ind < this.size() ? new Model(this.name + "." + ind, this.model) : null; | |
} | |
proto.size = function(){ | |
return this.get("length"); | |
} | |
proto.add = function(obj){ | |
var ret = new Model(this.name + "." + this.size(), this.model, obj); | |
this.incr("length"); | |
return ret; | |
} | |
proto.remove = function(at){ | |
this.at(at).remove(); | |
for(var x = at + 1, l = this.size(); x < l; x++){ | |
this.at(x).rename(this.name + "." + (x - 1)); | |
} | |
this.decr("length"); | |
} | |
proto.each = function(fn){ | |
for(var i = 0, l = this.size(); i < l; i++){ | |
if(fn(this.at(i), i, this) === false) break; | |
} | |
} | |
proto.filter = function(filter, fn, single){ | |
if(typeof fn === 'boolean'){ | |
single = fn; | |
fn = null; | |
} | |
var ret = [], d = 0 | |
this.each(function(model, ind, c){ | |
if(filter(model)){ | |
ret[d++] = model; | |
fn && fn(model, ind, c); | |
if(single) return false; | |
} | |
}); | |
return single ? ret[0] : ret; | |
} | |
proto.is = function(prop, is, fn, single){ | |
return this.filter(function(model){ | |
return model.get(prop) === is; | |
}, fn, single); | |
} | |
proto.isnt = function(prop, is, fn, single){ | |
return this.filter(function(model){ | |
return model.get(prop) !== is; | |
}, fn, single); | |
} | |
proto.lt = function(prop, is, fn, single){ | |
return this.filter(function(model){ | |
return model.get(prop) < is; | |
}, fn, single); | |
} | |
proto.gt = function(prop, is, fn, single){ | |
return this.filter(function(model){ | |
return model.get(prop) > is; | |
}, fn, single); | |
} | |
proto.lte = function(prop, is, fn, single){ | |
return this.filter(function(model){ | |
return model.get(prop) <= is; | |
}, fn, single); | |
} | |
proto.gte = function(prop, is, fn, single){ | |
return this.filter(function(model){ | |
return model.get(prop) >= is; | |
}, fn, single); | |
} | |
var storage = { | |
model: function(name, scheme){ | |
return new Model(name, scheme); | |
}, | |
collection: function(name, scheme){ | |
return new Collection(name, scheme); | |
}, | |
data: stay.data, | |
types:{ | |
string: String, | |
"int": parseInt, | |
number: parseFloat, | |
date: function(str){return new Date(str)} | |
} | |
} | |
return storage; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var fs = require('fs'); | |
exports.store = function(fname){ | |
var data = {}, saveTO = null; | |
// Read the file contents, if the file exists, synchronously into the data object | |
try{ | |
fs.readFileSync(fname, 'utf8').split("\u0000").forEach(function(line){ | |
line = line.split("\u0001"); | |
data[line[0]] = line[1]; | |
}); | |
} | |
catch(e){} | |
// Function to make a string out of the data object | |
function stringify(){ | |
var dat = [], d = 0, x; | |
for(x in data) dat[d++] = x + "\u0001" + data[x]; | |
return dat.join("\u0000"); | |
} | |
// Function to save string to file async (a timeout is used so a block of modifying calls will all be modified in the file at the same time) | |
function save(){ | |
clearTimeout(saveTO); | |
saveTO = setTimeout(function(){ | |
fs.writeFile(fname, stringify()); | |
saveTO = null; | |
}, 13) | |
} | |
// When the process exits, make sure the file is saved | |
process.on("exit", function(){ | |
if(saveTO !== null){ | |
clearTimeout(saveTO); | |
fs.writeFileSync(fname, stringify()); | |
} | |
}) | |
return { | |
get: function(prop){ | |
return data[prop]; | |
}, | |
set: function(prop, val){ | |
data[prop] = val + ""; | |
save(); | |
}, | |
exists: function(prop){ | |
return typeof data[prop] !== "undefined"; | |
}, | |
del: function(prop){ | |
delete data[prop]; | |
save(); | |
}, | |
save: save, | |
data: data | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment