extract disk related actions to frgid & add tests
parent
a8030aa14e
commit
bc612290dc
|
|
@ -8,6 +8,7 @@
|
|||
"repository": "https://github.com/marcus13345/frigid.git",
|
||||
"devDependencies": {
|
||||
"@types/node": "^15.12.2",
|
||||
"chai": "^4.3.4",
|
||||
"typescript": "^4.3.2",
|
||||
"yarn": "^1.22.10"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
import { existsSync, readFileSync, writeFileSync } from "fs";
|
||||
import Serializable from "./Serializable.js";
|
||||
|
||||
const PERSIST_LOCATION = Symbol('PERSIST_LOCATION');
|
||||
export const RESTORE = Symbol('RESTORE');
|
||||
|
||||
export default class Frigid extends Serializable {
|
||||
static create(filename: string, ...args: any[]) {
|
||||
if(existsSync(filename)) {
|
||||
const instance = this.deserialize(readFileSync(filename));
|
||||
// TS is plain and simply wrong... symbols can be used to index object...
|
||||
// @ts-ignore
|
||||
instance[PERSIST_LOCATION] = filename;
|
||||
instance[RESTORE]();
|
||||
return instance;
|
||||
} else {
|
||||
const instance = new this(...args);
|
||||
// again... TS is wrong...
|
||||
// @ts-ignore
|
||||
instance[PERSIST_LOCATION] = filename;
|
||||
instance[RESTORE]();
|
||||
instance.sync();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
sync() {
|
||||
const data = this.serialize();
|
||||
// this is getting annoying...
|
||||
// @ts-ignore
|
||||
writeFileSync(this[PERSIST_LOCATION], data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
// import { Ubjson } from '@shelacek/ubjson';
|
||||
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
||||
import { reverseLookup } from './reverseLookup.js';
|
||||
|
||||
export default class Serializable {
|
||||
|
||||
|
|
@ -14,26 +15,21 @@ export default class Serializable {
|
|||
static INSTANCE_DECLARATION = '$$INSTANCE_ID';
|
||||
static INSTANCE_REFERENCE = '$$INSTANCE_REF';
|
||||
|
||||
// things that need to be stored only at runtime
|
||||
// are keyed with symbols to not interfere with
|
||||
// user code.
|
||||
static PERSIST_LOCATION = Symbol('PERSIST_LOCATION');
|
||||
static serializationDependencies(): any[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return JSON.stringify(this.toSerializableObject(), null, 2);
|
||||
}
|
||||
|
||||
static serializationDependencies(): any[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
static fromJson(str: string) {
|
||||
return this.fromSerializableObject(JSON.parse(str));
|
||||
}
|
||||
|
||||
// thisdoesnt operate recursively, it doesnt need to, because dependency
|
||||
// resoltion isnt required. we simply declare the dependencies.
|
||||
// so we never touch static serializationDependencies!
|
||||
// this doesnt operate recursively, it doesnt need to, because
|
||||
// dependency resoltion isnt required. we simply declare the
|
||||
// dependencies. so we never touch serializationDependencies!
|
||||
toSerializableObject() {
|
||||
const instances: Map<number, object> = new Map();
|
||||
|
||||
|
|
@ -197,57 +193,4 @@ export default class Serializable {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
async restore() {}
|
||||
|
||||
static createFromDisk(filename: string, ...args: any[]) {
|
||||
const filepath = createFilepath(filename);
|
||||
if(existsSync(filepath)) {
|
||||
const instance = this.deserialize(readFileSync(filepath));
|
||||
// TS is plain and simply wrong... symbols can be used to index object...
|
||||
// @ts-ignore
|
||||
instance[Serializable.PERSIST_LOCATION] = filepath;
|
||||
instance?.restore();
|
||||
return instance;
|
||||
} else {
|
||||
const instance = new this(...args);
|
||||
// again... TS is wrong...
|
||||
// @ts-ignore
|
||||
instance[Serializable.PERSIST_LOCATION] = filepath;
|
||||
instance?.updateDisk();
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
updateDisk(filepath?: string) {
|
||||
// if it hasnt yet been written to disk...
|
||||
// this can happen if the contrustor
|
||||
// was called outside of createFromDisk
|
||||
if(filepath) {
|
||||
// see above... TS7053 is just _wrong_. incorrect. thats not how JS works.
|
||||
// @ts-ignore
|
||||
this[Serializable.PERSIST_LOCATION] = createFilepath(filepath);
|
||||
}
|
||||
const data = this.serialize();
|
||||
// this is getting annoying...
|
||||
// @ts-ignore
|
||||
writeFileSync(this[Serializable.PERSIST_LOCATION], data);
|
||||
}
|
||||
}
|
||||
|
||||
function createFilepath(path: string) {
|
||||
return `data/${path}`;
|
||||
}
|
||||
|
||||
|
||||
function reverseLookup<K, V>(map: Map<K, V>, value: V): K {
|
||||
// console.log('searching for', value, 'in', map);
|
||||
for(const [k, v] of map) {
|
||||
if(v === value) {
|
||||
// console.log('found in key', k);
|
||||
return k;
|
||||
}
|
||||
}
|
||||
// console.log(value, 'not found')
|
||||
return null;
|
||||
}
|
||||
|
|
@ -1 +1,2 @@
|
|||
export {default as Serializable} from './Serializable.js';
|
||||
export {default as Serializable} from './Serializable.js';
|
||||
export {default as Frigid, RESTORE} from './Frigid.js'
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
export function reverseLookup<K, V>(map: Map<K, V>, value: V): K {
|
||||
// console.log('searching for', value, 'in', map);
|
||||
for (const [k, v] of map) {
|
||||
if (v === value) {
|
||||
// console.log('found in key', k);
|
||||
return k;
|
||||
}
|
||||
}
|
||||
// console.log(value, 'not found')
|
||||
return null;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"$$INSTANCE_ID": 0,
|
||||
"foo": "bar",
|
||||
"$$CLASS_NAME": "Test"
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import {Frigid, RESTORE} from '../out/index.js';
|
||||
import { existsSync, readFileSync, unlinkSync } from 'fs';
|
||||
import { expect } from 'chai';
|
||||
|
||||
const trackingData = {
|
||||
constructorCalls: 0,
|
||||
restoreCalls: 0
|
||||
}
|
||||
|
||||
class Test extends Frigid {
|
||||
foo = 'bar';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
trackingData.constructorCalls ++;
|
||||
}
|
||||
|
||||
[RESTORE]() {
|
||||
trackingData.restoreCalls ++;
|
||||
}
|
||||
}
|
||||
|
||||
const filepath = 'test.state.json';
|
||||
|
||||
if(existsSync(filepath)) {
|
||||
unlinkSync(filepath)
|
||||
}
|
||||
|
||||
const test = Test.create(filepath)
|
||||
|
||||
expect(test.sync.bind(test)).to.not.throw();
|
||||
|
||||
expect(existsSync(filepath)).to.be.true;
|
||||
expect(readFileSync(filepath).toString()).to.not.be.empty;
|
||||
|
||||
expect(trackingData.constructorCalls).to.equal(1);
|
||||
expect(trackingData.restoreCalls).to.equal(1);
|
||||
|
||||
const retest = Test.create(filepath);
|
||||
|
||||
expect(trackingData.constructorCalls).to.equal(1);
|
||||
expect(trackingData.restoreCalls).to.equal(2);
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
import { Serializable } from '../out/index.js'
|
||||
|
||||
class Sub extends Serializable {
|
||||
otherData = sharedObject;
|
||||
root;
|
||||
|
||||
static serializationDependencies() {
|
||||
return [Root];
|
||||
}
|
||||
}
|
||||
|
||||
class Root extends Serializable{
|
||||
stuff = sharedObject;
|
||||
child;
|
||||
|
||||
static serializationDependencies() {
|
||||
return [Sub];
|
||||
}
|
||||
|
||||
test() {
|
||||
return {
|
||||
circular: this.child.root === this,
|
||||
shared: this.stuff === this.child.otherData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sharedObject = {shared: 'data'}
|
||||
|
||||
const root = new Root();
|
||||
const sub = new Sub();
|
||||
|
||||
root.child = sub;
|
||||
sub.root = root;
|
||||
|
||||
console.clear();
|
||||
console.log('#'.repeat(process.stdout.columns));
|
||||
|
||||
console.log(root);
|
||||
const json = root.toJson();
|
||||
console.log(json);
|
||||
const obj = Root.fromJson(json);
|
||||
console.log(obj);
|
||||
const tests = obj.test();
|
||||
console.log(tests);
|
||||
|
||||
const passing = Object.values(tests).reduce((v, acc) => v && acc, true);
|
||||
if(!passing) {
|
||||
console.log('Some tests failed!');
|
||||
process.exit(1);
|
||||
};
|
||||
|
|
@ -1,57 +1,4 @@
|
|||
import { Serializable } from '../out/index.js'
|
||||
import './Serializable.js';
|
||||
import './Frigid.js';
|
||||
|
||||
class Sub extends Serializable {
|
||||
otherData = sharedObject;
|
||||
root;
|
||||
|
||||
static serializationDependencies() {
|
||||
return [Root];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Root extends Serializable{
|
||||
stuff = sharedObject;
|
||||
child;
|
||||
|
||||
static serializationDependencies() {
|
||||
return [Sub];
|
||||
}
|
||||
|
||||
test() {
|
||||
return {
|
||||
circular: this.child.root === this,
|
||||
shared: this.stuff === this.child.otherData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const sharedObject = {shared: 'data'}
|
||||
|
||||
const root = new Root();
|
||||
const sub = new Sub();
|
||||
|
||||
root.child = sub;
|
||||
sub.root = root;
|
||||
|
||||
console.clear();
|
||||
console.log('#'.repeat(process.stdout.columns));
|
||||
|
||||
|
||||
|
||||
console.log(root);
|
||||
const json = root.toJson();
|
||||
console.log(json);
|
||||
const obj = Root.fromJson(json);
|
||||
console.log(obj);
|
||||
const tests = obj.test();
|
||||
console.log(tests);
|
||||
|
||||
const passing = Object.values(tests).reduce((v, acc) => v && acc, true);
|
||||
if(!passing) {
|
||||
console.log('Some tests failed!');
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log('All tests Passed!');
|
||||
}
|
||||
console.log('All tests passed!');
|
||||
37
yarn.lock
37
yarn.lock
|
|
@ -6,6 +6,43 @@
|
|||
version "15.12.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d"
|
||||
|
||||
assertion-error@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
|
||||
|
||||
chai@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49"
|
||||
dependencies:
|
||||
assertion-error "^1.1.0"
|
||||
check-error "^1.0.2"
|
||||
deep-eql "^3.0.1"
|
||||
get-func-name "^2.0.0"
|
||||
pathval "^1.1.1"
|
||||
type-detect "^4.0.5"
|
||||
|
||||
check-error@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
|
||||
|
||||
deep-eql@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
|
||||
dependencies:
|
||||
type-detect "^4.0.0"
|
||||
|
||||
get-func-name@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
|
||||
|
||||
pathval@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d"
|
||||
|
||||
type-detect@^4.0.0, type-detect@^4.0.5:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||
|
||||
typescript@^4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805"
|
||||
|
|
|
|||
Reference in New Issue