Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you replace a method of a Moose object at runtime?

Is it possible to replace a method of a Moose object at runtime ? By looking at the source code of Class::MOP::Method (which Moose::Meta::Method inherits from) I concluded that by doing

 $method->{body} = sub{ my stuff }

I would be able to replace at runtime a method of an object. I can get the method using

 $object->meta->find_method_by_name(<method_name>);

However, this didn't quite work out.

Is it conceivable to modify methods at run time? And, what is the way to do it with Moose?

like image 482
xxxxxxx Avatar asked Mar 12 '10 18:03

xxxxxxx


2 Answers

Moose or not, that does not sound like a good idea.

Instead, design your object to have an accessor for the method. For example, users of your class can use My::Frobnicator->frobnicator->() to get and invoke the frobnicator method and use My::Frobnicator->frobnicator(sub { } ) to set it.

like image 170
Sinan Ünür Avatar answered Oct 18 '22 19:10

Sinan Ünür


Sinan's idea is a great start.

But with an little extra tweak, you can make using your method accessor just like using a normal method.

#!/usr/bin/perl
use strict;
use warnings;
use Carp;

my $f = Frob->new;

$f->frob(
    sub { 
        my $self = shift;
        print "$self was frobbed\n"; 
        print Carp::longmess('frob') 
    }
);

print "\nCall frob as normal sub\n";
$f->frobit;

print "\nGoto frob\n";
$f->goto_frob;

BEGIN { 
    package Frob;
    use Moose;

    has 'frob' => (
        is => 'rw',
        isa => 'CodeRef',
    );

    sub frobit {
        &{$_[0]->frob};
    }
    sub goto_frob {
        goto $_[0]->frob;
    }

}

The two methods in Frob are very similar.

  • frobit passes all arguments, including the invocant to the code ref.
  • goto_frob passes all arguments, including the invocant to the code ref, and replaces goto_frob's stack frame with the code refs.

Which to use depends on what you want in the stack.


Regarding munging the body storage of a Class::MOP::Method object, like so $method->{body} = sub { 'foo' }:

It's never a good idea to violate encapsulation when you are doing OOP. Especially not when you are working with complex object systems like Moose and Class::MOP. It's asking for trouble. Sometimes, there is no other way to get what you want, but even then, violating encapsulation is still a bad idea.

like image 4
daotoad Avatar answered Oct 18 '22 18:10

daotoad