How can I monkey-patch an instance method in Perl?

前端 未结 8 2310
醉酒成梦
醉酒成梦 2020-12-08 01:25

I\'m trying to monkey-patch (duck-punch :-) a LWP::UserAgent instance, like so:

sub _user_agent_get_basic_credentials_patch {
  return ($usernam         


        
8条回答
  •  误落风尘
    2020-12-08 01:37

    In the spirit of Perl's "making hard things possible", here's an example of how to do single-instance monkey patching without mucking with the inheritance.

    I DO NOT recommend you actually doing this in any code that anyone else will have to support, debug or depend on (like you said, consenting adults):

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    {
    
        package Monkey;
    
        sub new { return bless {}, shift }
        sub bar { return 'you called ' . __PACKAGE__ . '::bar' }
    }
    
    use Scalar::Util qw(refaddr);
    
    my $f = Monkey->new;
    my $g = Monkey->new;
    my $h = Monkey->new;
    
    print $f->bar, "\n";    # prints "you called Monkey::bar"
    
    monkey_patch( $f, 'bar', sub { "you, sir, are an ape" } );
    monkey_patch( $g, 'bar', sub { "you, also, are an ape" } );
    
    print $f->bar, "\n";    # prints "you, sir, are an ape"
    print $g->bar, "\n";    # prints "you, also, are an ape"
    print $h->bar, "\n";    # prints "you called Monkey::bar"
    
    my %originals;
    my %monkeys;
    
    sub monkey_patch {
        my ( $obj, $method, $new ) = @_;
        my $package = ref($obj);
        $originals{$method} ||= $obj->can($method) or die "no method $method in $package";
        no strict 'refs';
        no warnings 'redefine';
        $monkeys{ refaddr($obj) }->{$method} = $new;
        *{ $package . '::' . $method } = sub {
            if ( my $monkey_patch = $monkeys{ refaddr( $_[0] ) }->{$method} ) {
                return $monkey_patch->(@_);
            } else {
                return $originals{$method}->(@_);
            }
        };
    }
    

提交回复
热议问题