Skip to content

Moose Singleton Method: Now without roles!

July 7, 2009

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 😉

Leave a comment