Integrate PouchORM package into Gallery project

This commit is contained in:
Timothy Farrell 2018-04-21 14:50:14 -05:00
parent ad779629a5
commit 8cfb7f575e
11 changed files with 23 additions and 269 deletions

View File

@ -14,7 +14,7 @@
"domvm": "~3.2.1", "domvm": "~3.2.1",
"exif-parser": "~0.1.9", "exif-parser": "~0.1.9",
"extract-text-webpack-plugin": "^3.0.2", "extract-text-webpack-plugin": "^3.0.2",
"frptools": "3.1.0", "frptools": "3.1.1",
"linear-partitioning": "0.3.2", "linear-partitioning": "0.3.2",
"pica": "~2.0.8", "pica": "~2.0.8",
"pouchdb-adapter-http": "~6.4.1", "pouchdb-adapter-http": "~6.4.1",
@ -24,6 +24,7 @@
"pouchdb-core": "~6.4.1", "pouchdb-core": "~6.4.1",
"pouchdb-find": "~6.4.1", "pouchdb-find": "~6.4.1",
"pouchdb-replication": "~6.4.1", "pouchdb-replication": "~6.4.1",
"pouchorm": "~1.0.0",
"router": "2.1.0", "router": "2.1.0",
"semantic-ui-reset": "^2.2.12", "semantic-ui-reset": "^2.2.12",
"semantic-ui-site": "^2.2.12", "semantic-ui-site": "^2.2.12",

View File

@ -1,4 +1,6 @@
import { PouchDB, TypeSpec } from '../services/db.js'; import { TypeSpec } from 'pouchorm';
import { PouchDB } from '../services/db.js';
import { ImageType } from '../data/image.js'; import { ImageType } from '../data/image.js';
import { extractID } from '../utils/conversion.js'; import { extractID } from '../utils/conversion.js';

View File

@ -1,4 +1,6 @@
import { PouchDB, TypeSpec } from '../services/db.js'; import { TypeSpec } from 'pouchorm';
import { PouchDB } from '../services/db.js';
import { sha256 } from '../utils/crypto.js'; import { sha256 } from '../utils/crypto.js';
import { blobToArrayBuffer } from '../utils/conversion.js'; import { blobToArrayBuffer } from '../utils/conversion.js';

View File

@ -1,4 +1,6 @@
import { PouchDB, TypeSpec } from '../services/db.js'; import { TypeSpec } from 'pouchorm';
import { PouchDB } from '../services/db.js';
import { blobToArrayBuffer, deepAssign } from '../utils/conversion.js'; import { blobToArrayBuffer, deepAssign } from '../utils/conversion.js';
import { backgroundTask } from '../utils/event.js'; import { backgroundTask } from '../utils/event.js';
import { FileType } from './file.js'; import { FileType } from './file.js';

View File

@ -4,11 +4,7 @@ import http from 'pouchdb-adapter-http';
import replication from 'pouchdb-replication'; import replication from 'pouchdb-replication';
import find from 'pouchdb-find'; import find from 'pouchdb-find';
import { log, warn } from './console.js'; import { PouchORM } from 'pouchorm';
import { isObject } from '../utils/comparators.js';
import { LiveArray } from '../utils/livearray.js';
import { Watcher } from '../utils/watcher.js';
import { deepAssign, pouchDocHash } from '../utils/conversion.js';
export const PouchDB = core export const PouchDB = core
.plugin(idb) .plugin(idb)
@ -16,168 +12,3 @@ export const PouchDB = core
.plugin(replication) .plugin(replication)
.plugin(find) .plugin(find)
.plugin(PouchORM); .plugin(PouchORM);
export class TypeSpec {
constructor(props) {
this._populateId(props);
Object.assign(this, { $links: {} }, props, { type: this._prefix });
}
static getUniqueID(doc) {
throw 'NotImplemented';
}
static validate(doc) {}
instantiate(doc) {
return new this._cls(docs);
}
_populateId(doc) {
if (!doc._id) {
doc._id = `${this._prefix}_${this._cls.getUniqueID(doc)}`;
}
return doc;
}
_hash() {
return pouchDocHash(this);
}
async delete() {
return await this.update({ _deleted: true });
}
async save() {
this._cls.validate(this);
const { rev } = await this._db.put(this);
this._rev = rev;
return this;
}
async addAttachment(attName, dataBlob) {
const { rev } = await this._db.putAttachment(
this._id,
attName,
this._rev,
dataBlob,
dataBlob.type
);
this._rev = rev;
return this;
}
async getAttachment(attName) {
return await this._db.getAttachment(this._id, attName);
}
async removeAttachment(attName) {
return await this._db.removeAttachment(this._id, attName, this._rev);
}
async update(props, save = true) {
deepAssign(this, props);
if (save) {
await this.save();
}
return this;
}
}
export function PouchORM(PouchDB) {
PouchDB.registerType = (name, cls, db) => {
const prefix = name.toLowerCase();
const _db = db || PouchDB(prefix);
_db.setMaxListeners(1000);
const _baseSelector = Object.freeze({
_id: { $gt: `${prefix}_0`, $lt: `${prefix}_\ufff0` }
});
const watch = Watcher(_db, _baseSelector, { include_docs: true });
if (!cls.hasOwnProperty('validate')) {
warn(`${cls.name} has no validation.`);
}
const instantiate = doc => new cls(doc);
async function find(idOrSelector, opts = {}) {
if (typeof idOrSelector === 'string') {
return instantiate(await _db.get(idOrSelector));
}
const isSelector = isObject(idOrSelector);
const selector = Object.assign(
isSelector && idOrSelector._deleted ? { _deleted: true } : { _deleted: { exists: false } },
isSelector ? idOrSelector : _baseSelector
);
if (opts.index) {
opts.use_index = [prefix, opts.index];
delete opts.index;
}
if (opts.live) {
opts.mapper = instantiate;
return LiveArray(_db, idOrSelector, opts);
}
return (await _db.find(Object.assign({ selector: idOrSelector }, opts))).docs.map(instantiate);
}
async function getOrCreate(props) {
let doc = await new cls(props);
try {
await doc.save();
} catch (e) {
if (e.status !== 409) {
throw e;
}
doc = await find(doc._id);
}
return doc;
}
async function _delete(id) {
try {
const doc = await find(id);
doc._deleted = true;
await _db.put(doc);
} catch (e) {
if (e.status !== 404) {
throw e;
}
}
}
async function _index(name, fields) {
return _db.createIndex({
index: {
ddoc: prefix,
fields,
name
}
});
}
Object.defineProperties(cls.prototype, {
_name: { value: name },
_prefix: { value: prefix },
_db: { value: _db },
_cls: { value: cls },
_baseSelector: { value: _baseSelector }
});
Object.defineProperties(cls, {
getOrCreate: { value: getOrCreate },
find: { value: find },
index: { value: _index },
delete: { value: _delete },
subscribe: { value: watch },
db: { value: _db },
name: { value: name },
prefix: { value: prefix },
selector: { value: _baseSelector }
});
return cls;
};
}

