(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/):
## 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
cd ModAttachr
+1 -1
View File
@@ -69,5 +69,5 @@
"name": "ModAttachr"
},
"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.
*/
+19 -64
View File
@@ -1,25 +1,25 @@
import { IItemsHoldr } from "itemsholdr";
import { EventNames } from "./EventNames";
import { IEventNames } from "./EventNames";
/**
* General schema for a mod, including its name and events.
*/
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;
/**
* 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 {
/**
* Mods to be immediately added via addMod.
* Event names for mods.
*/
eventNames?: IEventNames;
/**
* Mods that may be enabled or disabled.
*/
mods?: IMod[];
@@ -83,85 +88,35 @@ export interface IModAttachrSettings {
*/
itemsHolder?: IItemsHoldr;
/**
* Whether there should be a ItemsHoldr created if one isn't given.
*/
storeLocally?: boolean;
/**
* Transforms mod names to storage keys.
*/
transformModName?: ITransformModName;
/**
* Holds keys for mod events.
*/
eventNames?: EventNames;
}
/**
* Hookups for extensible triggered mod events.
*/
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.
*
* @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.
* @param modName Name of a mod to enable.
*/
enableMod(name: string, ...args: any[]): any;
enableMod(modName: string): void;
/**
* 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.
* @param modName Name of a mod to disable.
*/
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 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 eventName Name of an event to fire.
* @param args Any additional arguments to pass to event callbacks.
*/
fireEvent(name: 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;
fireEvent(eventName: string, ...args: any[]): void;
}
-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 {
ICallbackRegister, IEventCallback, IEventsRegister, IMod,
@@ -30,12 +30,12 @@ export class ModAttachr implements IModAttachr {
/**
* Holds keys for mod events.
*/
public readonly eventNames: EventNames;
private readonly eventNames: IEventNames;
/**
* 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.
@@ -58,18 +58,86 @@ export class ModAttachr implements IModAttachr {
* @param settings Settings to be used for initialization.
*/
public constructor(settings: IModAttachrSettings = {}) {
this.eventNames = settings.eventNames || new EventNames();
this.transformModName = settings.transformModName || ((name: string): string => name);
this.eventNames = settings.eventNames === undefined
? 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;
} else if (settings.storeLocally) {
this.itemsHolder = new ItemsHoldr();
}
if (settings.mods) {
if (settings.mods !== undefined) {
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.
*/
public addMod(mod: IMod): void {
private addMod(mod: IMod): void {
const modEvents: ICallbackRegister = mod.events;
for (const name in modEvents) {
@@ -96,94 +164,17 @@ export class ModAttachr implements IModAttachr {
this.mods[mod.name] = mod;
if (this.itemsHolder) {
if (this.itemsHolder !== undefined) {
const storedKey: string = this.transformModName(mod.name);
this.itemsHolder.addItem(storedKey, {
valueDefault: false,
valueDefault: Boolean(mod.enabled),
});
if (this.itemsHolder.getItem(storedKey)) {
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);
}
} else if (mod.enabled === true) {
this.enableMod(mod.name);
}
}
@@ -195,11 +186,11 @@ export class ModAttachr implements IModAttachr {
* @param args Any additional arguments to pass to event callbacks.
* @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 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.
* @returns The mod under the name.
*/
private retrieveMod(name: string): IMod {
const mod: IMod = this.mods[name];
if (!mod) {
private retrieveMod(modName: string): IMod {
if (!{}.hasOwnProperty.call(this.mods, modName)) {
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": {
"ban-types": false,
"completed-docs": false,
"no-any": false,
"no-unsafe-any": false,
"strict-boolean-expressions": false
"no-unsafe-any": false
}
}