'use strict'; const taulut = { luokat: 'luokat', }; class Transaktio { peruttu = false; muutokset = []; constructor(tietokanta) { this.tietokanta = tietokanta; this.seuraavaId = tietokanta.seuraavaId; this.taulut = new Map; for (const taulu of tietokanta.taulut.keys()) { this.taulut.set(taulu, new Map); } } peru() { this.peruttu = true; } hae(taulu, id) { if (!this.taulut.has(taulu)) { throw new Error(`ei taulua ${taulu}`); } if (this.taulut.get(taulu).has(id)) { return this.taulut.get(taulu).get(id); } else { return this.tietokanta.taulut.get(taulu).get(id); } } lisää(taulu, sisältö) { if (this.peruttu) { throw new Error(`yritys lisätä rivi perutussa transaktiossa`); } if (!this.taulut.has(taulu)) { throw new Error(`ei taulua ${taulu}`); } const id = this.seuraavaId++; this.muutokset.push({ taulu, id, uusi: sisältö, }); this.taulut.get(taulu).set(id, sisältö); return id; } poista(taulu, id) { if (this.peruttu) { throw new Error(`yritys poistaa rivi perutussa transaktiossa`); } const vanha = this.hae(taulu, id); if (vanha === undefined) { throw new Error(`ei riviä ${id} taulussa ${taulu}`); } this.muutokset.push({ taulu, id, vanha, }); this.taulut.get(taulu).set(id, undefined); } } class Tietokanta { seuraavaId = 0; taulut = new Map; historia = []; constructor() { this.taulut.set(taulut.luokat, new Map); } transaktio(funktio) { const transaktio = new Transaktio(this); funktio(transaktio); return [this, this.suorita(transaktio)]; } suorita(transaktio) { if (transaktio.peruttu) { return []; } // Varmista, että invariantit ovat yhä totta for (const {taulu, id, vanha, uusi} of transaktio.muutokset) { } // Suorita muutokset for (const {taulu, id, uusi} of transaktio.muutokset) { if (uusi !== undefined) { this.taulut.get(taulu).set(id, uusi); } else { this.taulut.get(taulu).delete(id); } } this.historia.push({ muutokset: transaktio.muutokset, idMuutos: transaktio.seuraavaId - this.seuraavaId, }); this.seuraavaId = transaktio.seuraavaId; return transaktio.muutokset; } kumoa() { if (this.historia.length === 0) { return []; } const {muutokset, idMuutos} = this.historia.pop(); this.seuraavaId -= idMuutos; const kumotut = []; for (const {taulu, id, vanha, uusi} of muutokset) { if (vanha !== undefined) { this.taulut.get(taulu).set(id, vanha); } else { this.taulut.get(taulu).delete(id); } kumotut.push({ taulu, id, vanha: uusi, uusi: vanha, }); } return [this, kumotut]; } hae(taulu, id) { if (!this.taulut.has(taulu)) { throw new Error(`ei taulua ${taulu}`); } return this.taulut.get(taulu).get(id); } järjestyksessä(taulu, järjestys) { if (!this.taulut.has(taulu)) { throw new Error(`ei taulua ${taulu}`); } const taulukko = Array.from(this.taulut.get(taulu).entries()); taulukko.sort(([xId, x], [yId, y]) => { const vertaus = järjestys(x, y); if (vertaus < 0 || vertaus > 0) { return vertaus; } else { return xId - yId; } }); return taulukko.map(([id, _]) => id); } } const _tietokanta = new Tietokanta;