tiny_monk has asked for the wisdom of the Perl Monks concerning the following question:

Hello, Perl monks. I am currently studying how to generate a web page using PSGI/Plack. I read that it is not encouraged to use PSGI/Plack with any web development projects. Instead, a web framework that supports PSGI/Plack is recommended to those who would like to avail of the benefits of PSGI/Plack. However, I am still new to Perl. Down the road, I may use a framework if a job requires me to use one. But as of now, I don't think that using a framework will help me learn Perl and PSGI/Plack. That is why I am sticking to pure perl for now. Part of this learning process, I experienced having difficulty running a loop inside a PSGI application. The code goes like this:

#!/usr/bin/perl use strict; use warnings; use diagnostics; my $app = sub { my $html = get_html(); return [ 200, ['Content-Type' => 'text/html'], [$html] ]; }; sub get_html{ my @array; $array[0] = 'happy'; $array[1] = 'sad'; $array[2] = 'worrried'; return "<html> <head></head> <body> <select>"; foreach my $item (@array) {return "<option value='$item'>$item</option>";} return "</select> </body> </html>"; }

I gave the code a .psgi extension. I ran the code using the plackup command on the command line.The code produced a webpage on the browser that was ported to http://127.0.0.1:5000. But PSGI seemed to have ignored the foreach loop. In other words, the loop was not successfully invoked. I only got an empty dropdown menu. Unfortunately, I still do not know the solution to this problem. I'd very much appreciate if you could share your insight on this. Thanks.

Replies are listed 'Best First'.
Re: How do I run a loop within a PSGI application
by 1nickt (Canon) on Aug 18, 2015 at 05:57 UTC

    When you return from a sub, you exit the sub at that point, so the rest of it doesn't get executed.

    You want this:

    sub get_html { my @array = qw/ happy sad worried /; my $html = <<EOT; <html> <head></head> <body> <select> EOT foreach my $item (@array) { $html .= "<option value='$item'>$item</option>"; } $html .= <<EOT; </select> </body> </html> EOT return $html; }

    Disclaimer: It's very bad practice to mix other languages with Perl. If you are printing HTML you should use a templating system, which keeps the HTML in separate files and just plugs in the values of your data. I suggest not writing your own.

    The way forward always starts with a minimal test.
      Hello, 1nickt. I ran the code you suggested and it worked wonderfully. Thank you very much and I'm very grateful. :)
      Thank you 1nickt. Would there be a better way to return the entire code without ending prematurely? In a regular Perl script, using CGI, I would use the print command. However, this is totally a different context. I haven't figured out a command that would work.
      Thank you 1nickt. I'll study the code you gave me. Also, thank you for the advise on separating the HTML and Perl codes.
Re: How do I run a loop within a PSGI application
by kevbot (Vicar) on Aug 18, 2015 at 06:52 UTC
    Here is a way to use a template to generate the html (as 1nickt suggested). I used Template::Tiny, since it is a lightweight templating system whose syntax is compatible with the more feature rich Template::Toolkit. If you have Template::Toolkit installed you should be able to replace use Template::Tiny; with use Template;
    #!/usr/bin/perl use strict; use warnings; use diagnostics; use Template::Tiny; my $app = sub { my $html = get_html(); return [ 200, ['Content-Type' => 'text/html'], [$html] ]; }; sub get_html{ my @array; $array[0] = 'happy'; $array[1] = 'sad'; $array[2] = 'worrried'; my $template = do { local $/; <DATA> }; my $tt = Template::Tiny->new(); my $html; $tt->process(\$template, { my_array => \@array }, \$html); return $html; } __DATA__ <html> <head></head> <body> <select> [% FOREACH item IN my_array %]<option value='[% item %]'>[ +% item %]</option> [% END %]</select> </body> </html>
