Instance to wrappedrow
This commit is contained in:
parent
47d0ec687b
commit
13c3d86a10
6 changed files with 59 additions and 73 deletions
|
@ -1,14 +1,13 @@
|
||||||
import type { Column, ColumnShorthand, DataType } from "./columns";
|
import type { Column, ColumnShorthand, DataType } from "./columns";
|
||||||
import { type ExtractRowShape, Instance } from "./instance";
|
import { WrappedRow } from "./wrapped-row";
|
||||||
import { Prequel } from "./prequel";
|
import { Prequel } from "./prequel";
|
||||||
import { Table } from "./table";
|
import { Table } from "./table";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Prequel,
|
Prequel,
|
||||||
Table,
|
Table,
|
||||||
Instance,
|
WrappedRow,
|
||||||
type DataType,
|
type DataType,
|
||||||
type Column,
|
type Column,
|
||||||
type ColumnShorthand,
|
type ColumnShorthand,
|
||||||
type ExtractRowShape,
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
import type { Table } from "./table";
|
|
||||||
|
|
||||||
export type ExtractRowShape<T> = T extends Table<infer R> ? R : never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents an instance of a row in a table of a database.
|
|
||||||
* Wraps the raw Row data and provides methods for interacting with it.
|
|
||||||
*/
|
|
||||||
export abstract class Instance<
|
|
||||||
TableType extends Table<unknown>,
|
|
||||||
SerializedInstanceType = void,
|
|
||||||
> {
|
|
||||||
protected row: ExtractRowShape<TableType>;
|
|
||||||
protected table: TableType;
|
|
||||||
|
|
||||||
constructor(table: TableType, row: ExtractRowShape<TableType>) {
|
|
||||||
this.row = row;
|
|
||||||
this.table = table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves any changes made to the row back to the database.
|
|
||||||
*/
|
|
||||||
save() {
|
|
||||||
this.table.update(this.row);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Serializes the instance into a generic format
|
|
||||||
*/
|
|
||||||
serialize(): SerializedInstanceType {
|
|
||||||
throw new Error("Not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns The row data as a JSON string.
|
|
||||||
*/
|
|
||||||
toJSON(): string {
|
|
||||||
return JSON.stringify(this.row);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exports the raw row data.
|
|
||||||
* @returns The raw row data.
|
|
||||||
*/
|
|
||||||
export(): ExtractRowShape<TableType> {
|
|
||||||
return this.row;
|
|
||||||
}
|
|
||||||
}
|
|
28
src/table.ts
28
src/table.ts
|
@ -7,12 +7,15 @@ import {
|
||||||
} from "./columns";
|
} from "./columns";
|
||||||
import { SchemaError } from "./error";
|
import { SchemaError } from "./error";
|
||||||
import { Prequel } from "./prequel";
|
import { Prequel } from "./prequel";
|
||||||
|
import type { WrappedRow } from "./wrapped-row";
|
||||||
|
|
||||||
|
export type ExtractRowShape<T> = T extends Table<infer R> ? R : never;
|
||||||
|
|
||||||
/** Types that may appear in a row */
|
/** Types that may appear in a row */
|
||||||
type Value = string | number | null;
|
type ColumnValue = string | number | null;
|
||||||
|
|
||||||
export class PrequelIDNotFoundError extends Error {
|
export class PrequelIDNotFoundError extends Error {
|
||||||
constructor(tableName: string, id: Value) {
|
constructor(tableName: string, id: ColumnValue) {
|
||||||
super(`ID ${id} not found in ${tableName}`);
|
super(`ID ${id} not found in ${tableName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +28,11 @@ export class PrequelEmptyResultsError extends Error {
|
||||||
|
|
||||||
/** An arbitrarily shaped Row */
|
/** An arbitrarily shaped Row */
|
||||||
interface ArbitraryRow {
|
interface ArbitraryRow {
|
||||||
[key: string]: Value;
|
[key: string]: ColumnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shapeForQuery<T>(obj: Partial<T>): Record<string, Value> {
|
function shapeForQuery<T>(obj: Partial<T>): Record<string, ColumnValue> {
|
||||||
const asInsertDataShape: Record<string, Value> = {};
|
const asInsertDataShape: Record<string, ColumnValue> = {};
|
||||||
|
|
||||||
const keys = Object.keys(obj);
|
const keys = Object.keys(obj);
|
||||||
|
|
||||||
|
@ -43,7 +46,7 @@ function shapeForQuery<T>(obj: Partial<T>): Record<string, Value> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const asQueryKey = `$${key}`;
|
const asQueryKey = `$${key}`;
|
||||||
asInsertDataShape[asQueryKey] = val as Value;
|
asInsertDataShape[asQueryKey] = val as ColumnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return asInsertDataShape;
|
return asInsertDataShape;
|
||||||
|
@ -282,7 +285,7 @@ export class Table<RowShape> {
|
||||||
* @param id - The ID of the entry to check for existence.
|
* @param id - The ID of the entry to check for existence.
|
||||||
* @returns `true` if an entry with the specified ID exists, otherwise `false`.
|
* @returns `true` if an entry with the specified ID exists, otherwise `false`.
|
||||||
*/
|
*/
|
||||||
public exists(id: Value): boolean {
|
public exists(id: ColumnValue): boolean {
|
||||||
return this.findOneById(id) !== null;
|
return this.findOneById(id) !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +324,7 @@ export class Table<RowShape> {
|
||||||
* @param id - The unique identifier of the row to find.
|
* @param id - The unique identifier of the row to find.
|
||||||
* @returns The row shape if found, otherwise `null`.
|
* @returns The row shape if found, otherwise `null`.
|
||||||
*/
|
*/
|
||||||
public findOneById(id: Value): RowShape | null {
|
public findOneById(id: ColumnValue): RowShape | null {
|
||||||
return this._findQuery.get(`${id}`);
|
return this._findQuery.get(`${id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +336,7 @@ export class Table<RowShape> {
|
||||||
*
|
*
|
||||||
* @throws {Error} If no row is found with the specified ID.
|
* @throws {Error} If no row is found with the specified ID.
|
||||||
*/
|
*/
|
||||||
public findOneByIdOrFail(id: Value): RowShape {
|
public findOneByIdOrFail(id: ColumnValue): RowShape {
|
||||||
const found = this.findOneById(id);
|
const found = this.findOneById(id);
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
|
@ -429,7 +432,7 @@ export class Table<RowShape> {
|
||||||
* @param id - The unique identifier of the record to be deleted.
|
* @param id - The unique identifier of the record to be deleted.
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
public deleteById = (id: Value): void => {
|
public deleteById = (id: ColumnValue): void => {
|
||||||
if (this._primaryColumnName === "ROWID") {
|
if (this._primaryColumnName === "ROWID") {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"Cannot use `Table.deleteById()` without setting a primary column",
|
"Cannot use `Table.deleteById()` without setting a primary column",
|
||||||
|
@ -489,6 +492,11 @@ export class Table<RowShape> {
|
||||||
return res.count;
|
return res.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public save(wrapped: WrappedRow<RowShape>) {
|
||||||
|
const row = wrapped.unwrap();
|
||||||
|
this.update(row);
|
||||||
|
}
|
||||||
|
|
||||||
public toString() {
|
public toString() {
|
||||||
return `"${this._name}"`;
|
return `"${this._name}"`;
|
||||||
}
|
}
|
||||||
|
|
26
src/wrapped-row.ts
Normal file
26
src/wrapped-row.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* Represents an instance of a row in a table of a database.
|
||||||
|
* Wraps the raw Row data and provides methods for interacting with it.
|
||||||
|
*/
|
||||||
|
export class WrappedRow<RowShape> {
|
||||||
|
protected row: RowShape;
|
||||||
|
|
||||||
|
constructor(row: RowShape) {
|
||||||
|
this.row = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns The row data as a JSON string.
|
||||||
|
*/
|
||||||
|
toJSON(): string {
|
||||||
|
return JSON.stringify(this.row);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the raw row data.
|
||||||
|
* @returns The raw row data.
|
||||||
|
*/
|
||||||
|
unwrap(): RowShape {
|
||||||
|
return this.row;
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,14 @@ interface UserWithID {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UserWithBio {
|
||||||
|
user_id: number;
|
||||||
|
bio: {
|
||||||
|
name: string;
|
||||||
|
age: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface Post {
|
interface Post {
|
||||||
post_id: number;
|
post_id: number;
|
||||||
user_id: number;
|
user_id: number;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Database } from "bun:sqlite";
|
import { Database } from "bun:sqlite";
|
||||||
import { expect, test } from "bun:test";
|
import { expect, test } from "bun:test";
|
||||||
import { Instance, Prequel, Table } from "../src/index";
|
import { WrappedRow, Table } from "../src/index";
|
||||||
|
|
||||||
interface User {
|
interface User {
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -20,7 +20,7 @@ const table = new Table<User>(db, "Users", {
|
||||||
name: "TEXT",
|
name: "TEXT",
|
||||||
});
|
});
|
||||||
|
|
||||||
class UserInstance extends Instance<typeof table, SerializedUser> {
|
class UserWrappedRow extends WrappedRow<User> {
|
||||||
get name(): string {
|
get name(): string {
|
||||||
return this.row.name;
|
return this.row.name;
|
||||||
}
|
}
|
||||||
|
@ -28,25 +28,19 @@ class UserInstance extends Instance<typeof table, SerializedUser> {
|
||||||
set name(val: string) {
|
set name(val: string) {
|
||||||
this.row.name = val;
|
this.row.name = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize(): SerializedUser {
|
|
||||||
return {
|
|
||||||
name: this.row.name,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("setting values on an instance", () => {
|
test("setting values on an WrappedRow", () => {
|
||||||
table.insert({
|
table.insert({
|
||||||
id: 1,
|
id: 1,
|
||||||
name: "Alice",
|
name: "Alice",
|
||||||
});
|
});
|
||||||
|
|
||||||
const alice = table.findOneByIdOrFail(1);
|
const alice = table.findOneByIdOrFail(1);
|
||||||
const inst = new UserInstance(table, alice);
|
const inst = new UserWrappedRow(alice);
|
||||||
|
|
||||||
inst.name = "Bob";
|
inst.name = "Bob";
|
||||||
inst.save();
|
table.save(inst);
|
||||||
|
|
||||||
const bob = table.findOneByIdOrFail(1);
|
const bob = table.findOneByIdOrFail(1);
|
||||||
expect(bob.name).toEqual("Bob");
|
expect(bob.name).toEqual("Bob");
|
Loading…
Add table
Add a link
Reference in a new issue