(v0.7.4) Simplified and documented API

It's just enable, disable, and fireEvent now.
This commit is contained in:
Josh Goldberg
2018-01-16 02:41:29 -08:00
parent 2b94a73ebf
commit 2ecbcf53da
8 changed files with 399 additions and 262 deletions
+272
View File
@@ -12,6 +12,278 @@ Hookups for extensible triggered mod events.
After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo/): After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo/):
## Usage
### Constructor
```typescript
import { ModAttachr } from "modattachr";
const modAttacher = new ModAttachr({
mods: [
{
events: {
onModDisable() {
console.log("Disabled...");
},
onModEnable() {
console.log("Enabled!");
},
},
name: "Sample",
},
],
});
```
#### `eventNames`
Event names for mods.
This object needs to contain `string`s under `onModDisable` and `onModEnable`.
It defaults to a `new` instance of the exported `EventNames` class.
These will be used to look up the respective keys under mods' `events`.
You can override this behavior if you must.
```typescript
const modAttacher = new ModAttachr({
eventNames: {
onModDisable: "onDisable",
onModEnable: "onEnable",
},
mods: [
{
events: {
onDisable() {
console.log("Disabled...");
},
onEnable() {
console.log("Enabled!");
},
},
name: "Sample",
},
],
});
```
#### `mods`
Mods that may be enabled or disabled.
These must satisfy the `IMod` interface, which contains:
* `enabled: boolean` _(optional)_: Whether the mod is immediately enabled (by default, false).
* `events: Object`: Event callbacks, keyed by event name.
* `name: string`: User-readable name of the mod.
```typescript
const modAttacher = new ModAttachr({
mods: [
{
events: {
onModEnable() {
console.log("Enabled!");
},
},
name: "Sample",
},
],
});
```
Any mods with `enabled` set to `true` will be immediately enabled.
```typescript
// Enabled!
const modAttacher = new ModAttachr({
mods: [
{
enabled: true,
events: {
onModEnable() {
console.log("Enabled!");
},
},
name: "Sample",
},
],
});
```
#### `storage`
An optional `ItemsHoldr` to store whether mods are enabled.
This will store mod status across user sessions.
By default, mod statuses are directly stored under the mods' names.
You can override this behavior with `transformModName`.
```typescript
import { ItemsHoldr } from "itemsholdr";
import { modAttacher } from "modattachr";
const itemsHolder = new ItemsHoldr();
itemsHolder.setItem("Sample", true);
// Enabled!
const modAttacher = new ModAttachr({
mods: [
{
events: {
onModEnable() {
console.log("Enabled!");
},
},
name: "Sample",
},
],
storage: itemsHolder,
});
```
> Values from `storage` will override a mods' own `enabled` values.
#### `transformModName`
Transforms mod names to `storage` keys.
Used when `storage` is provided to get or set whether mods are enabled.
```typescript
const itemsHolder = new ItemsHoldr();
itemsHolder.setItem("Mods::Sample", true);
// Enabled!
const modAttacher = new ModAttachr({
mods: [
{
events: {
onModEnable() {
console.log("Enabled!");
},
},
name: "Sample",
},
],
storage: itemsHolder,
transformModName: (name) => `Mod::${name}`,
});
```
---
### `enableMod`
Parameters:
* `name: string`: Name of a mod to enable.
Enables a mod and calls its `onModEnable` event, if it exists.
Enabling a mod means that whenever an event the mod has a callback for is fired, the mod's callback will run.
```typescript
const modAttacher = new ModAttachr({
mods: [
{
events: {
onModEnable() {
console.log("Enabled!");
},
onTest() {
console.log("Testing.");
}
},
name: "Sample",
},
],
});
// Enabled!
modAttacher.enableMod("Sample");
// Testing.
modAttacher.fireEvent("onTest");
```
> If the mod wasn't already enabled, this does nothing.
### `disableMod`
Parameters:
* `name: string`: Name of a mod to disable.
Enables a mod and calls its `onModDisable` event, if it exists.
Disable a mod means that whenever an event the mod has a callback for is fired, the mod's callback will not run.
```typescript
// Enabled!
const modAttacher = new ModAttachr({
mods: [
{
enabled: true,
events: {
onModDisable() {
console.log("Disabling...");
},
onModEnable() {
console.log("Enabled!");
},
onTest() {
console.log("Testing.");
}
},
name: "Sample",
},
],
});
// Disabling...
modAttacher.disableMod("Sample");
modAttacher.fireEvent("onTest");
```
> If the mod wasn't already disabled, this does nothing.
### `fireEvent`
Parameters:
* `eventName: string`: Name of an event to fire.
* `args: ...any[]`: Any additional arguments to pass to event callbacks.
Fires an event, which calls all callbacks of mods listed for that event.
```typescript
const modAttacher = new ModAttachr({
mods: [
{
enabled: true,
events: {
onTest() {
console.log("Testing.");
}
},
name: "Sample",
},
],
});
// Testing.
modAttacher.fireEvent("onTest");
```
<!-- {{Development}} -->
## Development
After [forking the repo from GitHub](https://help.github.com/articles/fork-a-repo/):
``` ```
git clone https://github.com/<your-name-here>/ModAttachr git clone https://github.com/<your-name-here>/ModAttachr
cd ModAttachr cd ModAttachr
+1 -1
View File
@@ -69,5 +69,5 @@
"name": "ModAttachr" "name": "ModAttachr"
}, },
"types": "./src/index.d.ts", "types": "./src/index.d.ts",
"version": "0.7.3" "version": "0.7.4"
} }
+17 -2
View File
@@ -1,7 +1,22 @@
/** /**
* Holds keys for mod events. * Event names for mods.
*/ */
export class EventNames { export interface IEventNames {
/*
* Key for event when a mod is enabled.
*/
onModEnable: string;
/*
* Key for event when a mod is disabled.
*/
onModDisable: string;
}
/**
* Event names for mods.
*/
export class EventNames implements IEventNames {
/* /*
* Key for event when a mod is enabled. * Key for event when a mod is enabled.
*/ */
+19 -64
View File
@@ -1,25 +1,25 @@
import { IItemsHoldr } from "itemsholdr"; import { IItemsHoldr } from "itemsholdr";
import { EventNames } from "./EventNames"; import { IEventNames } from "./EventNames";
/** /**
* General schema for a mod, including its name and events. * General schema for a mod, including its name and events.
*/ */
export interface IMod { export interface IMod {
/** /**
* The user-readable name of the mod. * Whether the mod is immediately enabled (by default, false).
*/ */
name: string; enabled?: boolean;
/** /**
* The mapping of events to callback Functions to be evaluated. * Event callbacks, keyed by event name.
*/ */
events: ICallbackRegister; events: ICallbackRegister;
/** /**
* Whether the mod is currently enabled (by default, false). * User-readable name of the mod.
*/ */
enabled?: boolean; name: string;
} }
/** /**
@@ -74,7 +74,12 @@ export type ITransformModName = (name: string) => string;
*/ */
export interface IModAttachrSettings { export interface IModAttachrSettings {
/** /**
* Mods to be immediately added via addMod. * Event names for mods.
*/
eventNames?: IEventNames;
/**
* Mods that may be enabled or disabled.
*/ */
mods?: IMod[]; mods?: IMod[];
@@ -83,85 +88,35 @@ export interface IModAttachrSettings {
*/ */
itemsHolder?: IItemsHoldr; itemsHolder?: IItemsHoldr;
/**
* Whether there should be a ItemsHoldr created if one isn't given.
*/
storeLocally?: boolean;
/** /**
* Transforms mod names to storage keys. * Transforms mod names to storage keys.
*/ */
transformModName?: ITransformModName; transformModName?: ITransformModName;
/**
* Holds keys for mod events.
*/
eventNames?: EventNames;
} }
/** /**
* Hookups for extensible triggered mod events. * Hookups for extensible triggered mod events.
*/ */
export interface IModAttachr { export interface IModAttachr {
/**
* Holds keys for mod events.
*/
readonly eventNames: EventNames;
/**
* All known mods, keyed by name.
*/
readonly mods: IMods;
/**
* Adds a mod to the pool of mods.
*
* @param mod General schema for a mod, including its name and events.
*/
addMod(mod: IMod): any;
/** /**
* Enables a mod. * Enables a mod.
* *
* @param name The name of the mod to enable. * @param modName Name of a mod to enable.
* @param args Any additional arguments to pass to event callbacks.
* @returns The result of the mod's onModEnable event, if it exists.
*/ */
enableMod(name: string, ...args: any[]): any; enableMod(modName: string): void;
/** /**
* Disables a mod. * Disables a mod.
* *
* @param name The name of the mod to disable. * @param modName Name of a mod to disable.
* @param args Any additional arguments to pass to event callbacks.
* @returns The result of the mod's onModDisable event, if it exists.
*/ */
disableMod(name: string, ...args: any[]): any; disableMod(modName: string): void;
/** /**
* Toggles a mod via enableMod/disableMod. * Fires an event, which calls all callbacks of mods listed for that event.
* *
* @param name The name of the mod to toggle. * @param eventName Name of an event to fire.
* @param args Any additional arguments to pass to event callbacks.
* @returns The result of the mod's onModEnable or onModDisable event.
*/
toggleMod(name: string, ...args: any[]): any;
/**
* Fires an event, which calls all mods listed for that event.
*
* @param name Name of the event to fire.
* @param args Any additional arguments to pass to event callbacks. * @param args Any additional arguments to pass to event callbacks.
*/ */
fireEvent(name: string, ...args: any[]): void; fireEvent(eventName: string, ...args: any[]): void;
/**
* Fires an event for one mod.
*
* @param eventName Name of the event to fire.
* @param modName Name of the mod to fire the event.
* @param args Any additional arguments to pass to event callbacks.
* @returns The result of the fired mod event.
*/
fireModEvent(eventName: string, modName: string, ...args: any[]): any;
} }
-72
View File
@@ -1,72 +0,0 @@
import { expect } from "chai";
import { EventNames } from "./EventNames";
import { FakeEventNames } from "./fakes.test";
import { ModAttachr } from "./ModAttachr";
it("onModEnable is fired when a mod is enabled", (): void => {
// Arrange
const eventNames = new EventNames();
const mod = {
enabled: false,
events: {
[eventNames.onModEnable]: (): string => "success",
},
name: "Dummy Mod",
};
const modAttachr = new ModAttachr({
eventNames,
mods: [mod],
});
// Act
const eventResult: string = modAttachr.fireModEvent(eventNames.onModEnable, mod.name);
// Assert
expect(eventResult).to.be.equal("success");
});
it("onModDisable is fired when a mod is disabled", (): void => {
// Arrange
const eventNames = new EventNames();
const mod = {
enabled: false,
events: {
[eventNames.onModDisable]: (): string => "success",
},
name: "Dummy Mod",
};
const modAttachr = new ModAttachr({
eventNames,
mods: [mod],
});
// Act
const eventResult: string = modAttachr.fireModEvent(eventNames.onModDisable, mod.name);
// Assert
expect(eventResult).to.be.equal("success");
});
it("an arbitrary event is fired", (): void => {
// Arrange
const value = 42;
const eventNames = new FakeEventNames();
const mod = {
enabled: false,
events: {
[eventNames.fakeEvent]: (): number => value,
},
name: "Dummy Fake Mod",
};
const modAttachr = new ModAttachr({
eventNames,
mods: [mod],
});
// Act
const eventResult: number = modAttachr.fireModEvent(eventNames.fakeEvent, mod.name);
// Assert
expect(eventResult).to.be.equal(value);
});
+89 -100
View File
@@ -1,6 +1,6 @@
import { IItemsHoldr, ItemsHoldr } from "itemsholdr"; import { IItemsHoldr } from "itemsholdr";
import { EventNames } from "./EventNames"; import { EventNames, IEventNames } from "./EventNames";
import { import {
ICallbackRegister, IEventCallback, IEventsRegister, IMod, ICallbackRegister, IEventCallback, IEventsRegister, IMod,
@@ -30,12 +30,12 @@ export class ModAttachr implements IModAttachr {
/** /**
* Holds keys for mod events. * Holds keys for mod events.
*/ */
public readonly eventNames: EventNames; private readonly eventNames: IEventNames;
/** /**
* All known mods, keyed by name. * All known mods, keyed by name.
*/ */
public readonly mods: IMods = {}; private readonly mods: IMods = {};
/** /**
* For each event, the listing of mods that attach to that event. * For each event, the listing of mods that attach to that event.
@@ -58,18 +58,86 @@ export class ModAttachr implements IModAttachr {
* @param settings Settings to be used for initialization. * @param settings Settings to be used for initialization.
*/ */
public constructor(settings: IModAttachrSettings = {}) { public constructor(settings: IModAttachrSettings = {}) {
this.eventNames = settings.eventNames || new EventNames(); this.eventNames = settings.eventNames === undefined
this.transformModName = settings.transformModName || ((name: string): string => name); ? new EventNames()
: settings.eventNames;
this.transformModName = settings.transformModName === undefined
? ((name: string): string => name)
: settings.transformModName;
if (settings.itemsHolder) { if (settings.itemsHolder !== undefined) {
this.itemsHolder = settings.itemsHolder; this.itemsHolder = settings.itemsHolder;
} else if (settings.storeLocally) {
this.itemsHolder = new ItemsHoldr();
} }
if (settings.mods) { if (settings.mods !== undefined) {
for (const mod of settings.mods) { for (const mod of settings.mods) {
this.addMod(mod); if ({}.hasOwnProperty.call(settings.mods, mod)) {
this.addMod(mod);
}
}
}
}
/**
* Enables a mod.
*
* @param modName Name of a mod to enable.
*/
public enableMod(modName: string): void {
const mod: IMod = this.retrieveMod(modName);
if (mod.enabled === true) {
return;
}
mod.enabled = true;
if (this.itemsHolder) {
this.itemsHolder.setItem(this.transformModName(modName), true);
}
if (mod.events[this.eventNames.onModEnable] !== undefined) {
this.fireModEvent(this.eventNames.onModEnable, mod.name);
}
}
/**
* Disables a mod.
*
* @param modName Name of a mod to disable.
*/
public disableMod(modName: string): void {
const mod: IMod = this.retrieveMod(modName);
if (mod.enabled !== true) {
return;
}
mod.enabled = false;
if (this.itemsHolder) {
this.itemsHolder.setItem(this.transformModName(modName), false);
}
if (mod.events[this.eventNames.onModDisable] !== undefined) {
this.fireModEvent(this.eventNames.onModDisable, mod.name);
}
}
/**
* Fires an event, which calls all callbacks of mods listed for that event.
*
* @param eventName Name of the event to fire.
* @param args Any additional arguments to pass to event callbacks.
*/
public fireEvent(eventName: string, ...args: any[]): void {
if (!{}.hasOwnProperty.call(this.events, eventName)) {
return;
}
const mods: IMod[] = this.events[eventName];
for (const mod of mods) {
if (mod.enabled === true) {
retrieveModEvent(mod, eventName)(...args);
} }
} }
} }
@@ -79,7 +147,7 @@ export class ModAttachr implements IModAttachr {
* *
* @param mod General schema for a mod, including its name and events. * @param mod General schema for a mod, including its name and events.
*/ */
public addMod(mod: IMod): void { private addMod(mod: IMod): void {
const modEvents: ICallbackRegister = mod.events; const modEvents: ICallbackRegister = mod.events;
for (const name in modEvents) { for (const name in modEvents) {
@@ -96,94 +164,17 @@ export class ModAttachr implements IModAttachr {
this.mods[mod.name] = mod; this.mods[mod.name] = mod;
if (this.itemsHolder) { if (this.itemsHolder !== undefined) {
const storedKey: string = this.transformModName(mod.name); const storedKey: string = this.transformModName(mod.name);
this.itemsHolder.addItem(storedKey, { this.itemsHolder.addItem(storedKey, {
valueDefault: false, valueDefault: Boolean(mod.enabled),
}); });
if (this.itemsHolder.getItem(storedKey)) { if (this.itemsHolder.getItem(storedKey)) {
this.enableMod(mod.name); this.enableMod(mod.name);
} }
} } else if (mod.enabled === true) {
} this.enableMod(mod.name);
/**
* Enables a mod.
*
* @param name The name of the mod to enable.
* @param args Any additional arguments to pass to event callbacks.
* @returns The result of the mod's onModEnable event, if it exists.
*/
public enableMod(name: string, ...args: any[]): any {
const mod: IMod = this.mods[name];
if (!mod) {
throw new Error(`No mod of name '${name}'.`);
}
mod.enabled = true;
if (this.itemsHolder) {
this.itemsHolder.setItem(this.transformModName(name), true);
}
if (mod.events[this.eventNames.onModEnable]) {
return this.fireModEvent(this.eventNames.onModEnable, mod.name, ...args);
}
}
/**
* Disables a mod.
*
* @param name The name of the mod to disable.
* @param args Any additional arguments to pass to event callbacks.
* @returns The result of the mod's onModDisable event, if it exists.
*/
public disableMod(name: string, ...args: any[]): any {
const mod: IMod = this.retrieveMod(name);
this.mods[name].enabled = false;
if (this.itemsHolder) {
this.itemsHolder.setItem(this.transformModName(name), false);
}
if (mod.events[this.eventNames.onModDisable]) {
return this.fireModEvent(this.eventNames.onModDisable, mod.name, ...args);
}
}
/**
* Toggles a mod via enableMod/disableMod.
*
* @param name The name of the mod to toggle.
* @param args Any additional arguments to pass to event callbacks.
* @returns The result of the mod's onModEnable or onModDisable event.
*/
public toggleMod(name: string, ...args: any[]): any {
const mod: IMod = this.retrieveMod(name);
return mod.enabled
? this.disableMod(name, ...args)
: this.enableMod(name, ...args);
}
/**
* Fires an event, which calls all mods listed for that event.
*
* @param name Name of the event to fire.
* @param args Any additional arguments to pass to event callbacks.
*/
public fireEvent(name: string, ...args: any[]): void {
const mods: IMod[] = this.events[name];
if (!mods) {
return;
}
for (const mod of mods) {
if (mod.enabled) {
retrieveModEvent(mod, name)(...args);
}
} }
} }
@@ -195,11 +186,11 @@ export class ModAttachr implements IModAttachr {
* @param args Any additional arguments to pass to event callbacks. * @param args Any additional arguments to pass to event callbacks.
* @returns The result of the fired mod event. * @returns The result of the fired mod event.
*/ */
public fireModEvent(eventName: string, modName: string, ...args: any[]): any { private fireModEvent(eventName: string, modName: string, ...args: any[]): void {
const mod: IMod = this.retrieveMod(modName); const mod: IMod = this.retrieveMod(modName);
const eventCallback: IEventCallback = retrieveModEvent(mod, eventName); const eventCallback: IEventCallback = retrieveModEvent(mod, eventName);
return eventCallback(...args); eventCallback(...args);
} }
/** /**
@@ -208,13 +199,11 @@ export class ModAttachr implements IModAttachr {
* @param name Name of a mod. * @param name Name of a mod.
* @returns The mod under the name. * @returns The mod under the name.
*/ */
private retrieveMod(name: string): IMod { private retrieveMod(modName: string): IMod {
const mod: IMod = this.mods[name]; if (!{}.hasOwnProperty.call(this.mods, modName)) {
if (!mod) {
throw new Error(`Unknown mod requested: '${name}'.`); throw new Error(`Unknown mod requested: '${name}'.`);
} }
return mod; return this.mods[modName];
} }
} }
-20
View File
@@ -1,20 +0,0 @@
import { EventNames } from "./EventNames";
import { IModAttachrSettings } from "./IModAttachr";
import { ModAttachr } from "./ModAttachr";
/**
* @param settings Settings for the ModAttachr.
* @returns An ModAttachr instance.
*/
export const mockModAttachr = (settings?: IModAttachrSettings) =>
new ModAttachr(settings);
/**
* Holds keys for fake mod events.
*/
export class FakeEventNames extends EventNames {
/*
* Key for some arbitrary fake event.
*/
public readonly fakeEvent: string = "fakeEvent";
}
+1 -3
View File
@@ -6,10 +6,8 @@
] ]
}, },
"rules": { "rules": {
"ban-types": false,
"completed-docs": false, "completed-docs": false,
"no-any": false, "no-any": false,
"no-unsafe-any": false, "no-unsafe-any": false
"strict-boolean-expressions": false
} }
} }