Fix preload and add includeData option

This commit is contained in:
moos 2019-12-31 14:33:54 -08:00
parent cddce9d394
commit 4d97bcbc45
5 changed files with 264 additions and 19 deletions

View File

@ -86,7 +86,7 @@ WordPOS.defaults = {
* include data files in preload * include data files in preload
* @type {boolean} * @type {boolean}
*/ */
includeData: false, // WIP includeData: false,
/** /**
* set to true to enable debug logging * set to true to enable debug logging
@ -316,10 +316,6 @@ For CLI usage and examples, see [bin/README](bin).
See [bench/README](bench). See [bench/README](bench).
## TODO
- implement `includeData` option for preload
## Changes ## Changes
See [CHANGELOG](./CHANGELOG.md). See [CHANGELOG](./CHANGELOG.md).

View File

@ -1,6 +1,6 @@
{ {
"name": "wordpos", "name": "wordpos",
"version": "2.0.0-beta.11", "version": "2.0.0-beta.13",
"description": "wordpos is a set of part-of-speech utilities for Node.js & browser using the WordNet database.", "description": "wordpos is a set of part-of-speech utilities for Node.js & browser using the WordNet database.",
"author": "Moos <mooster@42at.com>", "author": "Moos <mooster@42at.com>",
"keywords": [ "keywords": [
@ -69,8 +69,8 @@
"prepare": "npm run build", "prepare": "npm run build",
"pretest": "node tools/makeJsonDict.js index data", "pretest": "node tools/makeJsonDict.js index data",
"test": "npm run test-node && npm run test-browser", "test": "npm run test-node && npm run test-browser",
"test-node": "mocha test", "test-node": "mocha test --exclude test/browser_test.js",
"test-browser": "mocha test/wordpos_test --require @babel/register" "test-browser": "mocha test/browser_test test/wordpos_test --require @babel/register"
}, },
"license": "MIT" "license": "MIT"
} }

View File

@ -44,7 +44,8 @@ class BaseFile {
this.options.debug && console.timeEnd('index load ' + this.posName) this.options.debug && console.timeEnd('index load ' + this.posName)
return promise return promise
.then(exports => { .then(exports => {
this.file = exports.default this.file = exports.default;
return this;
}) })
.catch(err => { .catch(err => {
console.error(`Error loading "${this.type}" file ${this.filePath}.`, err); console.error(`Error loading "${this.type}" file ${this.filePath}.`, err);
@ -54,7 +55,7 @@ class BaseFile {
} }
ready(fn, args) { ready(fn, args) {
return this.load().then(() => fn.apply(this, args)); return this.load().then(res => fn && fn.apply(this, args) || res);
} }
} }

View File

@ -34,6 +34,10 @@ class WordPOS {
} }
} }
ready() {
return this.loaded || Promise.resolve();
}
initFiles() { initFiles() {
const keys = Object.keys(POS); const keys = Object.keys(POS);
const loadOne = (Comp, pos) => new Comp(this.options.dictPath, POS[pos], this.options); const loadOne = (Comp, pos) => new Comp(this.options.dictPath, POS[pos], this.options);
@ -44,7 +48,7 @@ class WordPOS {
this.dataFiles = reducer(loader(DataFile)); this.dataFiles = reducer(loader(DataFile));
if (this.options.preload) { if (this.options.preload) {
this.loaded = this.preloadIndexes(this.options.preload); this.loaded = this.preloadFiles(this.options.preload);
} }
} }
@ -61,23 +65,28 @@ class WordPOS {
* @param {string|Array} [pos] POS to load (default: all) * @param {string|Array} [pos] POS to load (default: all)
* @return {Promise.<index data>} * @return {Promise.<index data>}
*/ */
preloadIndexes(pos) { preloadFiles(pos) {
let file = this.indexFile[pos]; let promise = this._preload(this.indexFiles, pos);
let load = p => file.load(); if (this.options.includeData) {
promise = Promise.all([].concat(promise, this._preload(this.dataFiles, pos)))
.then(res => res.flat());
}
return promise;
}
_preload(files, pos) {
let load = p => files[p].load();
let promise; let promise;
if (!pos || pos === true) { // preload all if (!pos || pos === true) { // preload all
promise = Promise.all(Object.keys(POS).map(load)); promise = Promise.all(Object.keys(POS).map(load));
} }
else if (typeof pos === 'string' && file) { else if (typeof pos === 'string' && files[pos]) {
promise = load(pos); promise = load(pos);
} }
else if (pos instanceof Array) { else if (pos instanceof Array) {
promise = pos.forEach(pos => file && load(pos)); promise = Promise.all(pos.map(load));
} }
// TODO includeData
return promise || Promise.reject(new RangeError(`Unknown POS "${pos}" for preload.`)); return promise || Promise.reject(new RangeError(`Unknown POS "${pos}" for preload.`));
} }
@ -183,6 +192,8 @@ WordPOS.defaults = {
*/ */
WordPOS.stopwords = stopwords; WordPOS.stopwords = stopwords;
WordPOS.POS = POS;
// Export as CJS handled by Parcel, otherwise will get WordPOS.default // Export as CJS handled by Parcel, otherwise will get WordPOS.default
// if use: export default WordPOS; // if use: export default WordPOS;
module.exports = WordPOS; module.exports = WordPOS;

237
test/browser_test.js Normal file
View File

@ -0,0 +1,237 @@
/**
* browser_test.js
*
* test file for browser-specific functionality
*
* Usage:
* npm install mocha -g
* mocha browser_test.js -require @babel/register
*
* or
*
* npm test
*
* Copyright (c) 2012-2020 mooster@42at.com
* https://github.com/moos/wordpos
*
* Released under MIT license
*/
// used in src code to signal test mode
global.window = global.window || {};
global.window.__mocha = true;
var
chai = require('chai'),
_ = require('underscore'),
assert = chai.assert,
browser = process.browser = process.argv.includes('@babel/register'),
WordPOS = require('../src/wordpos'),
wordpos,
path = require('path'),
dictPath = browser ? path.resolve('./test/dict') : undefined;
const dictRequired = () => Object.keys(require.cache).filter(p => /\bdict\b/i.test(p));
if (!browser) {
throw new Error('Not in browser mode!');
}
chai.config.showDiff = true;
describe('options: preload', () => {
// clear require.cache before each test
beforeEach(() => {
dictRequired().forEach((m) => delete require.cache[m]);
});
it('preload: false', () => {
wordpos = new WordPOS({
preload: false,
dictPath: dictPath
});
return wordpos.ready().then(res => {
assert.equal(res, undefined);
assert.equal(dictRequired().length, 0);
});
});
it('preload: true', () => {
assert.equal(dictRequired().length, 0);
wordpos = new WordPOS({
preload: true,
dictPath: dictPath
});
return wordpos.ready().then(res => {
assert.equal(res.length, 4);
res.forEach(index => assert.equal(index.type, 'index'));
res.forEach(index => assert.equal(/\bdict.index\./.test(index.filePath), true));
let reqs = dictRequired();
assert.equal(reqs.length, 4);
reqs.forEach(req => assert.equal(/\bdict.index\./.test(req), true));
Object.values(WordPOS.POS).forEach(pos => assert.notEqual(reqs.join().indexOf(`index.${pos}.js`), -1));
});
});
it('preload: "r"', () => {
wordpos = new WordPOS({
preload: 'r',
dictPath: dictPath
});
return wordpos.ready().then(res => {
assert.equal(res.type, 'index');
assert.equal(res.posName, 'adv');
let reqs = dictRequired();
assert.equal(reqs.length, 1);
assert.equal(/index\.adv\.js/.test(reqs[0]), true);
});
});
it('preload: ["r","a"]', () => {
wordpos = new WordPOS({
preload: ['r','a'],
dictPath: dictPath
});
return wordpos.ready().then(res => {
assert.equal(res.length, 2);
// TODO -- order may NOT be always the same!!!
assert.equal(res[0].type, 'index');
assert.equal(res[0].posName, 'adv');
assert.equal(res[1].type, 'index');
assert.equal(res[1].posName, 'adj');
let reqs = dictRequired();
assert.equal(reqs.length, 2);
assert.equal(/index\.adv\.js/.test(reqs[0]), true);
assert.equal(/index\.adj\.js/.test(reqs[1]), true);
});
});
it('preload: "foo"', () => {
wordpos = new WordPOS({
preload: 'foo',
dictPath: dictPath
});
return wordpos.ready().then(res => {
// shouldn't get here
assert(false);
}).catch(err => {
assert.equal(err, 'RangeError: Unknown POS "foo" for preload.')
});
});
});
describe('options: preload with includeData', () => {
// clear require.cache before each test
beforeEach(() => {
dictRequired().forEach((m) => delete require.cache[m]);
});
it('preload: false', () => {
wordpos = new WordPOS({
preload: false,
includeData: true,
dictPath: dictPath
});
return wordpos.ready().then(res => {
assert.equal(res, undefined);
assert.equal(dictRequired().length, 0);
});
});
it('preload: true', () => {
assert.equal(dictRequired().length, 0);
wordpos = new WordPOS({
preload: true,
includeData: true,
dictPath: dictPath
});
return wordpos.ready().then(res => {
assert.equal(res.length, 8);
assert.equal(res.filter(m => m.type === 'index').length, 4);
assert.equal(res.filter(m => m.type === 'data').length, 4);
assert.equal(res.filter(m => /\bdict.index\./.test(m.filePath)).length, 4);
assert.equal(res.filter(m => /\bdict.data\./.test(m.filePath)).length, 4);
let reqs = dictRequired();
assert.equal(reqs.length, 8);
assert.equal(reqs.filter(m => /\bdict.index\./.test(m)).length, 4);
assert.equal(reqs.filter(m => /\bdict.data\./.test(m)).length, 4);
let reqsStr = reqs.join();
Object.values(WordPOS.POS).forEach(pos => {
assert.equal(reqs.filter(m => m.indexOf(`index.${pos}.js`) !== -1).length, 1);
assert.equal(reqs.filter(m => m.indexOf(`data.${pos}.js`) !== -1).length, 1);
});
});
});
it('preload: "r"', () => {
wordpos = new WordPOS({
preload: 'r',
includeData: true,
dictPath: dictPath
});
return wordpos.ready().then(res => {
assert.equal(res.length, 2);
assert.equal(res[0].type, 'index');
assert.equal(res[0].posName, 'adv');
assert.equal(res[1].type, 'data');
assert.equal(res[1].posName, 'adv');
let reqs = dictRequired();
assert.equal(reqs.length, 2);
assert.equal(/index\.adv\.js/.test(reqs[0]), true);
assert.equal(/data\.adv\.js/.test(reqs[1]), true);
});
});
it('preload: ["r","a"]', () => {
wordpos = new WordPOS({
preload: ['r','a'],
includeData: true,
dictPath: dictPath
});
return wordpos.ready().then(res => {
assert.equal(res.length, 4);
// TODO -- order may NOT be always the same!!!
assert.equal(res[0].type, 'index');
assert.equal(res[0].posName, 'adv');
assert.equal(res[1].type, 'index');
assert.equal(res[1].posName, 'adj');
assert.equal(res[2].type, 'data');
assert.equal(res[2].posName, 'adv');
assert.equal(res[3].type, 'data');
assert.equal(res[3].posName, 'adj');
let reqs = dictRequired();
assert.equal(reqs.length, 4);
assert.equal(/index\.adv\.js/.test(reqs[0]), true);
assert.equal(/index\.adj\.js/.test(reqs[1]), true);
assert.equal(/data\.adv\.js/.test(reqs[2]), true);
assert.equal(/data\.adj\.js/.test(reqs[3]), true);
});
});
it('preload: "foo"', () => {
wordpos = new WordPOS({
preload: 'foo',
includeData: true,
dictPath: dictPath
});
return wordpos.ready().then(res => {
// shouldn't get here
assert(false);
}).catch(err => {
assert.equal(err, 'RangeError: Unknown POS "foo" for preload.')
});
});
});