use strict; use warnings; { package MyElem; sub new { my $class = shift; my %opts = @_; my $self = bless \%opts, $class; $self->_init; return $self; } sub _init { my $self = shift; } sub _tagname { shift; return "span"; } sub id { shift->{id} } sub class { shift->{class} } sub style { shift->{style} } sub title { shift->{title} } sub _start_tag { my $self = shift; my $tag = join " ", $self->_tagname, map { sprintf '%s="%s"', $_, $self->$_ } grep { defined $self->$_ } qw(id class style title); return "<$tag>"; } sub _end_tag { my $self = shift; my $tag = $self->_tagname; return ""; } sub output { my $self = shift; my ($tab) = @_; $self->_line($tab, $self->_start_tag, $self->value, $self->_end_tag); } sub _line { shift; my $t = shift; print(("\t" x $t), @_, "\n"); } } { package MyCell; use base "MyElem"; sub value { shift->{value}; } sub _tagname { return "td"; } sub output { my $self = shift; my ($tab) = @_; $self->_line($tab, $self->_start_tag, $self->value, $self->_end_tag); } } { package MyCell::Header; use base "MyCell"; sub _tagname { return "th"; } } { package MyRow; use base "MyElem"; use Scalar::Util qw(blessed); sub _init { my $self = shift; @{$self->{cells}} = map { blessed($_) ? $_ : "MyCell"->new(value => $_) } @{$self->{cells}||[]}; $self->SUPER::_init; } sub cells { @{shift->{cells}}; } sub _tagname { return "tr"; } sub output { my $self = shift; my ($tab) = @_; $self->_line($tab, $self->_start_tag); $_->output($tab + 1) for $self->cells; $self->_line($tab, $self->_end_tag); } } { package MyRow::WithHead; use base "MyRow"; use Scalar::Util qw(blessed); sub _init { my $self = shift; $self->{cells}[0] = "MyCell::Header"->new(value => $self->{cells}[0]) unless blessed($self->{cells}[0]); $self->SUPER::_init; } } { package MyRow::AllHeads; use base "MyRow"; use Scalar::Util qw(blessed); sub _init { my $self = shift; @{$self->{cells}} = map { blessed($_) ? $_ : "MyCell::Header"->new(value => $_) } @{$self->{cells}||[]}; $self->SUPER::_init; } } { package MyTable; use base "MyElem"; use Scalar::Util qw(blessed); sub _init { my $self = shift; @{$self->{rows}} = map { blessed($_) ? $_ : "MyRow"->new(cells => $_) } @{$self->{rows}||[]}; $self->SUPER::_init; } sub rows { @{shift->{rows}}; } sub _tagname { return "table"; } sub output { my $self = shift; my ($tab) = @_; $self->_line($tab, $self->_start_tag); $self->_line($tab + 1,qq($self->{caption})) if $self->{caption}; $_->output($tab+1) for $self->rows; $self->_line($tab, $self->_end_tag); } } sub data { my @args = @_; "MyRow"->new(cells => \@args) } sub whead { my @args = @_; "MyRow::WithHead"->new(cells => \@args) } sub headings { my @args = @_; "MyRow::AllHeads"->new(cells => \@args) } my $table = "MyTable"->new( id => 't1', rows => [ headings (qw/NAME STRENGTH COMMENT/), whead (qw/1 2 no/), data (qw/2 3 yes/), whead (qw/10 20 another/), data (qw/100 200 comment/), data (1000, "MyCell"->new(value => 2000, style=>"color:red"), "again"), ], ); $table->output(1);