First of all, ++Juerd.

I agree that maps, and nested maps in particular, are less readable than normal loops. Your code is very readable. But I like pure functions. This approach leaves me no easy way to stick the generated HTML into a variable. And it may just be that it is 3 AM hereabouts, but I cannot see how to do nested tables at all, when the table function is written to print.

So, here is an alternative, returning good old-fashioned strings. Beware, I have been known to overdesign stuff. In fact, I am getting dangerously close to inventing a HTML templating system, aren't I? :-)

But the code actually using the functions should still be readable, I hope.

Update: My original implementation of these routines had a memory leak. Also, the functions' behaviour when called in non-void context "inside" another call ... did not DWIM. While the first problem could be solved in several ways, the least bad solution I found to the second, was to make the functions' behaviour depend on void context or not.

Having made this change, the original three examples remain unchanged, and produce the same output as before. My two new examples illustrate the second problem.

{ my %coll; $coll{contents} = \0; # A ref. Any ref. my $element = sub { my ($name, $optional_newline) = @_;; $optional_newline ||= ''; return sub (&) { my $ret = do { # I cannot localize lexicals, but I can localize members of a # lexically scoped aggregate, much to the same effect: local $coll{contents}=""; local $coll{attributes}=""; shift->(); "<$name$coll{attributes}>$optional_newline" . "$coll{contents}</$name>$optional_newline"; }; if (defined wantarray) { return $ret; } elsif (ref $coll{contents}) { return; } else { $coll{contents} .= $ret; return; # We are in void context, but might as well be explici +t. } }; }; sub table (&); sub row (&); sub cell (&); *table = $element->( table => $/ ); *row = $element->( 'tr' => $/ ); *cell = $element->( 'td' ); # The only way to get any actual contents: sub lit (@) { $coll{contents} .= $_ for @_ } # In the simple case, this one reads a lot better: sub cells (@) { cell { lit $_; } for @_ } # I cannot approach a templating system without attributes, right? # (I am sorry already. I just cannot help it ...) sub att (@) { # Sets/unsets all attributes each time -- last call gets served: $coll{attributes}=""; while (@_) { my ($key, $value) = (shift, shift); unless (defined $value) { $coll{attributes} .= " $key"; next; } # From CGI::Utils::simple_escape: $value =~ s{&}{&amp;}gso; $value =~ s{<}{&lt;}gso; $value =~ s{>}{&gt;}gso; $value =~ s{\"}{&quot;}gso; $coll{attributes} .= qq( $key="$value"); } } } # Your examples would then need s/cell/cells/g and a print: print table { for (1..10) { row { cells "foo", "bar", "baz"; } } }; print table { row { cells @$_ } for $db->query('SELECT * FROM foo')->a +rrays }; # I have been known to overdesign my examples as well ... print table { att border => 1; my %valign = qw( foo top bar center baz bottom); my $quux_cell = sub { my $quux = shift; cell { lit $quux; att valign => $valign{$quux} if $valign{$quux}; +}; }; for (1..10) { row { cell { table { for ("top", "center", "bottom") { row { cell { att align => 'center'; lit $_; }; }; } }; }; for (qw/foo bar baz/) { $quux_cell->($_); } } } }; # New examples, per the update (see above): # Would have had the rows appearing twice in my original implementatio +n; # the first time in the order generated: print table { my @r = map {row { cells $_, $_*$_ }} 1..10; for (0..$#r) { my $j = rand($_); @r[$_,$j]=@r[$j,$_]; } lit @r; }; # Would have had all of the inner tables appearing twice in my # original implementation; the first time at the start of the row, # outside of the TD elements: print table { my $x; for (1..3) { row { for (1..3) { ++$x; cells $x, table { att border => 1; for (1..$x) { row { cells(("&nbsp;")x$x) } } }; } }; } };

The Sidhekin
print "Just another Perl ${\(trickster and hacker)},"


In reply to Re: Readable HTML tables by Sidhekin
in thread Readable HTML tables by Juerd

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.