mirror of
https://github.com/runebookai/tome.git
synced 2025-07-21 00:27:30 +03:00
Refactors the Model layer to remove the whole “everything must be
static” thing and moves to a normal OO setup.
I had previously misunderstood Svelte’s reactivity and believed you
could only work with plain JS objects (ie. not class instances). Turns
out that was wrong. Svelte is perfectly happy with classes as long as
you define them a specific way.
This big refactor switches to a more sane setup of model classes, with
properties for each column, that are instantiated throughout the app. No
more static functions (where they don’t make sense).
Defining models isn’t vastly different from before, except that you
define the properties in the class itself using Svelte’s `$state`.
```ts
class User extends Base<Row>('users') {
firstName: string = $state('');
lastName: string = $state('');
}
```
We no longer have a separate `Instance` interface describing the model –
the class itself functions as that now. We still have a `Row` interface
describing the raw database row, however.
Properties are defined on the class as `$state`. Svelte’s reactivity
works just fine when you instantiate these classes.
> [!NOTE]
> This means model files must have a `.svelte.ts` extension.
The one caveat with this setup is how you instantiate models.
Javascript’s constructor semantics combined with Svelte’s reactivity
makes it so we can’t dynamically assign properties values in a
constructor. I’m not entirely sure why, to be honest.
Instead, you MUST use the `new` static function.
```ts
const user = User.new({ firstName: 'Matte', lastName: 'Noble' });
```
`fromSql` is still static. However, it now returns an instance of the
model. `toSql` is no longer a static function, but otherwise is the
same.
```ts
static async fromSql(row: Row): Promise<User> {
return User.new({
firstName: row.first_name,
lastName: row.last_name,
});
}
async toSql(): Promise<ToSqlRow<Row>> {
return {
first_name: this.firstName,
last_name: this.lastName,
};
}
```
41 lines
918 B
TypeScript
41 lines
918 B
TypeScript
/**
|
|
* Model class NOT backed by a database
|
|
*/
|
|
export default function BareModel<T extends Obj>() {
|
|
let repo: T[] = $state([]);
|
|
|
|
return class BareModel {
|
|
static reset(instances: T[] = []) {
|
|
repo = instances;
|
|
}
|
|
|
|
static add(instance: T) {
|
|
repo.push(instance);
|
|
}
|
|
|
|
static delete(instance: T) {
|
|
repo = repo.filter(i => i !== instance);
|
|
}
|
|
|
|
static all(): T[] {
|
|
return repo;
|
|
}
|
|
|
|
static find(id: string): T | undefined {
|
|
return repo.findBy('id', id);
|
|
}
|
|
|
|
static findBy(params: Partial<T>): T | undefined {
|
|
return repo.find(r => Object.entries(params).every(([key, value]) => r[key] == value));
|
|
}
|
|
|
|
static first(): T {
|
|
return repo[0];
|
|
}
|
|
|
|
static last(): T {
|
|
return repo[repo.length - 1];
|
|
}
|
|
};
|
|
}
|