object-middleware
v1.3.0
Published
Subscribe middlewares to methods of a class
Downloads
10
Maintainers
Readme
object-middleware
Install
npm install object-middleware
yarn add object-middleware
About
This simple function should help with subscribing middlewares to methods of an object. It helps when you use a 3rd party package and you would like to inject your own logic into them. For example when you use MongoDb and you would like to validate the response from the database.
Using
/**
* Subscribe a middleware to all methods or to specific methods (constructor is excluded)
*
* @param object Subject object
* @param middleware Middleware function
* @param type Type of the middleware
* @param methodName Name of a method or list of method names, if is not specified or an empty array, all methods will be touched
* @param subscribeInPrototype If is set to true, middleware will be subscribed in prototype = all instances of the parent class will be affected
*/
subscribe(
object,
middleware,
type = ObjectMiddlewareType.BEFORE,
methodName = [],
subscribeInPrototype = false
)
/**
* Type-safe subscribe function (for TypeScript only). This method doesn't work with private methods.
*
* @param object Subject object
* @param middleware Middleware function
* @param methodName Name of a method
* @param type Type of the middleware
* @param subscribeInPrototype If is set to true, middleware will be subscribed in prototype = all instances of the parent class will be affected
*/
subscribeTypeSafe(
object,
middleware,
methodName,
type,
subscribeInPrototype = false
)
/**
* Unsubscribe middleware
*
* @param object Subject object
* @param middleware Middleware function
* @param type Type of the middleware
* @param methodName Name of a method or list of method names, if is not specified or an empty array, all methods will be touched
* @param unsubscribeInPrototype If is set to true, middleware will be unsubscribed in prototype = all instances of the parent class will be affected
*/
unsubscribe(
object,
middleware,
type = ObjectMiddlewareType.BEFORE,
methodName = [],
unsubscribeInPrototype = false
)
/**
* Unsubscribe all
*
* @param object Subject object
* @param methodName Name of a method or list of method names, if is not specified or an empty array, all methods will be touched
* @param unsubscribeInPrototype If is set to true, middleware will be unsubscribed in prototype = all instances of the parent class will be affected
*/
const unsubscribeAll = (
object,
methodName = [],
unsubscribeInPrototype = false
)
More info in the example section.
Documentation
Middleware types:
AFTER
: the middleware is performed after an object method
BEFORE
: the middleware is performed before an object method
CONDITION_AFTER
: an object method is performed, then the middleware is performed; if the meddleware's return is true, the return is the object method return. Otherwise will return undefined
CONDITION_BEFORE
: an object method is performed if the return of the middleware is true
OVERRIDE
: middleware can overwrite the return of an object method
Middleware function:
Middleware function will receive at least one parameter. First parameter is an object with reference to the object where the middleware is subscribed, the name of the method where the middleware is subscribed and the result from the origin method. The result is passed only in types AFTER
, CONDITION_AFTER
and OVERRIDE
.
type ObjectMiddlewareParams<T, R = any> = {
methodName: string; // name of the origin method
ref: T; // reference to the origin object
result?: R; // result of the origin method; undefined in types BEFORE and CONDITION_BEFORE
}
As next parameters are passed parameters from the origin object method.
class MyClass {
method(prop1: number, prop2: string) {} // origin method
}
const myObject = new MyClass(); // origin object
// if this middleware will be subscribed to the myObject.method then as the second and third parameter will be passed parameter from the origin method
const middleware = <T>(ref: ObjectMiddlewareParams<T>, prop1: number, prop2: string)
// or
const middleware = <T>(ref: ObjectMiddlewareParams<T>, ...props: any[])
Examples
class MyClass {
myMethod() {
console.log("myMethod");
}
myReturn() {
console.log("myReturn");
return 123;
}
passProps(prop1: number, prop2: string) {
console.log("passProps");
return `${prop1}${prop2}`;
}
}
const myObject = new MyClass();
AFTER
- The middleware is performed after an object method.
const middleware = () => {
console.log("middleware");
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.AFTER
);
myObject.myMethod(); // CALL the METHOD
// console:
// myMethod
// middleware
BEFORE
- The middleware is performed before an object method.
const middleware = () => {
console.log("middleware");
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.BEFORE
);
myObject.myMethod(); // CALL the METHOD
// console:
// middleware
// myMethod
CONDITION_AFTER
- An object method is performed, then the middleware is performed; the meddleware's return is true
, than the return is the object method return.
const middleware = () => {
console.log("middleware");
return true;
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.CONDITION_AFTER
);
console.log(myObject.myReturn()); // CALL the METHOD
// console:
// myReturn
// middleware
// 123
CONDITION_AFTER
- An object method is performed, then the middleware is performed; the meddleware's return is false
, than the return is undefined.
const middleware = () => {
console.log("middleware");
return false;
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.CONDITION_AFTER
);
console.log(myObject.myReturn()); // CALL the METHOD
// console:
// myReturn
// middleware
// undefined
CONDITION_BEFORE
- An object method is performed because the middleware's return is true
.
const middleware = () => {
console.log("middleware");
return true;
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.CONDITION_BEFORE
);
console.log(myObject.myReturn()); // CALL the METHOD
// console:
// middleware
// myReturn
// 123
CONDITION_BEFORE
- An object method is not performed because the middleware's return is false
.
const middleware = () => {
console.log("middleware");
return false;
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.CONDITION_BEFORE
);
console.log(myObject.myReturn()); // CALL the METHOD
// console:
// middleware
// undefined
OVERRIDE
- Middleware will overwrite the return of an object method.
const middleware = () => {
console.log("middleware");
return 987;
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.OVERRIDE
);
console.log(myObject.myReturn()); // CALL the METHOD
// console:
// myReturn
// middleware
// 987
OVERRIDE
- Middleware will return the return of an object method.
const middleware = (ref) => {
console.log("middleware");
return ref.result;
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.OVERRIDE
);
console.log(myObject.myReturn()); // CALL the METHOD
// console:
// myReturn
// middleware
// 123
Passing properties
const middleware = (
ref: ObjectMiddlewareParams<MyClass>,
...props: any
) => {
console.log(ref);
console.log(props);
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.AFTER
);
myObject.passProps(55, "abcd"); // CALL the METHOD
// console:
// {
// methodName: 'passProps',
// ref: MyClass {
// myMethod: [Function (anonymous)],
// myReturn: [Function (anonymous)],
// passProps: [Function (anonymous)]
// },
// result: "55abcd"
// }
// [ 55, 'abcd' ]
More Examples
Working with simple object and TypeScript type-safe using. Trying to subsribe in a private method or anything else except for a method type will cause a TypeScript error.
const myObject = {
myMethod: (num: number): number => {
return num;
}
};
const middleware: ObjectMiddlewareFunction<
typeof myObject,
typeof myObject["myMethod"]
> = (ref, num: number) => {
return ref.result && num > 10 ? ref.result : 0;
};
subscribeTypeSafe(
myObject,
middleware,
"myMethod",
ObjectMiddlewareType.OVERRIDE
);
console.log(myObject.myMethod(9));
console.log(myObject.myMethod(11));
// console:
// 0
// 11
This code will cause an error.
class MyClass {
private myMethod() {
}
}
const myClass = new MyClass();
const myObject = {
myMethod: 5
};
const middleware = () => {};
// this code will cause an error because myMetod doesn't exist in myClass (because it is private we can't access to it)
subscribeTypeSafe(
myClass,
middleware,
"myMethod",
ObjectMiddlewareType.BEFORE
);
// this code will cause an error because myObject.myMethod is not a function
subscribeTypeSafe(
myObject,
middleware,
"myMethod",
ObjectMiddlewareType.BEFORE
);
Subscribe the middleware only to "passProps" method.
subscribe(
myObject,
middleware,
ObjectMiddlewareType.BEFORE,
[myObject.passProps.name]
);
ASYNC functions and Promises work as well.
class MyClass {
async passProps(prop: number) {
return prop;
}
}
const myObject = new MyClass();
const middleware = async (
ref: ObjectMiddlewareParams<MyClass>,
prop: number
) => {
return prop > 10;
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.CONDITION_BEFORE
);
console.log(await myObject.passProps(5))
// comsole:
// undefined
console.log(await myObject.passProps(11))
// comsole:
// 11
Overriding a private method works as well.
class MyClass {
private privateMethod(prop: number) {
return prop;
}
publicMethod(prop: number) {
return this.privateMethod(prop);
}
}
const myObject = new MyClass();
const middleware = (
ref: ObjectMiddlewareParams<MyClass>,
prop: number
) => {
return prop * 2;
};
subscribe(
myObject,
middleware,
ObjectMiddlewareType.OVERRIDE,
["privateMethod"]
);
console.log(myObject.publicMethod(5))
// console:
// 10
Subscribing a middleware to the prototype of an object. This will affect all objects created from the same class.
class MyClass {
constructor(private num: number) {}
testMethod(num: number) {
return this.num;
}
}
const myObject1 = new MyClass(4);
const myObject2 = new MyClass(6);
const myObject3 = new MyClass(8);
const middleware: ObjectMiddlewareOverrideFunction<MyClass> = (
ref: ObjectMiddlewareParams<MyClass>,
num: number
) => {
return ref.result * num;
};
subscribe(
myObject1,
middleware,
ObjectMiddlewareType.OVERRIDE,
[],
true
);
console.log(myObject1.testMethod(2));
console.log(myObject2.testMethod(4));
console.log(myObject3.testMethod(6));
// console:
// 8
// 24
// 48
Subscribing a middleware in the prototype and add another middleware to single instance.
class TestClass {
constructor() {}
testMethod() {
return 0;
}
}
const testObject1 = new TestClass();
const testObject2 = new TestClass();
const middleware1 = () => 15;
const middleware2 = () => 20;
subscribe(
testObject1,
middleware1,
ObjectMiddlewareType.OVERRIDE,
[],
true
);
console.log(testObject1.testMethod());
console.log(testObject2.testMethod());
// console:
// 15
// 15
subscribe(
testObject2,
middleware2,
ObjectMiddlewareType.OVERRIDE
);
console.log(testObject1.testMethod());
console.log(testObject2.testMethod());
// console:
// 15
// 20
// this must be called before unsubscribeAll
unsubscribe(
testObject2,
middleware2,
ObjectMiddlewareType.OVERRIDE
);
unsubscribeAll(testObject2, [], true);
console.log(testObject1.testMethod());
console.log(testObject2.testMethod());
// console:
// 0
// 0
Note
When you subsribe a middleware in the prototype and then you use unsubscribeAll
, the middleware will be ubsubscribed in all instances. You can subscribe a middleware in the prototype and then subscribe another middleware in a single instance.
When you combine subscribing in a prototype and in a instance, you need to unsubscribe them in the correct order.
License
MIT © MartinTichovsky