CL-WHO-like HTML templating for other languages?

前端 未结 9 1027
别那么骄傲
别那么骄傲 2020-12-10 04:44

Common Lisp guys have their CL-WHO, which makes HTML templating integrated with the \"main\" language thus making the task easier. For those who don\'t know CL-WHO, it looks

相关标签:
9条回答
  • 2020-12-10 05:12

    Perl's standard CGI module can do something similar:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    use CGI qw/:standard/;
    
    print 
        start_html("An example"),
        h1(
            {
                -align => "left",
                -class => "headerinfo",
            },
            'this is an example'
        ),
        "The CGI module has functions that add HTML:",
        ul( map li($_),
            ("start_html",
            "h1, h2, h3, etc.",
            "ol, ul, li",
            "ol, ul, li",
            "table, tr, th, td")
        ),
        "and many more.",
        end_html();
    

    That produces:

    <!DOCTYPE html
            PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
    <head>
    <title>An example</title>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    </head>
    <body>
    <h1 class="headerinfo" align="left">this is an example</h1>The CGI module has functions that add HTML:<ul><li>start_html</li> <li>h1, h2, h3, etc.</li> <li>ol, ul, li</li> <li>ol, ul, li</li> <li>table, tr, th, td</li></ul>and many more.
    </body>
    </html>
    

    The li section could be rewritten like this

    print ol(map li($_), @list);
    

    if you had a list or an array.

    0 讨论(0)
  • 2020-12-10 05:16

    Perl's CGI module has support for something like this.

    use CGI ':standard';
    use Lisp::Fmt 
    
    print header();
    
    print table( { -border => 1, -cellpading => 4},
        loop({ below => 25, by=> 5}, sub {
            my $i = shift;
            tr( {-align => 'right'} ,
                loop({ from => $i, below $i + 5}, sub {
                    my $j = shift;
                    td({-bgcolor => ($oddp eq $j ? 'pink' : 'green')}
                        fmt("~@R", 1+$j);
                })
            )
        });
    

    I tried to keep it lispy, so you'll have to implement a lispy loop function yourself. I don't really program Common List, so I hope I understood your code correctly.

    0 讨论(0)
  • 2020-12-10 05:20

    Clojure

    There are a bunch of CL-WHO-inspired HTML-generating libraries available in Clojure (as one would expect, Clojure being a Lisp). Here's how you could do it using the HTML library that comes with Compojure, and cl-format:

    (use 'compojure.html
         'com.infolace.format)
    
    (html
     [:table {:border 0 :cellpadding 4}
      (map (fn [tds] [:tr {:align "right"} tds])
           (partition 5 (map (fn [num color]
                               [:td {:bgcolor color}
                                (cl-format nil "~@R" (inc num))])
                             (range 25)
                             (cycle ["green" "pink"]))))])
    

    Compojure's HTML library makes good use of Clojure's literal hash-maps as attribute/value pairs, and using literal vectors for tags instead of lists for everything helps the tags stand out a bit and avoids some of the need for macro magic.

    partition breaks up a collection into groups of some number of elements. cycle generates an infinitely repeating list of the elements of a collection. These plus range and map help you avoid explicit loops and counter variables.

    0 讨论(0)
  • 2020-12-10 05:20

    There's html-tags, a Chicken Scheme extension. html-tags generates either [X]HTML or SXML.

    Here's an example (using the loop extension and considering string output):

    (<table> border: 0 cellpadding: 4
      (string-intersperse
       (loop for i below 25 by 5
             collect
             (<tr> align: "right"
                   (string-intersperse
                    (loop for j from i below (+ i 5)
                          collect
                          (<td> bgcolor: (if (odd? j)
                                             "pink"
                                             "green")
                                (+ 1 j))))))))
    

    I'd add links to the loop and html-utils extensions (which is built on top of html-tags), but stackoverflow is considering I'm a spammer and only allows me to post a maximum of two links.

    0 讨论(0)
  • 2020-12-10 05:26

    For CPAN offerings have a look at the following (in alphabetical order)...

    • Builder
    • HTML::AsSubs
    • HTML::Tiny
    • Markapl
    • Template::Declare
    • XML::Generator

    Using the table part of the CL-WHO example provided (minus Roman numerals and s/background-color/color/ to squeeze code into screen width here!)....


    Builder

    use Builder;
    my $builder = Builder->new;
    my $h = $builder->block( 'Builder::XML' );
    
    $h->table( { border => 0, cellpadding => 4 }, sub {
       for ( my $i = 1; $i < 25; $i += 5 ) {
           $h->tr( { align => 'right' }, sub {
               for my $j (0..4) {
                   $h->td( { color => $j % 2 ? 'pink' : 'green' }, $i + $j );
               }
           });
       } 
    });
    
    say $builder->render;
    


    HTML::AsSubs

    use HTML::AsSubs;
    
    my $td = sub {
        my $i = shift;
        return map { 
            td( { color => $_ % 2 ? 'pink' : 'green' }, $i + $_ )
        } 0..4;
    };
    
    say table( { border => 0, cellpadding => 4 },
        map { 
            &tr( { align => 'right' }, $td->( $_ ) ) 
        } loop( below => 25, by => 5 )
    )->as_HTML;
    


    HTML::Tiny

    use HTML::Tiny;
    my $h = HTML::Tiny->new;
    
    my $td = sub {
        my $i = shift;
        return map { 
            $h->td( { 'color' => $_ % 2 ? 'pink' : 'green' }, $i + $_ )
        } 0..4;
    };
    
    say $h->table(
        { border => 0, cellpadding => 4 },
        [
            map { 
                $h->tr( { align => 'right' }, [ $td->( $_ ) ] )  
            } loop( below => 25, by => 5 )    
        ]
    );
    


    Markapl

    use Markapl;
    
    template 'MyTable' => sub {
        table ( border => 0, cellpadding => 4 ) {
           for ( my $i = 1; $i < 25; $i += 5 ) {
               row ( align => 'right' ) {
                   for my $j ( 0.. 4 ) {
                       td ( color => $j % 2 ? 'pink' : 'green' ) { $i + $j }
                   }
               }
           } 
        }
    };
    
    print main->render( 'MyTable' );
    


    Template::Declare

    package MyTemplates;
    use Template::Declare::Tags;
    use base 'Template::Declare';
    
    template 'MyTable' => sub {
        table {
            attr { border => 0, cellpadding => 4 };
            for ( my $i = 1; $i < 25; $i += 5 ) {
                row  {
                    attr { align => 'right' };
                        for my $j ( 0..4 ) {
                            cell {
                                attr { color => $j % 2 ? 'pink' : 'green' } 
                                outs $i + $j;
                            }
                        }
                }
            } 
        }
    };
    
    package main;
    use Template::Declare;
    Template::Declare->init( roots => ['MyTemplates'] );
    print Template::Declare->show( 'MyTable' );
    


    XML::Generator

    use XML::Generator;
    my $x = XML::Generator->new( pretty => 2 );
    
    my $td = sub {
        my $i = shift;
        return map { 
            $x->td( { 'color' => $_ % 2 ? 'pink' : 'green' }, $i + $_ )
        } 0..4;
    };
    
    say $x->table(
        { border => 0, cellpadding => 4 },
        map { 
            $x->tr( { align => 'right' }, $td->( $_ ) )  
        } loop( below => 25, by => 5 )    
    );
    


    And the following can be used to produce the "loop" in HTML::AsSubs / HTML::Tiny / XML::Generator examples....

    sub loop {
        my ( %p ) = @_;
        my @list;
    
        for ( my $i = $p{start} || 1; $i < $p{below}; $i += $p{by} ) {
            push @list, $i;
        }
    
        return @list;
    }
    
    0 讨论(0)
  • 2020-12-10 05:29

    Haskell

    Haskell has an HTML combinator library that is not all that different from CL-WHO. The lazy functional approach to programming, though, does result in a much different idiomatic iteration structure than the loop facilities in Common Lisp:

    import Data.Char
    import Data.List
    import Text.Html
    -- from http://fawcett.blogspot.com/2007/08/roman-numerals-in-haskell.html
    import RomanNumerals
    
    -- Simple roman numeral conversion; returns "" if it cannot convert.
    rom :: Int -> String
    rom r = let m = toRoman r
            in (map toUpper . maybe "" id) m
    
    -- Group a list N elements at a time.
    -- groupN 2 [1,2,3,4,5] == [[1,2],[3,4],[5]]
    groupN n [] = []
    groupN n xs = let (a, b) = splitAt n xs in a : (groupN n b)
    
    pink = "pink" -- for convenience below; green is already covered by Text.Html
    
    rom_table = table ! [border 0, cellpadding 4] << trs
        where
          -- a list of <tr> entries
          trs = map (rom_tr . map rom_td) rom_array
    
          -- generates a <tr> from a list of <td>s
          rom_tr tds = tr ! [align "right"] << tds
    
          -- generates a <td> given a numeral and a color
          rom_td (r, c) = td ! [bgcolor c] << r
    
          -- our 5 x 5 array (list x list) of numerals and colors
          rom_array = (groupN 5 . take 25) rom_colors
    
          -- a theoretically infinite list of pairs of roman numerals and colors
          -- (practically, though, the roman numeral library has limits!)
          rom_colors = zip (map rom [1..]) colors
    
          -- an infinite list of alternating green and pink colors
          colors = cycle [green, pink]
    
    main = let s = prettyHtml rom_table 
           in putStrLn s
    

    I should note there's also a little combinator library in Text.Html for composing tables using "above" and "beside" operators to calculate row/column spanning, but it's a little too simplistic in terms of applying attributes to duplicate this example exactly, and we don't need the fancy splitting of rows and columns.

    0 讨论(0)
提交回复
热议问题