Skip to content

Hot method patching framework for handling environmental method differences

License

Notifications You must be signed in to change notification settings

perry-mitchell/hot-patcher

Repository files navigation

Hot-Patcher

Hot method patching framework for handling environmental method differences

Build status npm version

About

Hot-Patcher provides a simple API to manage patched methods. I found while writing Buttercup that managing overwritten methods between environments (Node/Browser/React-Native) was becoming cumbersome, and having a single agreed-upon method of doing so was the best way to go.

Installation

Install Hot-Patcher from npm:

npm install hot-patcher --save

NB: This is an ESM library.

Usage

Hot-Patcher is a class and can simply be instantiated:

import { HotPatcher } from "hot-patcher";

const hp = new HotPatcher();

Hot-Patcher is designed to be used with patchable tools:

import { HotPatcher } from "hot-patcher";

export class MyHelper {
    public patcher: HotPatcher;

    constructor() {
        this.patcher = new HotPatcher();
    }

    increment(arg: number): number {
        return this.patcher.patchInline<number>("increment", someArg => {
            return someArg + 1;
        }, arg);
    }
}

You can then patch methods when required:

import { MyHelper } from "./MyHelper.js";

export function getHelper() {
    const helper = new MyHelper();
    helper.patch("increment", (val: number) => val + 2);
    return helper;
}

Patched methods can easily be fetched later:

import { getSharedPatcher } from "./patching.js";

const randomString = getSharedPatcher().get("randomString");
randomString(5); // Generates a random string

// Or, execute the method directly:
getSharedPatcher().execute("randomString", 5) // Generates a random string

You can check if a method is patched by using isPatched: patcher.isPatched("some method").

Inline patching and execution

Ideally you could wrap function implementation with a patch call, executing it on demand:

function add(a: number, b: number): number {
    return patcher.patchInline("add", (a, b) => a + b, a, b);
}

patcher.isPatched("add"); // false
add(1, 2); // 3
patcher.isPatched("add"); // true
// calling add() multiple times will call the patched method without "re-patching" it
// over and over again..

Plugins - Chaining/Sequencing functions

You can use Hot-Patcher to create sequences of functions:

patcher.plugin("increment", x => x * 2, x => x * 2);

patcher.execute("increment", 2); // 8

Which is basically syntactic sugar for a regular patch() call:

patcher
    .patch("increment", x => x * 2, { chain: true })
    .patch("increment", x => x * 2, { chain: true });

patcher.execute("increment", 2); // 8

Executing a regular patch() without chain: true will overwrite all chained methods with the new method.

Calling patch() with chain: true when a method already exists will simply add the new method after the existing:

patcher
    .patch("increment", x => x * 2, { chain: false }) // or simply without `chain` specified
    .patch("increment", x => x * 2, { chain: true });

patcher.execute("increment", 2); // still 8

Restoring methods

Methods can be restored to their originally patched function by calling the restore method:

const methodA = () => {};
const methodB = () => {};

patcher
    // methodA is now the current (and original)
    .patch("someMethod", methodA)
    // methodB is now the current
    .patch("someMethod", methodB);

// Restore "someMethod" to methodA (original)
patcher.restore("someMethod");

Use Sparingly

The intention of Hot-Patcher is not to push every method into a patching instance, but to provide a common API for specific methods which require patching in some specific environments or in situations where users/consumers are expected to provide their own custom implementations.

About

Hot method patching framework for handling environmental method differences

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published