Moose Singleton Method: Now without roles!
So instead of using roles wouldn’t it be nice if we could just do the following to an instantiated object:
$baz->add_singleton_method( legs => sub { 2 } );
Well if we define our Animal class like so then we can!
use MooseX::Declare; class Animal { method walk { "unknown" } method wild { "wild?... I'm bliming livid!" } method add_singleton_method( $method_name, $method ) { my $singleton = $self->meta->create_anon_class( superclasses => [ $self->meta->name ], methods => { $method_name => $method }, ); $singleton->add_method( meta => sub { $singleton } ); $singleton->rebless_instance( $self ); } }
This add_singleton_method does all the dirty work that applying a role to an object did in previous post.
To break this down:
my $singleton = $self->meta->create_anon_class( superclasses => [ $self->meta->name ], methods => { $method_name => $method }, );
This creates our anonymous class ($singleton). The “superclasses” part makes it a subclass of Animal and the “methods” part is where we add predefined methods (which we provide as args in add_singleton_methods)
$singleton->add_method( meta => sub { $singleton } );
Above adds the all important meta method. Without this then things can get a bit hairy!
$singleton->rebless_instance( $self );
This now reblesses the instance with this new anonymous class.
Now we can produce that singleton method like so:
my $baz = Animal->new; $baz->add_singleton_method( legs => sub { 2 } ); say $baz->legs; # => 2
And it all works in exactly same way as with role examples previously mentioned.
Now we can keep creating further anonymous subclasses by using add_singleton_method or you can just use Moose meta method to add more methods to current anonymous class:
$baz->meta->add_method( walk => sub { "when not drunk!" } );
Now if we tidy up the mechanics of all this into a Moose role with some extra fancy API:
NB. Think I’m safe from the Trade Description Act for my blog title over this role usage!)
package DoesSingletonMethod; use Moose::Role; our $singleton = sub { my $self = shift; my $methods = shift || {}; my $meta = $self->meta->create_anon_class( superclasses => [ $self->meta->name ], methods => $methods, ); $meta->add_method( meta => sub { $meta } ); $meta->rebless_instance( $self ); }; sub become_singleton { $_[0]->$singleton } sub add_singleton_method { $_[0]->$singleton({ $_[1] => $_[2] }) } sub add_singleton_methods { my $self = shift; $self->$singleton({ @_ }); } no Moose::Role; 1;
This nows means we can do the following:
use MooseX::Declare; class Animal with DoesSingletonMethod { method walk { "unknown" } method wild { "wild?... I'm bliming livid!" } } # one way to create singleton method.... my $baz = Animal->new; $baz->become_singleton; # make $baz a singleton $baz->meta->add_method( baz => 'baz!' ); # add method "baz" using meta # and another..... my $foo = Animal->new; $foo->add_singleton_method( foo => sub { 'foo!' } ); # and finally multiple methods.... my $bar = Animal->new; $bar->add_singleton_methods( bar1 => sub { 'bar1!' }, bar2 => sub { 'bar2!' }, );
Hmmm this is nice. Perhaps I’ll wrap this up as MooseX::SingletonMethod and upload to CPAN π
Trackbacks