How can I overload Moose constructors?

Sorry for the Java jargon, but how can I overload the Moose constructors?

Suppose I represent a segment. I can either take a start point and a point, or a start point and a length, or an end point and a length.

How can I allow such alternative construction methods?

+4
source share
2 answers

You do not need to override new . You can specify your own BUILD :

 #!/usr/bin/perl package My::Segment; use Moose; use namespace::autoclean; use Carp qw( confess ); has 'start' => (is => 'ro', isa => 'Num', predicate => 'has_start', writer => '_set_start', ); has 'end' => (is => 'ro', isa => 'Num', predicate => 'has_end', writer => '_set_end', ); has 'length' => (is => 'ro', isa => 'Num', predicate => 'has_length', writer => '_set_length', ); sub BUILD { my $self = shift; $self->has_start and $self->has_end and $self->length and do { return if $self->length == $self->end - $self->start; confess "Inconsistent start, end and length"; }; $self->has_start and $self->has_end and do { $self->_set_length($self->end - $self->start); return; }; $self->has_start and $self->has_length and do { $self->_set_end($self->start + $self->length); return; }; $self->has_end and $self->has_length and do { $self->_set_start($self->end - $self->length); return; }; confess "At least two of start, end or length must be supplied"; } __PACKAGE__->meta->make_immutable; package main; use YAML; my $x = My::Segment->new(start => 0, length => 3); my $y = My::Segment->new(start => 1, end => 4); my $z = My::Segment->new(end => 5, length => 3); print Dump($_) for $x, $y, $z; my $w = My::Segment->new(start => 0, end => 0, length => 1); 
+11
source

Sinan BUILD answer is probably the easiest, most direct solution. Using BUILDARGS , as mentioned above, is also a smart solution.

I felt it worth mentioning that Type Coercions could be used. Given the class:

 class LineSegment { has [qw(startX startY endX endY)] => ( isa => 'Num', is => 'ro', required => 1 ); } 

You can use a set of constraints, for example:

 class_type 'LineSegment'; subtype StartLength => as Hashref => where { exists $_->{startX} && $_->{startY} && $_->{length} }; subtype EndLength => as Hashref => where { exists $_->{endX} && $_->{endY} && $_->{length} }; coerce LineSegment => from StartLength => via { my ($endX, $endY) = calc_end($_); LineSegment->new( startX => $_->{startX}, startY => $_->{startY}, endX => $endX, endY => $endY, )}; coerce LineSegment => from EndLength => via { my ($startX, $startY) = calc_start($_); LineSegment->new( startX => $startX, startY => $startY, endX => $_->{endX}, endY => $_->{endY}, )}; 

Then in your code:

  use Moose::Util::TypeConstraints; find_type_constraint('LineSegment')->coerce({ startX => $x, startY => $y, length => $length }); 

Although this example may be redundant, there are several times when coercion is an elegant solution. For example, if you have an existing LineSegment class, you do not want to add the length attribute (although BUILDARGS will work BUILDARGS too)

+3
source

All Articles