0

I'm trying to write a perl module using Mouse, and after the object has been initialized, but before the user makes any calls, I need to initialize object1 with the two values from object2 and object3 that the user was required to give. I tried to use Mouse's after feature to have a subroutine called after new:

package Test;
use strict;
use Mouse;

has 'object1'    => ( is => 'rw', isa => 'Any');
has 'object2'    => ( is => 'ro', isa => 'Str', required => 1); 
has 'object3'    => ( is => 'ro', isa => 'Str', required => 1); 

after 'new' => sub { 
    my ($self) = @_; 
    $self->object1(#do stuff with object2 and object3);
};

1;

However, currently I get this error:

Invalid object instance: 'Test' at lib/Test.pm line 18.

Is there a way I can initialize a value with user supplied values before the user gets the object reference returned to them?

srchulo
  • 5,143
  • 4
  • 43
  • 72
  • Don't know about Mouse, but you should never override `new` with Moose. BUILD and BUILDARGS are there for such purposes. – ikegami Jan 28 '14 at 03:41
  • I'm not trying to override new, I just want to do something after new has been called. – srchulo Jan 28 '14 at 06:43
  • @ikegami What does the build function do? – srchulo Jan 28 '14 at 06:50
  • Specifically the reason for your error is that the `after "new"` sub gets passed the exact same parameters that `new` got passed. `new` doesn't get passed a copy of `$self` (because the entire point of `new` is that it *constructs* `$self`). – tobyink Jan 28 '14 at 11:33

1 Answers1

2

Mouse is compatible with Moose. Object creation has the following phases:

  1. Arguments are passed through the BUILDARGS method if one is defined. This can munge the arguments before Moose/Mouse touch them, e.g. to provide default arguments or to accommodate other calling styles than the keyword convention.
  2. A new instance is created and the fields (declared by has) are populated.
  3. The object is now set up and ready to use (from Mouse's/Moose's perspective). You might have a different idea of a correct setup, and can perform your checks in a BUILD method. This is the point where you can also perform remaining initialization that can't be expressed with has declarations.

So your example might become:

use strict;
use warnings;

package Your::Class;
use Mouse;

has 'object1' => ( is => 'rw', isa => 'Any');
has 'object2' => ( is => 'ro', isa => 'Str', required => 1); 
has 'object3' => ( is => 'ro', isa => 'Str', required => 1); 

sub BUILD { 
    my ($self) = @_; 
    $self->object1($self->object2 . $self->object3);
};

package main;
use Test::More;

# Your::Class->new(object2 => "foo", object3 => "bar");
my $instance = new_ok('Your::Class', [object2 => "foo", object3 => "bar"]);
is($instance->object1, "foobar");

done_testing;

To learn more about object construction in Moose and Moose-compatible object systems, read Moose::Manual::Construction.

amon
  • 57,091
  • 2
  • 89
  • 149