Migrate to SQLite for persistence, and fix native modules in Electron
parent
63e7c883f3
commit
a5ffac8d75
@ -0,0 +1,17 @@
|
||||
detectedArch=`uname -m`
|
||||
|
||||
case "$detectedArch" in
|
||||
i?86) arch="x32" ;;
|
||||
x86_64) arch="x64" ;;
|
||||
esac
|
||||
|
||||
case "$OSTYPE" in
|
||||
darwin*) platform="darwin" ;;
|
||||
linux*) platform="linux" ;;
|
||||
esac
|
||||
|
||||
echo "Rebuilding SQLite3 for Electron... ($platform, $arch)"
|
||||
|
||||
npm rebuild --runtime="node" --target="5.1.0" --arch="$arch" sqlite3
|
||||
mkdir -p node_modules/sqlite3/lib/binding/electron-v1.0-$platform-$arch
|
||||
cp "node_modules/sqlite3/lib/binding/node-v47-$platform-$arch/node_sqlite3.node" "node_modules/sqlite3/lib/binding/electron-v1.0-$platform-$arch/node_sqlite3.node"
|
@ -0,0 +1,164 @@
|
||||
'use strict';
|
||||
|
||||
const Promise = require("bluebird");
|
||||
const knex = require("knex");
|
||||
const fs = Promise.promisifyAll(require("fs"));
|
||||
const matchObject = require("match-object");
|
||||
const uuid = require("uuid");
|
||||
const pick = require("object-pick");
|
||||
const snakeCase = require("snake-case");
|
||||
const rfr = require("rfr");
|
||||
|
||||
const knexErrors = rfr("lib/db/knex-error-type");
|
||||
|
||||
function loadCollection(db, collection) {
|
||||
return Promise.try(() => {
|
||||
return db(collection).select();
|
||||
}).then((results) => {
|
||||
return results.map((result) => {
|
||||
return Object.assign(JSON.parse(result.value), {
|
||||
$id: result.id
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = function(path) {
|
||||
let collections = {};
|
||||
|
||||
let db = knex({
|
||||
client: "sqlite3",
|
||||
connection: {
|
||||
filename: path
|
||||
},
|
||||
useNullAsDefault: true,
|
||||
debug: true
|
||||
});
|
||||
|
||||
function createTable(name) {
|
||||
return db.schema.createTable(name, function(table) {
|
||||
table.text("id");
|
||||
table.text("value");
|
||||
});
|
||||
}
|
||||
|
||||
function getCollection(name) {
|
||||
let indexes; // TODO
|
||||
|
||||
function findIdIndex(id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return collections[name].findIndex((item) => item.$id === id);
|
||||
}
|
||||
|
||||
return {
|
||||
find: function(query) {
|
||||
return collections[name].filter((item) => matchObject(query, item));
|
||||
},
|
||||
findOne: function(query) {
|
||||
let result = collections[name].find((item) => matchObject(query, item));
|
||||
|
||||
if (result == null) {
|
||||
throw new Error("No results found");
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
insert: function(object, options = {}) {
|
||||
/* Intentional mutation. */
|
||||
Object.assign(object, {$id: uuid.v4()});
|
||||
collections[name].push(object);
|
||||
|
||||
return db(name).insert({
|
||||
id: object.$id,
|
||||
value: JSON.stringify(object)
|
||||
}).then(() => {});
|
||||
},
|
||||
update: function(object, options = {}) {
|
||||
let index;
|
||||
if (index = findIdIndex(object.$id)) {
|
||||
if (options.patch === true) {
|
||||
Object.assign(collections[name][index], object);
|
||||
} else {
|
||||
collections[name][index] = object;
|
||||
}
|
||||
|
||||
return db(name).where({id: object.$id}).update({
|
||||
value: JSON.stringify(collections[name][index])
|
||||
}).then(() => {});
|
||||
} else {
|
||||
throw new Error("No such object exists");
|
||||
}
|
||||
},
|
||||
upsert: function(object, options = {}) {
|
||||
try {
|
||||
this.update(object, options);
|
||||
} catch (err) {
|
||||
this.insert(object, options);
|
||||
}
|
||||
},
|
||||
upsertBy: function(keys, object, options = {}) {
|
||||
let query = pick(object, keys);
|
||||
|
||||
try {
|
||||
let result = this.findOne(query);
|
||||
// FIXME: The following shouldn't be in the try block...
|
||||
object.$id = result.$id;
|
||||
return this.update(object, options);
|
||||
} catch (err) {
|
||||
return this.insert(object, options);
|
||||
}
|
||||
|
||||
},
|
||||
delete: function(object) {
|
||||
let index;
|
||||
if (index = findIdIndex(object.$id)) {
|
||||
collections[name].splice(index, 1);
|
||||
|
||||
return db(name).where({id: object.$id}).delete().then(() => {});
|
||||
} else {
|
||||
throw new Error("No such object exists");
|
||||
}
|
||||
},
|
||||
deleteBy: function(query) {
|
||||
let toRemove = collections[name].filter((item) => matchObject(query, item));
|
||||
collections[name] = collections[name].filter((item) => (toRemove.indexOf(item) !== -1));
|
||||
|
||||
return Promise.map(toRemove, (item) => {
|
||||
return db(name).where({id: item.$id}).delete();
|
||||
});
|
||||
},
|
||||
ensureIndex: function(property) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dbAPI = {
|
||||
close: function() {},
|
||||
collection: function(name) {
|
||||
return Promise.try(() => {
|
||||
let snakeCasedName = snakeCase(name);
|
||||
|
||||
if (collections[snakeCasedName] != null) {
|
||||
return getCollection(snakeCasedName);
|
||||
} else {
|
||||
return Promise.try(() => {
|
||||
return loadCollection(db, snakeCasedName);
|
||||
}).then((collectionData) => {
|
||||
collections[snakeCasedName] = collectionData;
|
||||
}).catch(knexErrors.sqlite.NoSuchTable, (err) => {
|
||||
collections[snakeCasedName] = [];
|
||||
return createTable(snakeCasedName);
|
||||
}).then(() => {
|
||||
return getCollection(snakeCasedName);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return dbAPI;
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
sqlite: {
|
||||
NoSuchTable: function(err) {
|
||||
return err.message.includes("SQLITE_ERROR: no such table:");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue