Skip to content

Commit

Permalink
Merge pull request #6 from Batterii/nb/definition-hierarchy
Browse files Browse the repository at this point in the history
Implement Definition Hierarchy, individual adapter methods
  • Loading branch information
Noah committed Feb 16, 2021
2 parents b969a5f + 1503cbb commit b51522b
Show file tree
Hide file tree
Showing 20 changed files with 447 additions and 102 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.json
Expand Up @@ -4,9 +4,11 @@
"sort-imports": "off",
"no-await-in-loop": "off",
"class-methods-use-this": "off",
"no-underscore-dangle": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/generic-type-naming": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/naming-convention": "off",
"@typescript-eslint/member-naming": "off"
}
}
10 changes: 6 additions & 4 deletions lib/adapters/adapter.ts
@@ -1,7 +1,9 @@
import {Evaluator} from "../evaluator";

export interface Adapter {
build<T>(Model: any): T;
build<T>(Model: any, evaluator?: Evaluator): T|Promise<T>;
save<T>(instance: T, Model?: any): Promise<T>;
destroy(instance: Record<string, any>, Model?: any): Promise<void>;
relate(instance: any, name: string, other: any, Model?: any): Promise<Record<string, any>>;
set(instance: Record<string, any>, key: string, value: any): Promise<Record<string, any>>;
destroy<T>(instance: T, Model?: any): Promise<void>;
relate<T>(instance: T, name: string, other: any, Model?: any): T|Promise<T>;
set<T>(instance: T, key: string, value: any): T|Promise<T>;
}
10 changes: 5 additions & 5 deletions lib/adapters/default-adapter.ts
@@ -1,23 +1,23 @@
import {Adapter} from "./adapter";

/* eslint-disable class-methods-use-this */
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
export class DefaultAdapter implements Adapter {
build<T>(Model: any): T {
build(Model: any): any {
return new Model();
}

async save<T>(instance: any, _Model?: any): Promise<T> {
async save(instance: any, _Model?: any): Promise<any> {
return instance.save();
}

async destroy(instance: any, _Model?: any): Promise<void> {
await instance.destroy();
}

async relate(instance: any, name: string, other: any, _Model?: any): Promise<any> {
return this.set(instance, name, other);
relate(instance: any, name: string, other: any, _Model?: any): any {
instance[name] = other;
return instance;
}

set(instance: any, key: string, value: any): any {
Expand Down
3 changes: 1 addition & 2 deletions lib/adapters/objection-adapter.ts
@@ -1,9 +1,8 @@
import {DefaultAdapter} from "./default-adapter";

/* eslint-disable class-methods-use-this */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
export class ObjectionAdapter extends DefaultAdapter {
async save<T>(instance: T, Model: any): Promise<T> {
async save(instance: any, Model: any): Promise<any> {
return Model.query().insertAndFetch(instance);
}

Expand Down
1 change: 0 additions & 1 deletion lib/adapters/sequelize-adapter.ts
@@ -1,6 +1,5 @@
import {DefaultAdapter} from "./default-adapter";

/* eslint-disable class-methods-use-this */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
export class SequelizeAdapter extends DefaultAdapter {
build(Model: any): any {
Expand Down
14 changes: 13 additions & 1 deletion lib/assembler.ts
@@ -1,3 +1,4 @@
import {Adapter} from "./adapters/adapter";
import {AttributeAssigner} from "./attribute-assigner";
import {Callback} from "./callback";
import {Pojo} from "./types";
Expand All @@ -6,9 +7,16 @@ export class Assembler<T> {
attributeAssigner: AttributeAssigner<T>;
callbacks: Callback<T>[];

constructor(attributeAssigner: AttributeAssigner<T>, callbacks: Callback<T>[]) {
adapter: Adapter;

constructor(
attributeAssigner: AttributeAssigner<T>,
callbacks: Callback<T>[],
adapter: Adapter,
) {
this.attributeAssigner = attributeAssigner;
this.callbacks = callbacks;
this.adapter = adapter;
}

async toObject(): Promise<Pojo> {
Expand All @@ -19,6 +27,10 @@ export class Assembler<T> {
return this.attributeAssigner.toInstance();
}

async save(instance: any, Model: any): Promise<any> {
return this.adapter.save(instance, Model);
}

async runCallbacks(name: string, instance: T): Promise<void> {
const callbacks = this.callbacks.filter((c) => c.name === name);

Expand Down
15 changes: 10 additions & 5 deletions lib/attribute-assigner.ts
@@ -1,3 +1,4 @@
import {Adapter} from "./adapters/adapter";
import {Attribute} from "./attributes/attribute";
import {Evaluator} from "./evaluator";
import {FixtureRiveter} from "./fixture-riveter";
Expand All @@ -11,17 +12,21 @@ export class AttributeAssigner<T> {
attributes: Attribute[];
attributeNamesToAssign?: string[];

adapter: Adapter;

constructor(
fixtureRiveter: FixtureRiveter,
name: string,
model: ModelConstructor<T>,
evaluator: Evaluator,
adapter: Adapter,
) {
this.fixtureRiveter = fixtureRiveter;
this.name = name;
this.model = model;
this.evaluator = evaluator;
this.attributes = evaluator.attributes;
this.adapter = adapter;
}

getAttributeNamesToAssign(): string[] {
Expand Down Expand Up @@ -52,7 +57,8 @@ export class AttributeAssigner<T> {
}

attributesForInstance(): string[] {
return this.getAttributeNamesToAssign();
const invokedMethods = Array.from(this.evaluator.fetchedAttributes.keys());
return this.getAttributeNamesToAssign().filter((a) => !invokedMethods.includes(a));
}

async toObject(): Promise<Record<string, any>> {
Expand All @@ -65,17 +71,16 @@ export class AttributeAssigner<T> {
}

async toInstance(): Promise<T> {
const adapter = this.fixtureRiveter.getAdapter(this.name);
const instance = adapter.build<T>(this.model);
const instance = await this.adapter.build<T>(this.model, this.evaluator);
const relationNames = this.relationNames();
const attributeNames = this.attributesForInstance();

for (const name of attributeNames) {
const attribute = await this._get(name);
if (relationNames.includes(name)) {
await adapter.relate(instance, name, attribute, this.model);
await this.adapter.relate(instance, name, attribute, this.model);
} else {
adapter.set(instance, name, attribute);
this.adapter.set(instance, name, attribute);
}
}

Expand Down
62 changes: 62 additions & 0 deletions lib/definition-hierarchy.ts
@@ -0,0 +1,62 @@
import {Definition} from "./definition";
import {FixtureRiveter} from "./fixture-riveter";

/* eslint-disable @typescript-eslint/unbound-method */
export class DefinitionHierarchy {
fixtureRiveter: FixtureRiveter;
fixtureName: string;

constructor(fixtureRiveter: FixtureRiveter, fixtureName: string) {
this.fixtureRiveter = fixtureRiveter;
this.fixtureName = fixtureName;
}

static setAdapterMethods(definition: Definition<any>): void {
const toBuild = definition.toBuild();
if (toBuild) {
this.prototype.build = toBuild;
}

const toSave = definition.toSave();
if (toSave) {
this.prototype.save = toSave;
}

const toDestroy = definition.toDestroy();
if (toDestroy) {
this.prototype.destroy = toDestroy;
}

const toRelate = definition.toRelate();
if (toRelate) {
this.prototype.relate = toRelate;
}

const toSet = definition.toSet();
if (toSet) {
this.prototype.set = toSet;
}
}

build(Model: any): any {
return this.fixtureRiveter.getAdapter(this.fixtureName).build(Model);
}

async save(instance: any, Model?: any): Promise<any> {
return this.fixtureRiveter.getAdapter(this.fixtureName).save(instance, Model);
}

async destroy(instance: any, Model?: any): Promise<void> {
return this.fixtureRiveter.getAdapter(this.fixtureName).destroy(instance, Model);
}

async relate(instance: any, name: string, other: any, Model?: any): Promise<any> {
return this.fixtureRiveter
.getAdapter(this.fixtureName)
.relate(instance, name, other, Model);
}

async set(instance: any, key: string, value: any): Promise<any> {
return this.fixtureRiveter.getAdapter(this.fixtureName).set(instance, key, value);
}
}
23 changes: 23 additions & 0 deletions lib/definition-proxy.ts
Expand Up @@ -14,6 +14,7 @@ import {
FixtureOptions,
ModelConstructor,
OverrideForRelation,
CurrentEvaluator,
} from "./types";
import {CallbackFunction} from "./callback";
import {FixtureRiveter, nameGuard} from "./fixture-riveter";
Expand Down Expand Up @@ -161,4 +162,26 @@ export class DefinitionProxy<T> {
after(...rest: any[]): void {
this.definition.after(...rest);
}

toBuild<U = T>(
fn: (Model: ModelConstructor<U>, evaluator: CurrentEvaluator<U>) => T|Promise<T>,
): void {
this.definition._toBuild = fn;
}

toSave(fn: (instance: any, Model?: any) => Promise<any>): void {
this.definition._toSave = fn;
}

toDestroy(fn: (instance: any, Model?: any) => Promise<void>): void {
this.definition._toDestroy = fn;
}

toRelate(fn: (instance: any, name: string, other: any, Model?: any) => Promise<any>): void {
this.definition._toRelate = fn;
}

toSet(fn: (instance: any, key: string, value: any) => Promise<any>): void {
this.definition._toSet = fn;
}
}
63 changes: 49 additions & 14 deletions lib/definition.ts
@@ -1,8 +1,5 @@
import {Attribute} from "./attributes/attribute";
import {
Callback,
CallbackFunction,
} from "./callback";
import {Callback, CallbackFunction} from "./callback";
import {CallbackHandler} from "./callback-handler";
import {Declaration} from "./declarations/declaration";
import {DeclarationHandler} from "./declaration-handler";
Expand All @@ -11,6 +8,8 @@ import {FixtureRiveter} from "./fixture-riveter";
import {SequenceHandler} from "./sequence-handler";
import {BlockFunction} from "./types";

import {last} from "lodash";

export class Definition<T> {
fixtureRiveter: FixtureRiveter;
name: string;
Expand All @@ -26,6 +25,12 @@ export class Definition<T> {
sequenceHandler: SequenceHandler;
declarationHandler: DeclarationHandler;

_toBuild?: any;
_toSave?: any;
_toDestroy?: any;
_toRelate?: any;
_toSet?: any;

constructor(name: string, fixtureRiveter: FixtureRiveter) {
this.name = name;
this.fixtureRiveter = fixtureRiveter;
Expand Down Expand Up @@ -62,6 +67,16 @@ export class Definition<T> {
this.declarationHandler.declareAttribute(declaration);
}

getAttributes(): Attribute[] {
this.compile();

return this.aggregateFromTraitsAndSelf(
"getAttributes",
() => this.declarationHandler.getAttributes(),
);
}


defineTrait(newTrait: Trait<T>): void {
if (!this.traitsCache.has(newTrait.name)) {
this.traitsCache.set(newTrait.name, newTrait);
Expand All @@ -77,8 +92,6 @@ export class Definition<T> {
}

traitByName(name: string): Trait<T> {
// This is overridden by both Fixture and Trait, so ignore it please
// I've only written this out so Typescript will shut up lol
return this.traitsCache.get(name) || this.fixtureRiveter.getTrait(name);
}

Expand All @@ -103,19 +116,41 @@ export class Definition<T> {
}

getCallbacks(): Callback<T>[] {
return this.aggregateFromTraitsAndSelf(
"getCallbacks",
() => this.callbackHandler.callbacks,
);
}

aggregateFromTraitsAndSelf(traitMethod: string, block: () => any|any[]): any[] {
this.compile();

return [
this.getBaseTraits().map((t) => t.getCallbacks()),
this.callbackHandler.callbacks,
this.getAdditionalTraits().map((t) => t.getCallbacks()),
this.getBaseTraits().map((t) => t[traitMethod]()),
block(),
this.getAdditionalTraits().map((t) => t[traitMethod]()),
].flat(2).filter(Boolean);
}

copy<C extends Definition<T>>(): C {
const copy: C = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
copy.compiled = false;
copy.attributes = [];
return copy;
toBuild(): ((Model: any) => any) | undefined {
return last(this.aggregateFromTraitsAndSelf("toBuild", () => this._toBuild));
}

toSave(): ((instance: any, Model?: any) => Promise<any>) | undefined {
return last(this.aggregateFromTraitsAndSelf("toSave", () => this._toSave));
}

toDestroy(): ((instance: any, Model?: any) => Promise<any>) | undefined {
return last(this.aggregateFromTraitsAndSelf("toDestroy", () => this._toDestroy));
}

toRelate(): (
(instance: any, name: string, other: any, Model?: any) => Promise<any> | any
) | undefined {
return last(this.aggregateFromTraitsAndSelf("toRelate", () => this._toRelate));
}

toSet(): ((instance: any, key: string, value: any) => Promise<any> | any) | undefined {
return last(this.aggregateFromTraitsAndSelf("toSet", () => this._toSet));
}
}
4 changes: 4 additions & 0 deletions lib/evaluator.ts
Expand Up @@ -15,6 +15,8 @@ export class Evaluator {
fixtureRiveter: FixtureRiveter;
overrides: Record<string, any>;

fetchedAttributes: Set<string>;

constructor(
fixtureRiveter: FixtureRiveter,
buildStrategy: Strategy,
Expand All @@ -30,6 +32,7 @@ export class Evaluator {
}
this.overrides = overrides;
this.attributeFns = new Map();
this.fetchedAttributes = new Set();

this.defineAttributes(attributes);
}
Expand All @@ -54,6 +57,7 @@ export class Evaluator {
this.cachedValues.set(name, await fn(this));
}
}
this.fetchedAttributes.add(name);
return this.cachedValues.get(name);
}

Expand Down

0 comments on commit b51522b

Please sign in to comment.