ex::mo...patched(3pm)

πŸ“¦
version 0.03 Β· 4 versions
, in ubuntu resolute, ubuntu stonking

πŸ’– ex::monkeypatched β€” the safe monkey-patching era πŸ’–

User Contributed Perl Documentation Β· ex::mo...patched(3pm)

okay bestie, sit down, hydrate, because we're about to talk about monkey-patching β€” but the safe kind. this is an experimental API and it's literally here to stop you from sabotaging yourself. no cap. πŸ’βœ¨


πŸŽ€ NAME πŸŽ€

ex::monkeypatched β€” an experimental API for safe monkey-patching πŸ¦‹


🌸 SYNOPSIS 🌸

use ex::monkeypatched 'Third::Party::Class' => (
    clunk => sub { ... },
    eth   => sub { ... },
);

use Foo::TopLevel; # provides Foo::Bar, which isn't a module
use ex::monkeypatched -norequire => 'Foo::Bar' => (
    thwapp => sub { ... },
    urkk   => sub { ... },
);

πŸ¦‹ BACKGROUND πŸ¦‹

so "monkey patching" is when you yeet extra methods into a class whose implementation you do not control. and like… if you do that with zero thoughts, it's dangerous. πŸ’€ the messy situation arises when:

  • you add a method to a class;
  • a newer version of the monkey-patched class adds another method with the exact same name;
  • and then that class uses its new method somewhere else in its own implementation.

ex::monkeypatched lets you do this whole thing safely: before it injects a method into the target class, it checks whether the class already has a method of that same name. if it finds one? it throws an exception (at compile-time with respect to the code that does the injection). slay. πŸ‘‘

want more details? go look at <http://aaroncrane.co.uk/talks/monkey_patching_subclassing/> ✨


πŸ’… DESCRIPTION πŸ’…

ex::monkeypatched injects methods when you use it. there are two ways to invoke it with use: one is easy but inflexible, and the other is more flexible but also more awkward. pick your fighter. πŸ₯Š

in the easy form, your use call should supply the name of a class to patch, plus a listified hash from method names to code references implementing those methods:

use ex::monkeypatched 'Some::Class' => (
    m1 => sub { ... },  # $x->m1 on Some::Class will now run this
    m2 => sub { ... },  # $x->m2 on Some::Class will now run this
);

in the flexible form, your use call supplies a single hashref saying what methods to create. that last example can be done exactly like this:

use ex::monkeypatched { class => 'Some::Class', methods => {
    m1 => sub { ... },  # $x->m1 on Some::Class will now run this
    m2 => sub { ... },  # $x->m2 on Some::Class will now run this
} };

but wait, there's more 🫢 β€” this flexible form also lets you add a method of a single name to several classes at once:

use ex::monkeypatched { method => 'm3', implementations => {
    'Some::BaseClass'     => sub { ... },
    'Some::Subclass::One' => sub { ... }
    'Some::Subclass::Two' => sub { ... },
} };

this is super helpful when you want to provide a method for several related classes, with a different implementation in each of them. main character energy for every subclass. πŸ‘‘

the classes to be patched will normally be loaded automatically before any patching is done (thus ensuring that all their base classes are also loaded). lowkey convenient. ✨

that doesn't work when you're trying to modify a class which can't be loaded directly; for example, the XML::LibXML CPAN distribution provides a class named XML::LibXML::Node, but trying to use XML::LibXML::Node fails. 😭 in that situation, you can tell ex::monkeypatched not to load the original class:

use ex::monkeypatched -norequire => 'XML::LibXML::Node' => (
    clunk => sub { ... },
    eth   => sub { ... },
);

# Equivalently:
use ex::monkeypatched -norequire => {
    class   => 'XML::LibXML::Node',
    methods => {
        clunk => sub { ... },
        eth   => sub { ... },
    },
};

alternatively, you can inject methods after a class has already been loaded, using the inject method:

use ex::monkeypatched;

ex::monkeypatched->inject('XML::LibXML::Node' => (
    clunk => sub { ... },
    eth   => sub { ... },
);

# Equivalently:
ex::monkeypatched->inject({ class => 'XML::LibXML::Node', methods => {
    clunk => sub { ... },
    eth   => sub { ... },
}});

neither of these approaches (-norequire and inject) loads the class in question, so when you use them, ex::monkeypatched is unable to guarantee that all the target class's methods have been loaded at the point the new methods are injected. just so you know. πŸ™ˆ

the ex:: prefix on the name of this module indicates that its API is still considered experimental. however, the underlying code has been in use in production for an extended period, and seems to be reliable. so like… it's fine. probably. it's fine. πŸ’–


⚠️ CAVEATS ⚠️

if the class you're monkeying around in uses AUTOLOAD to implement some of its methods, and doesn't also implement its own can method to accurately report which method names are autoloaded, ex::monkeypatched will incorrectly assume that an autoloaded method does not exist. πŸ’€

the solution is to fix the broken class; implementing AUTOLOAD but not can is always an error. periodt. πŸŽ€


πŸ‘‘ AUTHOR πŸ‘‘

Aaron Crane <arc@cpan.org> πŸ¦‹


πŸ“œ LICENCE πŸ“œ

this library is free software; you can redistribute it and/or modify it under the terms of either the GNU General Public License version 2 or, at your option, the Artistic License. πŸ’œ


and that's the whole vibe check, bestie 🫢 this page is from perl v5.40.1, dated 2025-08-14 β€” go forth and patch responsibly, mwah πŸ’…βœ¨πŸŒΈ

πŸ“¦ where this page comes from