sub table { my ($tab,$opt) = @_; my @attributes; push @attributes, qq(id="$opt->{id}") if $opt->{id}; push @attributes, qq(class="$opt->{class}") if $opt->{class}; push @attributes, qq(style="$opt->{style}") if $opt->{style}; my $format = @attributes > 0 ? ' '.join(' ',@attributes) : ''; my $tag = 'table'.$format; line($tab,qq(<$tag>)); line($tab + 1,qq($opt->{caption})) if $opt->{caption}; row($tab + 1,'header',$opt->{headings}) if $opt->{headings}; my $next_needs_head; for (@{$opt->{rows} || []}) { if (!ref and /^whead$/) { $next_needs_head = 1; next; } elsif (!ref) { $next_needs_head = 0; next; } row($tab + 1, $next_needs_head?"whead":"data", $_); $next_needs_head = 0; } line($tab,q()); } table(1, { id => 't1', 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/], ], }); #### sub table { my ($tab,$opt) = @_; my @attributes; push @attributes, qq(id="$opt->{id}") if $opt->{id}; push @attributes, qq(class="$opt->{class}") if $opt->{class}; push @attributes, qq(style="$opt->{style}") if $opt->{style}; my $format = @attributes > 0 ? ' '.join(' ',@attributes) : ''; my $tag = 'table'.$format; line($tab,qq(<$tag>)); line($tab + 1,qq($opt->{caption})) if $opt->{caption}; row($tab + 1,'header',$opt->{headings}) if $opt->{headings}; for (@{$opt->{rows} || []}) { row($tab + 1, $_->isa("MyRow::WithHead")?"whead":"data", $_); } line($tab,q()); } sub data { bless \@_, "MyRow" } sub whead { bless \@_, "MyRow::WithHead" } table(1, { id => 't1', 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/), ], }); #### sub table { my ($tab,$opt) = @_; my @attributes; push @attributes, qq(id="$opt->{id}") if $opt->{id}; push @attributes, qq(class="$opt->{class}") if $opt->{class}; push @attributes, qq(style="$opt->{style}") if $opt->{style}; my $format = @attributes > 0 ? ' '.join(' ',@attributes) : ''; my $tag = 'table'.$format; line($tab,qq(<$tag>)); line($tab + 1,qq($opt->{caption})) if $opt->{caption}; row($tab + 1,'header',$opt->{headings}) if $opt->{headings}; $_->output($tab+1) for @{$opt->{rows} || []}; line($tab,q()); } { package MyCell; sub output { my $self = shift; my ($tab) = @_; ::line($tab, qq(@$self)); } } { package MyCell::Header; use base "MyCell"; sub output { my $self = shift; my ($tab) = @_; ::line($tab, qq(@$self)); } } { package MyRow; sub cells { my $self = shift; return map { bless [$_], "MyCell" } @$self; } sub output { my $self = shift; my ($tab) = @_; ::line($tab,qq()); $_->output($tab + 1) for $self->cells; ::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; } } sub data { bless \@_, "MyRow" } sub whead { bless \@_, "MyRow::WithHead" } table(1, { id => 't1', 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/), ], }); #### 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); #### 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);