use strict; use warnings; { package MyElem; sub _line { shift; my $t = shift; print(("\t" x $t), @_, "\n"); } } { package MyCell; use base "MyElem"; sub output { my $self = shift; my ($tab) = @_; $self->_line($tab, qq(@$self)); } } { package MyCell::Header; use base "MyCell"; sub output { my $self = shift; my ($tab) = @_; $self->_line($tab, qq(@$self)); } } { package MyRow; use base "MyElem"; sub cells { my $self = shift; return map { bless [$_], "MyCell" } @$self; } sub output { my $self = shift; my ($tab) = @_; $self->_line($tab,qq()); $_->output($tab + 1) for $self->cells; $self->_line($tab,qq()); } } { package MyRow::WithHead; use base "MyRow"; sub cells { my $self = shift; my @cells = $self->SUPER::cells; bless $cells[0], "MyCell::Header"; return @cells; } } { package MyRow::AllHeads; use base "MyRow"; sub cells { my $self = shift; return map { bless [$_], "MyCell::Header" } @$self; } } { package MyTable; use base "MyElem"; sub output { my $self = shift; my ($tab) = @_; my @attributes; push @attributes, qq(id="$self->{id}") if $self->{id}; push @attributes, qq(class="$self->{class}") if $self->{class}; push @attributes, qq(style="$self->{style}") if $self->{style}; my $format = @attributes > 0 ? ' '.join(' ',@attributes) : ''; my $tag = 'table'.$format; $self->_line($tab, qq(<$tag>)); $self->_line($tab + 1,qq($self->{caption})) if $self->{caption}; $self->{headings}->output($tab+1) if $self->{headings}; $_->output($tab+1) for @{$self->{rows} || []}; $self->_line($tab,q()); } } sub data { bless \@_, "MyRow" } sub whead { bless \@_, "MyRow::WithHead" } sub headings { bless \@_, "MyRow::AllHeads" } sub table { bless {@_}, "MyTable" } my $table = table( id => 't1', headings => headings(qw/NAME STRENGTH COMMENT/), rows => [ whead(qw/1 2 no/), data (qw/2 3 yes/), whead(qw/10 20 another/), data (qw/100 200 comment/), ], ); $table->output(1);