Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create (or not) class instance methods at construction time based on inputs?

Tags:

class

perl

How would I create my class such that some methods will exist in the instance only if certain values were passed to the constructor?

Perhaps a more generic way of asking is: How can I add a method to an existing class instance?

like image 251
Douglas Mauch Avatar asked Apr 16 '13 19:04

Douglas Mauch


3 Answers

You can attach an anonymous sub to an object based on flags:

use strict;
use warnings;
package Object;
sub new {
   my $class = shift;
   my $self = bless {}, $class;
   my %args = @_; 
   if ($args{method}) {
       $self->{method} = sub { print "hello\n" }
   }   
   return $self;
}

sub method {
    my $self = shift;
    if (not defined $self->{method}) {
        warn "Not bound\n";
        return;
    }   
    $self->{method}->();
}
1;

to use:

use Object;
my $obj1 = Object->new(method=>1);
$obj1->method();
my $obj2 = Object->new();
$obj2->method();

You can extend this to a number of methods through the same interface.

like image 91
perreal Avatar answered Nov 14 '22 21:11

perreal


You can use Moose to apply a role at runtime.

package My::Class;
use Moose;

has foo => ( isa => 'Str', is => 'ro', required => 1 );

sub BUILD {
  my $self = shift;

  if ($self->foo eq 'bar') {
    My::Class::Role->meta->apply($self);
  }  
}

no Moose;

package My::Class::Role;
use Moose::Role;

sub frobnicate {
  my $self = shift;

  print "Frobnicated!\n";
}

no Moose;

my $something = My::Class->new( foo => 'bar' );
print $something, "\n";
$something->frobnicate;
my $something_else = My::Class->new( foo => 'baz' );
print $something_else, "\n";
$something_else->frobnicate;

Gives:

Moose::Meta::Class::__ANON__::SERIAL::1=HASH(0x2fd5a10)
Frobnicated!
My::Class=HASH(0x2fd2c08)
Can't locate object method "frobnicate" via package "My::Class" at testmoose.pl line 32.
like image 21
Oesor Avatar answered Nov 14 '22 22:11

Oesor


use AUTOLOAD to define the function. As a example method foo is called if $self->{foo} exists

sub AUTOLOAD {
    my $methodname = $AUTOLOAD;
    if ($methodname eq "foo" && exists($_[0]->{foo})){
          goto &fooimplementationsub;
    }
    return;
}

An alternative technique is to use globs to define a new method at runtime

*PACKAGE::method = sub { 
    #code here
};

This has the disadvantage that the method is now visible to all instances of the class so is not quite what you want.

A third and possibly more risky/inefficient method is to use string eval

eval <<EOF
sub foo { 
    #code here
};
EOF

Again this has the disadvantage that the method is now visible to all instances of the class so is not quite what you want.

like image 21
user1937198 Avatar answered Nov 14 '22 23:11

user1937198