NAME
Class::Monkey - Monkey Patch a class/instance with modifiers and other sweet stuff
DESCRIPTION
Say we have a module installed on the system. It does some handy things, but you find a bug or a strange feature. We can easily fix it without subclassing by the following...
# StupidClass.pm
package SupidClass;
sub new {
my $class = shift;
return bless {}, $class;
}
sub name {
my ($self, $name) = @_;
print "Hello, ${name}\n";
}
sub no_args {
print "No arguments were specified!\n";
}
1;
Above is our class. A stupid one at that. The name
method doesn't validate the arguments.. it just tries to print them in a 'hello' string. We can use an around
method to call the name
method if arguments are passed, or to call no_args
if not. We can happily do this from the program.
# our_program.pl
use Class::Monkey qw<StupidClass>;
# The patch
around 'name' => sub {
my $method = shift;
my $self = shift;
if (@_) {
$self->$method(@_);
}
else {
$self->no_args();
}
},
qw<StupidClass>;
# /The Patch
$s->name(); # actually executes no_args
$s->name("World"): # runs name
SYNOPSIS
Simply import the classes you want to patch as an array when you use Class::Monkey
. Doing this means you won't even need to use
the module you want to patch - Class::Monkey takes care of that for you.
use Class::Monkey qw<Some::Package Another::Module>;
method 'needThisMethod' => sub {
...
},
qw<Some::Package>;
my $p = Some::Package->new;
$p->needThisMethod;
METHODS
haz
Please see tweak
for more information on how to get this method. haz
behaves the exact same way as extends
.
use Class::Monkey '-tweak';
haz 'FooClass';
haz qw<FooClass Another::FooClass More::FooClass>;
tweak
This method is only available when you use Class::Monkey '-tweak'
. This option may be preferred over the default modifier methods when you need to patch a class from a script using Moose/Mouse/Moo/Mo, etc. When you add -tweak, it will export only the tweak
and haz
methods.
use Class::Monkey '-tweak';
haz 'Foo';
tweak 'mymethod' => (
class => 'Foo',
override => sub {
print "mymethod has been overridden\n";
},
);
You can replace 'override' in the above example with any of the available Class::Monkey modifiers (ie: before, method, after, around). Also class
can be the full name of the class as above, or an instance.
canpatch
Tells Class::Monkey you want to be able to patch the specified modules, but not to 'use' them.
use Class::Monkey;
use MyFoo;
canpatch qw<MyFoo AndThis AndThat>;
# then do stuff with MyFoo as normal
exports
Have a subroutine in your file you want to explort to your patched class? Use exports
to do so.
package Foo;
sub new { return bless {}, __PACKAGE__ }
1;
# test.pl
package MyPatcher;
use Class::Monkey qw<Foo>;
sub foo { print "Hiya\n"; }
exports 'foo', qw<Foo>;
my $foo = Foo->new;
$foo->foo(); # prints Hiya
exports 'foo', $foo; # works with instances too
extends
Sometimes you might not want to include the module you want to patch when you use Class::Monkey
. No problem. You can use extends
to do it later on.
use Class::Monkey;
extends 'SomeClass';
extends qw<SomeClass FooClass>;
has
Gives the wanted class an accessor. You can assign it a read-only or read-writable type (Similar to Moose). Because it works on remote packages you need to give it the full name of the method including the class.
use Class::Monkey qw<Foo::Class>;
has 'Foo::Class::greet' => ( is => 'ro', default => 'Hello' ); # read-only
has 'Foo::Class::name' => ( is => 'rw', default => 'World' ); # read-writable
my $foo = Foo::Class->new;
say "Hello, " . $foo->name;
$foo->name('Monkey); # updates the name accessor to return a new value
If you leave out the is
parameter when you define an accessor it will always default to read-writable.
instance
Note This method should be deprecated as all modifiers now support constants OR an instance. Class::Monkey will determine which method should be used, so calling instance
is no longer required.
Patch an instance method instead of an entire class
# Pig.pm
package Pig;
sub new { return bless {}, shift; }
sub says { print "Oink!\n"; }
# test.pl
package main;
use Class::Monkey qw<Pig>;
my $pig = Pig->new;
my $pig2 = Pig->new;
instance 'says' => sub {
print "Meow\n";
},
$pig2;
# only $pig2 will have its says method overridden
As of 0.002 you can now do it like this
override 'says' => sub {
print "Meow\n";
}, $pig2;
before 'says' => sub {
print "Going to speak\n";
}, $pig;
etc..
original
If you want to run the original version of a patched method, but not unpatch it right away you can use original
to do so. It will run the old method before it was patched with any arguments you specify, but the actual method will still remain patched.
after 'someMethod' => sub {
print "Blah\n"
},
qw<Foo>;
original('Foo', 'someMethod', qw<these are my args>);
OR if you prefer, you can just call Class::Monkey::PatchedClassName::method-
(@args)>
Class::Monkey::Foo->someMethod('these', 'are', 'my', 'args);
override
Overrides an already existing method. If the target method doesn't exist then Class::Monkey will throw an error.
override 'foo' => sub {
return "foo bar";
},
qw<Some::Module>;
method
Creates a brand new method in the target module. It will NOT allow you to override an existing one using this, and will throw an error.
method 'active_customers' => sub {
my $self = shift;
return $self->search({ status => 'active' });
},
qw<Schema::ResultSet::Customer>;
before
Simply adds code to the target method before the original code is ran
# Foo.pm
package Foo;
sub new { return bless {}, __PACKAGE__; }
sub hello { print "Hello, $self->{name}; }
1;
# test.pl
use Class::Monkey qw<Foo>;
my $foo = Foo->new;
before 'hello' => {
my $self = shift;
$self->{name} = 'World';
},
qw<Foo>;
print $foo->hello . "\n";
after
Basically the same as before
, but appends the code specified to the END of the original
around
Around gives the user a bit more control over the subroutine. When you create an around method the first argument will be the original method, the second is $self
and the third is any arguments passed to the original subroutine. In a away this allows you to control the flow of the entire subroutine.
package MyFoo;
sub greet {
my ($self, $name) = @_;
print "Hello, $name!\n";
}
1;
# test.pl
use Class::Monkey qw<MyFoo>;
# only call greet if any arguments were passed to MyFoo->greet()
around 'greet' => sub {
my $method = shift;
my $self = shift;
$self->$method(@_)
if @_;
},
qw<MyFoo>;
unpatch
Undoes any modifications made to patched methods, restoring it to its original state.
override 'this' => sub {
print "Blah\n";
}, qw<FooClass>;
unpatch 'this', 'FooClass';
AUTHOR
Brad Haywood <brad@geeksware.net>
LICENSE
You may distribute this code under the same terms as Perl itself.