{ package Row; use Moose; use Moose::Util::TypeConstraints; has 'row' => ( traits => ['Array'], is => 'ro', isa => 'ArrayRef[Cell]', required => 1, default => sub { [] }, handles => { pushCell => 'push', getCell => 'get', allCells => 'elements', }, ); } #### sub addCell { my ($self, $index, @cells) = @_; $self->pushRow( [] ) if !defined $self->getRow($index); $self->getRow($index)->pushCell(@cells); } #### { package Grid;; use Moose; use Moose::Util::TypeConstraints; # coerce an 'ArrayRef[ArrayRef[Cell]]' struct provided by the caller into an 'ArrayRef[Row]' type subtype 'A::Row' => as 'ArrayRef[Row]'; coerce 'A::Row' => from 'ArrayRef[ArrayRef[Cell]]' => via { [ map {Row->new( row => $_ )} @$_ ] }; # coerce an 'ArrayRef[Cell]' struct provided by the caller into a 'Row' type coerce 'Row' => from 'ArrayRef[Cell]' => via { Row->new( row => $_ ) }; has 'grid' => ( traits => ['Array'], is => 'ro', isa => 'A::Row', required => 1, default => sub { [] }, coerce => 1, handles => { addRow => 'push', getRow => 'get', allRows => 'elements', }, ); } #### my $grid = Grid->new( grid => [ [$cellA0, $cellB0, $cellC0], [$cellA1, $cellB1, $cellC1], [$cellA2], ] ); #### #!/usr/bin/perl -w use Modern::Perl '2011'; { package Cell; use Moose; has 'name' => ( is => 'rw', isa => 'Str', default => '', ); no Moose; __PACKAGE__->meta->make_immutable; } { package Row; use Moose; use Moose::Util::TypeConstraints; has 'row' => ( traits => ['Array'], is => 'ro', isa => 'ArrayRef[Cell]', required => 1, default => sub { [] }, handles => { pushCell => 'push', getCell => 'get', allCells => 'elements', }, ); sub toString { my $self = shift; join "\t", map($_->name, $self->allCells); } no Moose; no Moose::Util::TypeConstraints; __PACKAGE__->meta->make_immutable; } { package Grid;; use Moose; use Moose::Util::TypeConstraints; # coerce an 'ArrayRef[ArrayRef[Cell]]' struct provided by the caller into an 'ArrayRef[Row]' type subtype 'A::Row' => as 'ArrayRef[Row]'; coerce 'A::Row' => from 'ArrayRef[ArrayRef[Cell]]' => via { [ map {Row->new( row => $_ )} @$_ ] }; # coerce an 'ArrayRef[Cell]' struct provided by the caller into a 'Row' type coerce 'Row' => from 'ArrayRef[Cell]' => via { Row->new( row => $_ ) }; has 'grid' => ( traits => ['Array'], is => 'ro', isa => 'A::Row', required => 1, default => sub { [] }, coerce => 1, handles => { addRow => 'push', getRow => 'get', allRows => 'elements', }, ); sub addCell { my ($self, $index, @cells) = @_; $self->addRow( [] ) if !defined $self->getRow($index); $self->getRow($index)->pushCell(@cells); } sub toString { my $self = shift; join "\n", map {join "\t", map($_->name, $_->allCells)} $self->allRows; } no Moose; no Moose::Util::TypeConstraints; __PACKAGE__->meta->make_immutable; } use strict; ################## ### Test Cases ### ################## # testing Row class #CASE #1: create a new instance by first building a row (reference to an array), and then passing this to the constructor print "BEGIN row1 test\n"; my $row; for my $y ("A" .. "E") { my $mycell = Cell->new( name => "${y}0" ); push @$row, $mycell; } my $row1 = Row->new( row => $row ); print $row1->toString . "\n"; my $mycell = Cell->new( name => "F0" ); $row1->pushCell($mycell); print $row1->toString . "\n"; #$row1->pushCell( q/Whammi/ ); #print $row1->toString . "\n"; print "END row1 test\n\n"; #CASE #2: create a new empty instance of a row, then add new elements individually. print "BEGIN row2 test\n"; my $row2 = Row->new; for my $y ("A" .. "E") { my $mycell = Cell->new( name => "${y}0" ); $row2->pushCell($mycell); } print $row2->toString . "\n"; #$row2->pushCell( q/Whammi/ ); #print $row2->toString . "\n"; print "END row2 test\n\n"; # testing Grid class #CASE #1A: create a new Grid instance by first building an array of Row structs, and then passing this to the constructor (as a reference) print "BEGIN grid1A test\n"; my $struct1A; #my @struct1A; # both of these methods work for my $x (0 .. 4) { my $rowRef; for my $y ("A" .. "E") { my $mycell = Cell->new( name => "$y$x" ); push @$rowRef, $mycell; } my $row = Row->new( row => $rowRef ); push @$struct1A, $row; #push @struct1A, $row; } my $grid1A = Grid->new( grid => $struct1A ); #my $grid1A = Grid->new( grid => \@struct1A ); print $grid1A->toString . "\n"; print "\n"; my $mycell1A = Cell->new( name => "F3" ); $grid1A->addCell(3, $mycell1A); print $grid1A->toString . "\n"; #$grid1A->addCell(3, q/Whammi/); #print $grid1A->toString . "\n"; print "END grid1A test\n\n"; #CASE #1B: create a new Grid instance by first building a 2d-array of Cell structs, and then passing this to the constructor (as a reference) :: coercion needs to be working for this to succeed print "BEGIN grid1B test\n"; #my $struct1B; my @struct1B; #both methods work for my $x (0 .. 4) { for my $y ("A" .. "E") { my $mycell = Cell->new( name => "$y$x" ); #push @{$$struct1B[$x]}, $mycell; # yikes! that's pretty scary! Builds an array-of-arrays-of-Cells as a reference push @{$struct1B[$x]}, $mycell; } } #my $grid1B = Grid->new( grid => $struct1B ); my $grid1B = Grid->new( grid => \@struct1B ); print $grid1B->toString . "\n"; print "\n"; my $mycell1B = Cell->new( name => "F3" ); $grid1B->addCell(3, $mycell1B); print $grid1B->toString . "\n"; #$grid1B->addCell(3, q/Whammi/); #print $grid1B->toString . "\n"; print "END grid1B test\n\n"; #CASE #2A: create a new empty Grid instance, then add new rows individually as Row structs. print "BEGIN grid2A test\n"; my $grid2A = Grid->new; for my $x (0 .. 4) { #my $rowRef; my @row; # both of these methods work as well for my $y ("A" .. "E") { my $mycell = Cell->new( name => "$y$x" ); #push @$rowRef, $mycell; push @row, $mycell; } #my $row = Row->new( row => $rowRef ); my $row = Row->new( row => \@row ); $grid2A->addRow( $row ); } print $grid2A->toString . "\n"; print "\n"; my $mycell2A = Cell->new( name => "F3" ); $grid2A->addCell(3, $mycell2A); print $grid2A->toString . "\n"; #$grid2A->addCell(3, q/Whammi/); #print $grid2A->toString . "\n"; print "END grid2A test\n\n"; #CASE #2B: create a new empty Grid instance, then add new rows individually as an array of Cell structs :: coercion needs to be working for this to succeed print "BEGIN grid2B test\n"; my $grid2B = Grid->new; for my $x (0 .. 4) { #my $rowRef; my @row; # both of these methods work for my $y ("A" .. "E") { my $mycell = Cell->new( name => "$y$x" ); #push @$rowRef, $mycell; push @row, $mycell; } $grid2B->addRow( \@row ); #$grid2B->addRow( $rowRef ); } print $grid2B->toString . "\n"; print "\n"; my $mycell2B = Cell->new( name => "F3" ); $grid2B->addCell(3, $mycell2B); print $grid2B->toString . "\n"; #$grid2B->addCell(3, q/Whammi/); #print $grid2B->toString . "\n"; print "END grid2B test\n\n"; #CASE #3: create a new empty Grid instance, then add new rows individually as an array of Cell structs :: coercion needs to be working for this to succeed #this differs from CASE 2B in that I am constructing an anonymous array in one expression, rather than pushing the elements one-by-one onto the anon array. print "BEGIN grid3 test\n"; my $grid3 = Grid->new; my $o1 = Cell->new( name => 'X1' ); my $o2 = Cell->new( name => 'Y1' ); my $o3 = Cell->new( name => 'Z1' ); $grid3->addRow([$o1, $o2, $o3]); print $grid3->toString . "\n"; my $o4 = Cell->new( name => 'W1' ); $grid3->addCell(0, $o4); print $grid3->toString . "\n"; #$grid3->addRow( qw/Whammi1 Whammi2/ ); #print $grid3->toString . "\n"; print "END grid3 test\n\n"; #CASE #4: last case; create a new empty Grid instance, then add each cell one-by-one print "BEGIN grid4 test\n"; my $grid4 = Grid->new; for my $x (0 .. 4) { for my $y ("A" .. "E") { my $mycell = Cell->new( name => "$y$x" ); $grid4->addCell($x, $mycell); } } print $grid4->toString . "\n"; print "\n"; my $mycell4A = Cell->new( name => "F3" ); $grid4->addCell(3, $mycell4A); print $grid4->toString . "\n"; print "\n"; my $mycell4B = Cell->new( name => "A5" ); $grid4->addCell(5, $mycell4B); print $grid4->toString . "\n"; print "END grid4 test\n\n"; #### BEGIN row1 test A0 B0 C0 D0 E0 A0 B0 C0 D0 E0 F0 END row1 test BEGIN row2 test A0 B0 C0 D0 E0 END row2 test BEGIN grid1A test A0 B0 C0 D0 E0 A1 B1 C1 D1 E1 A2 B2 C2 D2 E2 A3 B3 C3 D3 E3 F3 A4 B4 C4 D4 E4 END grid1A test BEGIN grid1B test A0 B0 C0 D0 E0 A1 B1 C1 D1 E1 A2 B2 C2 D2 E2 A3 B3 C3 D3 E3 F3 A4 B4 C4 D4 E4 END grid1B test BEGIN grid2A test A0 B0 C0 D0 E0 A1 B1 C1 D1 E1 A2 B2 C2 D2 E2 A3 B3 C3 D3 E3 F3 A4 B4 C4 D4 E4 END grid2A test BEGIN grid2B test A0 B0 C0 D0 E0 A1 B1 C1 D1 E1 A2 B2 C2 D2 E2 A3 B3 C3 D3 E3 F3 A4 B4 C4 D4 E4 END grid2B test BEGIN grid3 test X1 Y1 Z1 X1 Y1 Z1 W1 END grid3 test BEGIN grid4 test A0 B0 C0 D0 E0 A1 B1 C1 D1 E1 A2 B2 C2 D2 E2 A3 B3 C3 D3 E3 F3 A4 B4 C4 D4 E4 A5 END grid4 test