Last active
July 9, 2019 13:10
-
-
Save ArturoRodriguezRomero/356b1f0032f7a9457ec4f3055e66d9e1 to your computer and use it in GitHub Desktop.
Typescript class that keeps a history of previous values. Allows for GET, SET, UNDO, REDO, RESET, RESTORE.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Class that keeps history of a value ~ past values. | |
| */ | |
| export class Snapshot<T> { | |
| private readonly initial: T; | |
| private readonly value: T; | |
| private readonly history: Snapshot<T>[]; | |
| private readonly current: number; | |
| private constructor( | |
| value: T, | |
| initial: T, | |
| history: Snapshot<T>[] = [], | |
| current: number = 0 | |
| ) { | |
| this.initial = initial; | |
| this.value = value; | |
| this.history = history; | |
| this.current = current; | |
| } | |
| /** | |
| * Returns a new Snapshot object. | |
| * @param value Initial value. History first item. | |
| * @returns new Snapshot object with initial value. | |
| * @typeparam T type of value. | |
| */ | |
| public static create<T>(value: T): Snapshot<T> { | |
| return new Snapshot(value, value, [new Snapshot<T>(value, value)]); | |
| } | |
| /** | |
| * Adds item to history and returns a new Snapshot object. | |
| * @param value New value. History last item. | |
| * @returns new Snapshot object with value as current. | |
| */ | |
| public set(value: T): Snapshot<T> { | |
| const current = this.current + 1; | |
| const history = [ | |
| ...this.history.slice(0, current), | |
| new Snapshot<T>(value, this.initial) | |
| ]; | |
| return new Snapshot<T>(value, this.initial, history, current); | |
| } | |
| /** | |
| * Returns the current value. | |
| * @returns current value. | |
| */ | |
| public get(): T { | |
| return this.value; | |
| } | |
| /** | |
| * Returns all values. | |
| * @returns all values that this Snapshot object ever held. | |
| */ | |
| public historic(): T[] { | |
| return this.history.map( | |
| (value: Snapshot<T>): T => { | |
| return value.get(); | |
| } | |
| ); | |
| } | |
| /** | |
| * Sets current value to previous in history and returns a new Snapshot object. | |
| * @returns new Snapshot object with current as previous value. | |
| */ | |
| public undo(): Snapshot<T> { | |
| const previous = this.current - 1; | |
| return this.setCurrent(previous); | |
| } | |
| /** | |
| * Sets current value to next in history and returns a new Snapshot object. | |
| * @returns new Snapshot object with current as next value. | |
| */ | |
| public redo(): Snapshot<T> { | |
| const next = this.current + 1; | |
| return this.setCurrent(next); | |
| } | |
| /** | |
| * Sets current value to initial in history and returns a new Snapshot object. | |
| * @returns new Snapshot object with initial as current | |
| */ | |
| public reset(): Snapshot<T> { | |
| const next = this.current + 1; | |
| return new Snapshot(this.initial, this.initial, this.history, next); | |
| } | |
| /** | |
| * Returns a new Snapshot object with a cleared history. | |
| * @returns new Snapshot object with cleared history. | |
| */ | |
| public restore(): Snapshot<T> { | |
| return Snapshot.create(this.initial); | |
| } | |
| private setCurrent(to: number): Snapshot<T> { | |
| const existsBefore = to !== -1; | |
| const existsAfter = to !== this.history.length; | |
| const exists = existsBefore && existsAfter; | |
| if (!exists) { | |
| return new Snapshot<T>(this.value, this.initial, this.history); | |
| } | |
| const value = this.history[to].value; | |
| return new Snapshot<T>(value, this.initial, this.history, to); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { Snapshot } from "./Snapshot"; | |
| describe("Snapshot", () => { | |
| let value: Snapshot<number>; | |
| beforeEach(() => { | |
| value = Snapshot.create<number>(0); | |
| }); | |
| describe("get", () => { | |
| it("should return initial value", () => { | |
| const given = value.get(); | |
| const expected = 0; | |
| expect(given).toBe(expected); | |
| }); | |
| }); | |
| describe("set", () => { | |
| it("should set new value", () => { | |
| const given = value.set(1).get(); | |
| const expected = 1; | |
| expect(given).toBe(expected); | |
| }); | |
| it("should set a new new value", () => { | |
| const given = value | |
| .set(2) | |
| .set(3) | |
| .get(); | |
| const expected = 3; | |
| expect(given).toBe(expected); | |
| }); | |
| }); | |
| describe("undo", () => { | |
| it("should set current to previous value", () => { | |
| const given = value | |
| .set(2) | |
| .set(3) | |
| .undo() | |
| .get(); | |
| const expected = 2; | |
| expect(given).toBe(expected); | |
| }); | |
| it("should return initial value", () => { | |
| const given = value.undo().get(); | |
| const expected = 0; | |
| expect(given).toBe(expected); | |
| }); | |
| }); | |
| describe("redo", () => { | |
| it("should return initial value", () => { | |
| const given = value.redo().get(); | |
| const expected = 0; | |
| expect(given).toBe(expected); | |
| }); | |
| it("should redo to next value", () => { | |
| const given = value | |
| .set(2) | |
| .set(3) | |
| .undo() | |
| .redo() | |
| .get(); | |
| const expected = 3; | |
| expect(given).toBe(expected); | |
| }); | |
| it("should return initial value", () => { | |
| const given = value | |
| .set(2) | |
| .set(3) | |
| .undo() | |
| .redo() | |
| .get(); | |
| const expected = 3; | |
| expect(given).toBe(expected); | |
| }); | |
| it("should redo multiple to next value", () => { | |
| const given = value | |
| .set(2) | |
| .set(3) | |
| .undo() | |
| .undo() | |
| .redo() | |
| .redo() | |
| .get(); | |
| const expected = 3; | |
| expect(given).toBe(expected); | |
| }); | |
| }); | |
| describe("reset", () => { | |
| it("should set next value to initial", () => { | |
| const given = value | |
| .set(2) | |
| .set(3) | |
| .set(4) | |
| .reset() | |
| .get(); | |
| const expected = 0; | |
| expect(given).toBe(expected); | |
| }); | |
| }); | |
| describe("historic", () => { | |
| it("should return historic array", () => { | |
| const given = value | |
| .set(2) | |
| .set(3) | |
| .historic(); | |
| const expected = [0, 2, 3]; | |
| expect(given).toEqual(expected); | |
| }); | |
| }); | |
| describe("restore", () => { | |
| it("should restore to initial and erase history", () => { | |
| const given = value | |
| .set(2) | |
| .set(3) | |
| .restore(); | |
| const expected = 0; | |
| expect(given.get()).toEqual(expected); | |
| expect(given.historic().length).toBe(1); | |
| }); | |
| }); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment