diff --git a/README.md b/README.md index 129b9c9..f1be4bc 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ programming paradigms, it's purely experimental at this point and not suitable f | Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | -| ![Statements](https://img.shields.io/badge/statements-98.62%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-96.92%25-brightgreen.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-87.5%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-98.62%25-brightgreen.svg?style=flat) | +| ![Statements](https://img.shields.io/badge/statements-98.54%25-brightgreen.svg?style=flat) | ![Branches](https://img.shields.io/badge/branches-95.52%25-brightgreen.svg?style=flat) | ![Functions](https://img.shields.io/badge/functions-85.29%25-yellow.svg?style=flat) | ![Lines](https://img.shields.io/badge/lines-98.54%25-brightgreen.svg?style=flat) | ## Motivation diff --git a/src/common/index.ts b/src/common/index.ts index 9e8881d..9ee10fe 100644 --- a/src/common/index.ts +++ b/src/common/index.ts @@ -1,3 +1,4 @@ export * from "./range" export * from "./error" +export * from "./optics" export * from "./string" diff --git a/src/common/optics.ts b/src/common/optics.ts new file mode 100644 index 0000000..9427eb7 --- /dev/null +++ b/src/common/optics.ts @@ -0,0 +1,26 @@ +import {Optional} from "@fp-ts/optic" + +/** + * Represents an abstract class for focusable objects. + * + * This class provides an {@link Optional} that allows accessing and modifying a property or element in a + * given subject. + * + * @template TSubject The type of the subject on which the focus will be applied. + * @template TFocus The type of the property or element that will be focused. + */ +export abstract class Focusable { + + /** + * Creates a new instance of the focusable object. + */ + protected constructor( + /** + * Represents an {@link Optional} that focuses on TFocus in a given TSubject. + * + * @readonly + */ + protected readonly optic: Optional + ) { + } +} diff --git a/src/game/actor/actor.ts b/src/game/actor/actor.ts index 14a5fbe..8d2780b 100644 --- a/src/game/actor/actor.ts +++ b/src/game/actor/actor.ts @@ -3,13 +3,13 @@ * @module */ import * as Optic from "@fp-ts/optic" -import {Optional} from "@fp-ts/optic" import {Either} from "fp-ts/Either" import {Option} from "fp-ts/Option" import {ReadonlyRecord} from "fp-ts/ReadonlyRecord" import * as T from "io-ts" import {Mixed} from "io-ts" import {MaxLengthString, MinLengthString} from "../../common" +import {Focusable} from "../../common/optics" import {NameAttribute, Named, NamedData, NamedDataT} from "../attribute" import {UnknownActorError} from "./errors" @@ -106,24 +106,17 @@ export interface Actor< export abstract class AbstractActor< TData extends ActorData = unknown & ActorData, TContext extends ActorDataHolder = unknown & ActorDataHolder -> implements Actor { +> extends Focusable implements Actor { readonly name: NameAttribute - /** - * Represents an {@link Optional} that focuses on TData in a given TContext. - * - * @readonly - */ - protected readonly optic: Optional - /** * Constructor for creating an instance of the class. * * @param {ActorId} id - The identifier of the actor. */ protected constructor(readonly id: ActorId) { - this.optic = Optic.id().at("actors").key(id) + super(Optic.id().at("actors").key(id)) this.name = new NameAttribute(this.optic, ActorNameT) } diff --git a/src/game/attribute/attribute.ts b/src/game/attribute/attribute.ts index 7319b0c..877d72f 100644 --- a/src/game/attribute/attribute.ts +++ b/src/game/attribute/attribute.ts @@ -9,6 +9,7 @@ import {Either} from "fp-ts/Either" import {flow, pipe} from "fp-ts/function" import {Decoder} from "io-ts" import {PathReporter} from "io-ts/PathReporter" +import {Focusable} from "../../common/optics" import {AttributeAccessError, InvalidAttributeError, ReadOnlyAttributeError} from "./errors" /** @@ -116,14 +117,7 @@ export abstract class AbstractAttribute< TName extends string & keyof TData, TData = unknown, TContext = unknown -> implements Attribute { - - /** - * Represents an {@link Optional} that focuses on TData[TName] in a given TContext. - * - * @readonly - */ - protected readonly optic: Optional +> extends Focusable implements Attribute { /** * Represents a decoder for converting unknown input into the corresponding typed data value. @@ -147,7 +141,8 @@ export abstract class AbstractAttribute< optic: Optional, options?: AttributeOptions ) { - this.optic = optic.compose(Optic.id().at(name)) + super(optic.compose(Optic.id().at(name))) + this.updatable = options?.updatable ?? true // These methods are bound here to the instance to ensure that they maintain their "this" context