.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "ex::monkeypatched 3pm" .TH ex::monkeypatched 3pm "2011-12-22" "perl v5.20.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" ex::monkeypatched \- Experimental API for safe monkey\-patching .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 4 \& use ex::monkeypatched \*(AqThird::Party::Class\*(Aq => ( \& clunk => sub { ... }, \& eth => sub { ... }, \& ); \& \& use Foo::TopLevel; # provides Foo::Bar, which isn\*(Aqt a module \& use ex::monkeypatched \-norequire => \*(AqFoo::Bar\*(Aq => ( \& thwapp => sub { ... }, \& urkk => sub { ... }, \& ); .Ve .SH "BACKGROUND" .IX Header "BACKGROUND" The term \*(L"monkey patching\*(R" describes injecting additional methods into a class whose implementation you don't control. If done without care, this is dangerous; the problematic case arises when: .IP "\(bu" 4 You add a method to a class; .IP "\(bu" 4 A newer version of the monkey-patched class adds another method \fIof the same name\fR .IP "\(bu" 4 And uses that new method in some other part of its own implementation. .PP \&\f(CW\*(C`ex::monkeypatched\*(C'\fR lets you do this sort of monkey-patching safely: before it injects a method into the target class, it checks whether the class already has a method of the same name. If it finds such a method, it throws an exception (at compile-time with respect to the code that does the injection). .PP See for more details. .SH "DESCRIPTION" .IX Header "DESCRIPTION" \&\f(CW\*(C`ex::monkeypatched\*(C'\fR injects methods when you \f(CW\*(C`use\*(C'\fR it. There are two ways to invoke it with \f(CW\*(C`use\*(C'\fR: one is easy but inflexible, and the other is more flexible but also more awkward. .PP In the easy form, your \f(CW\*(C`use\*(C'\fR call should supply the name of a class to patch, and a listified hash from method names to code references implementing those methods: .PP .Vb 4 \& use ex::monkeypatched \*(AqSome::Class\*(Aq => ( \& m1 => sub { ... }, # $x\->m1 on Some::Class will now run this \& m2 => sub { ... }, # $x\->m2 on Some::Class will now run this \& ); .Ve .PP In the flexible form, your \f(CW\*(C`use\*(C'\fR call supplies a single hashref saying what methods to create. That last example can be done exactly like this: .PP .Vb 4 \& use ex::monkeypatched { class => \*(AqSome::Class\*(Aq, methods => { \& m1 => sub { ... }, # $x\->m1 on Some::Class will now run this \& m2 => sub { ... }, # $x\->m2 on Some::Class will now run this \& } }; .Ve .PP However, this flexible form also lets you add a method of a single name to several classes at once: .PP .Vb 5 \& use ex::monkeypatched { method => \*(Aqm3\*(Aq, implementations => { \& \*(AqSome::BaseClass\*(Aq => sub { ... }, \& \*(AqSome::Subclass::One\*(Aq => sub { ... } \& \*(AqSome::Subclass::Two\*(Aq => sub { ... }, \& } }; .Ve .PP This is helpful when you want to provide a method for several related classes, with a different implementation in each of them. .PP 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). .PP That doesn't work when you're trying to modify a class which can't be loaded directly; for example, the XML::LibXML \s-1CPAN\s0 distribution provides a class named \f(CW\*(C`XML::LibXML::Node\*(C'\fR, but trying to \f(CW\*(C`use XML::LibXML::Node\*(C'\fR fails. In that situation, you can tell \f(CW\*(C`ex::monkeypatched\*(C'\fR not to load the original class: .PP .Vb 4 \& use ex::monkeypatched \-norequire => \*(AqXML::LibXML::Node\*(Aq => ( \& clunk => sub { ... }, \& eth => sub { ... }, \& ); \& \& # Equivalently: \& use ex::monkeypatched \-norequire => { \& class => \*(AqXML::LibXML::Node\*(Aq, \& methods => { \& clunk => sub { ... }, \& eth => sub { ... }, \& }, \& }; .Ve .PP Alternatively, you can inject methods after a class has already been loaded, using the \f(CW\*(C`inject\*(C'\fR method: .PP .Vb 1 \& use ex::monkeypatched; \& \& ex::monkeypatched\->inject(\*(AqXML::LibXML::Node\*(Aq => ( \& clunk => sub { ... }, \& eth => sub { ... }, \& ); \& \& # Equivalently: \& ex::monkeypatched\->inject({ class => \*(AqXML::LibXML::Node\*(Aq, methods => { \& clunk => sub { ... }, \& eth => sub { ... }, \& }}); .Ve .PP Neither of these approaches (\f(CW\*(C`\-norequire\*(C'\fR and \f(CW\*(C`inject\*(C'\fR) loads the class in question, so when you use them, \f(CW\*(C`ex::monkeypatched\*(C'\fR is unable to guarantee that all the target class's methods have been loaded at the point the new methods are injected. .PP The \f(CW\*(C`ex::\*(C'\fR prefix on the name of this module indicates that its \s-1API\s0 is still considered experimental. However, the underlying code has been in use in production for an extended period, and seems to be reliable. .SH "CAVEATS" .IX Header "CAVEATS" If the class you're monkeying around in uses \f(CW\*(C`AUTOLOAD\*(C'\fR to implement some of its methods, and doesn't also implement its own \f(CW\*(C`can\*(C'\fR method to accurately report which method names are autoloaded, \f(CW\*(C`ex::monkeypatched\*(C'\fR will incorrectly assume that an autoloaded method does not exist. The solution is to fix the broken class; implementing \f(CW\*(C`AUTOLOAD\*(C'\fR but not \f(CW\*(C`can\*(C'\fR is always an error. .SH "AUTHOR" .IX Header "AUTHOR" Aaron Crane .SH "LICENCE" .IX Header "LICENCE" This library is free software; you can redistribute it and/or modify it under the terms of either the \s-1GNU\s0 General Public License version 2 or, at your option, the Artistic License.