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

前端 未结 8 2271
醉酒成梦
醉酒成梦 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:51

    As answered by Fayland Lam, the correct syntax is:

        local *LWP::UserAgent::get_basic_credentials = sub {
            return ( $username, $password );
        };
    

    But this is patching (dynamically scoped) the whole class and not just the instance. You can probably get away with this in your case.

    If you really want to affect just the instance, use the subclassing you described. This can be done 'on the fly' like this:

    {
       package My::LWP::UserAgent;
       our @ISA = qw/LWP::UserAgent/;
       sub get_basic_credentials {
          return ( $username, $password );
       };
    
       # ... and rebless $agent into current package
       $agent = bless $agent;
    }
    
    0 讨论(0)
  • 2020-12-08 01:57

    Building upon John Siracusa's answer… I found that I still wanted a reference to the original function. So I did this:

    MONKEY_PATCH_INSTANCE:
    {
      my $counter = 1; # could use a state var in perl 5.10
    
      sub monkey_patch_instance
      {
        my($instance, $method, $code) = @_;
        my $package = ref($instance) . '::MonkeyPatch' . $counter++;
        no strict 'refs';
        my $oldFunction = \&{ref($instance).'::'.$method};
        @{$package . '::ISA'} = (ref($instance));
        *{$package . '::' . $method} = sub {
            my ($self, @args) = @_;
            $code->($self, $oldFunction, @args);
        };
        bless $_[0], $package; # sneaky re-bless of aliased argument
      }
    }
    
    # let's say you have a database handle, $dbh
    # but you want to add code before and after $dbh->prepare("SELECT 1");
    
    monkey_patch_instance($dbh, prepare => sub {
        my ($self, $oldFunction, @args) = @_;
    
        print "Monkey patch (before)\n";
        my $output = $oldFunction->(($self, @args));
        print "Monkey patch (after)\n";
    
        return $output;
        });
    

    It's the same as in the original answer, except I pass through some parameters $self and $oldFunction.

    This lets us invoke $self's $oldFunction as usual, but decorate additional code around it.

    0 讨论(0)
提交回复
热议问题