View File

@ -1,42 +0,0 @@
import { prop, computed, id } from 'frptools';
import { Watcher } from './watcher.js';
import { pouchDocArrayHash } from './conversion.js';
// LiveArray is a subscribable property function that always returns the db results that match the provided selector and calls subscribers when the results change.
export function LiveArray(db, selector, opts = {}) {
const mapper = opts.mapper || id;
opts.mapper && delete opts.mapper;
opts.include_docs = true;
const _watcher = Watcher(db, selector, opts);
let changeSub = null;
const ready = prop(false);
const data = prop({ docs: [] });
const docs = computed(r => r.docs.map(mapper), [data], pouchDocArrayHash);
const cleanup = () => {
docs.unsubscribeAll();
ready.unsubscribeAll();
if (changeSub) {
changeSub();
changeSub = null;
}
data({ docs: [] });
};
const refresh = async function refresh(...args) {
data(await db.find({ selector }));
};
docs.ready = ready;
docs.cleanup = cleanup;
docs.selector = selector;
docs.db = db;
refresh().then(() => {
changeSub = _watcher(refresh);
ready(true);
});
return docs;
}

View File

@ -1,44 +0,0 @@
import { log, error } from '../services/console.js';
export function Watcher(db, selector, opts) {
const subscribers = new Set();
let changes = null;
return function subscribe(fn) {
subscribers.add(fn);
if (subscribers.size === 1 && !changes) {
log(`Watching "${db.name}" for ${JSON.stringify(selector)}`);
changes = db
.changes(
Object.assign(
{
since: 'now',
live: true,
selector
},
opts
)
)
.on('change', change => {
const { id, deleted, doc } = change;
log(
`Change from "${db.name}" for ${JSON.stringify(selector)} ${id} ${deleted ? 'deleted' : ''}`
);
subscribers.forEach(s => s(id, !!deleted, doc));
})
.on('error', err => {
error(err);
subscribers.empty();
});
}
return () => {
subscribers.delete(fn);
if (subscribers.size === 0 && changes) {
log('Unwatching:', db, selector);
changes.cancel();
changes = null;
}
};
};
}

View File

@ -2,8 +2,7 @@
"name": "pouchorm", "name": "pouchorm",
"version": "1.0.0", "version": "1.0.0",
"description": "Document Abstraction Layer for PouchDB", "description": "Document Abstraction Layer for PouchDB",
"main": "lib/index.js", "main": "src/index.js",
"jsnext:main": "src/index.js",
"files": ["dist", "lib", "src"], "files": ["dist", "lib", "src"],
"scripts": { "scripts": {
"test": "node ../../bin/runTests.js ./" "test": "node ../../bin/runTests.js ./"

View File

@ -1,4 +1,5 @@
import { LiveArray } from './livearray.js'; import { LiveArray } from './livearray.js';
import { Watcher } from './watcher.js';
import { isObject } from './utils.js'; import { isObject } from './utils.js';
export function PouchORM(PouchDB) { export function PouchORM(PouchDB) {
@ -12,7 +13,7 @@ export function PouchORM(PouchDB) {
const watch = Watcher(_db, _baseSelector, { include_docs: true }); const watch = Watcher(_db, _baseSelector, { include_docs: true });
if (!cls.hasOwnProperty('validate')) { if (!cls.hasOwnProperty('validate')) {
warn(`${cls.name} has no validation.`); // warn(`${cls.name} has no validation.`);
} }
const instantiate = doc => new cls(doc); const instantiate = doc => new cls(doc);

View File

@ -1,4 +1,4 @@
import { pouchDocHash } from './utils.js'; import { pouchDocHash, deepAssign } from './utils.js';
export class TypeSpec { export class TypeSpec {
constructor(props) { constructor(props) {

View File

@ -11,11 +11,13 @@ const digestRoutes = (routes, baseUrl) =>
return '(' + regExStr.substring(1, regExStr.lastIndexOf('/')) + ')'; return '(' + regExStr.substring(1, regExStr.lastIndexOf('/')) + ')';
}); });
return { return Object.assign(
{
matcher: new RegExp(`^${baseUrl}${reg}$`), matcher: new RegExp(`^${baseUrl}${reg}$`),
_i: i, _i: i
...route },
}; route
);
}); });
export function Router(routes, baseUrl = '#') { export function Router(routes, baseUrl = '#') {