Re: How do I run a loop within a PSGI application
by GrandFather (Saint) on Aug 18, 2015 at 07:30 UTC

    To follow up 1nickt's recommendation that you should use a templating system, consider:

    use strict; use warnings; use HTML::Template; my $app = sub { my $htmlFile = genHTML_File(); my @options = map {{value => $_}} 'happy', 'sad', 'worrried'; my $tplt = HTML::Template->new(filename => $htmlFile); $tplt->param(options => \@options); return [200, ['Content-Type' => 'text/html'], [$tplt->output()]]; }; print $app->()[2][0]; sub genHTML_File { my $filename = 'delme.html'; open my $outHTML, '>', $filename or die "Can't create '$filename': + $!\n"; print $outHTML <<HTML; <html> <head></head> <body> <select><TMPL_LOOP name='options'> <option value='<TMPL_VAR name="value"/>'><TMPL_VAR name='value'/>< +/option></TMPL_LOOP> </select> </body> </html> HTML return $filename; }

    Prints:

    <html> <head></head> <body> <select> <option value='happy'>happy</option> <option value='sad'>sad</option> <option value='worrried'>worrried</option> </select> </body> </html>

    In real code genHTML_File doesn't exist. Instead you put the HTML into an external file.

    Premature optimization is the root of all job security

      Kevbot, GrandFather, I would like to thank you for sharing your codes and suggestions. All of you you are successfully teaching me the benefits of using a templating system. It's a lot easier to revisit a Perl code when the HTML code is separated from it. I will look into Template::Toolkit and HTML::Template.. Though, I wonder if either one could run on PSGI/Plack. Do you have any recommendations where good templating system is concerned? Anyway, I'll study the code that both of you had proposed. Thank you.

        HTML::Template is a lot easier to learn and use for simple applications than Template::Toolkit, at least in my opinion. However TT (Template::Toolkit) is widely used (and abused) with a lot more "power" (read - "rope to hang yourself with") than HTML::Template.

        Premature optimization is the root of all job security
Re: How do I run a loop within a PSGI application
by Anonymous Monk on Aug 18, 2015 at 16:03 UTC
    ... and you do not have to use a framework to use Plack/PSGI.

      In fact, I have been wondering about the possibility of using PSGI/Plack without using any framework. Thanks.

        "...I have been wondering about the possibility of using PSGI/Plack without using any framework"

        Me too. My 2 ¢:

        # myapp.psgi use Plack::Request; use Plack::App::URLMap; use HTML::HTML5::Builder qw[:standard]; my $slash = sub { my $env = shift; my $status = 200; my $request = Plack::Request->new($env); my $headers = [ 'Content-Type' => 'text/plain', 'Content-Length' => length $request->param('nose') ]; my $body = [ $request->param('nose') ]; [ $status, $headers, $body ]; }; my $tiny_monk = sub { my @things = ( "Sex", "Drugs", "Rock and Roll", "Charlie Haden", ) +; my $html = html( -lang => 'en', head( title('My Favorite Things'), Meta( -charset => 'utf-8' ) +, ), body( h1('Testomato'), p('My Favorite Things...'), ul( map li($_), @things ), ), ); my $status = 200; my $headers = [ 'Content-Type' => 'text/html', 'Content-Length' => length $html ]; my $body = [ $html ]; [ $status, $headers, $body ]; }; my $urlmap = Plack::App::URLMap->new; $urlmap->mount( "/" => $slash ); $urlmap->mount( "/tiny_monk" => $tiny_monk ); my $app = $urlmap->to_app; __END__

        Start the app with plackup myapp.psgi and try curl http://localhost:5000/?nose=cuke as well as curl http://localhost:5000/tiny_monk.

        Please see also Plack::Request, Plack::App::URLMap and HTML::HTML5::Builder, map, Re^4: RFC: Proofread the POD for my HTML elements module and Re^3: RFC: Proofread the POD for my HTML elements module.

        Best regards, Karl

        «The Crux of the Biscuit is the Apostrophe»