starter

46 views
Skip to first unread message

ivo welch

unread,
Aug 16, 2016, 2:53:19 PM8/16/16
to psgi-plack

I am thinking about writing a new web program in PSGI/Plack.  I purchased the 24-chapter book, but it seems geared towards porting over existing apps, not as a starter tutorial.

as I was looking for more info, I noticed the following is dead on the plack webpage:

Patrick Donelan writes a good introduction for PSGI and Plack from the web application developers point of view.

but did not see an email for the maintainer.

nice starters are in http://search.cpan.org/~leejo/CGI-Alternatives-0.14/lib/CGI/Alternatives.pm and http://savage.net.au/Perl/html/plack.for.beginners.html .

so I really need basic tutorial examples / documentation for a new app.  specifically, I need
  • hello world (documentation already exists).  I like the simplicity of keeping each webpage in its own .pl file.  Less enthused by mojo like approach of one big dispatch app.  but I could live with it.   is one dispatch app required for psgi/plack (http://advent.plackperl.org/2009/12/day-12-maps-multiple-apps-with-mount-and-urlmap.html)?
  • session management (aka CGI:Session) -- aka php $_SESSION and CGI::SESSION
  • GET, POST, SERVER read access -- aka php $_GET $_POST $_SERVER and CGI::param
  • http redirect -- aka php header("Location: /elsewhere/file.thml") and CGI::redirect
  • CGI::Carp fatalstobrowser  -- aka php default
  • headers to make served up content public but expiring
I have been using Text::Template for a while, and it seems fine.  but it could be easily switched.

that's it.

probably 50 lines of code.  would it be possible for someone in the know to post here and on the plack/psgi website, too?

does psgi need/have an application skeleton generator, like Mojolicious::Command::generate::app?


help?

Andy Colson

unread,
Aug 16, 2016, 2:58:26 PM8/16/16
to psgi-...@googlegroups.com
1) I don't know of an easy way to have each page as its own .pl, sorry. 
2) sessions are pretty easy, as long as you dont mind either always starting it, or never starting it, based on url.  There is no start session in the middle of processing.
3) php is a bit confused.  GET and POST are methods, which you can get in plack with $req->method.  query params are availble with $req->parameters->{'q'}.  Most of the server stuff is available to, path_info for example is $req->path_info.
4) http redirect is as simple as returning a 302 header, which you can do.
5) uh... dunno ... probably.  I've seen dancer/mojo do this, but not sure how.

You dont need dancer/mojo, you can write using straight plack, that's how I do it.

my standard template would be something like:


#!/usr/bin/perl

use common::sense;
use Plack::Builder;
use Plack::Request;
use Plack::App::File;
use Plack::Session::Store::File;
use Template;

my %cfg = loadconfig();
my $tt = Template->new( {INCLUDE_PATH => 'templates'} );

my %funcs = (
    'main' => \&doIndex,
    'login' => \&doLogin,
    'q' => \&doSearch,
);

my $app = sub {
    my $env = shift; # PSGI env
    my $req = Plack::Request->new($env);
    if (! $req->session) {
        return [ 404, [ 'Content-Type' => 'text/html; charset=utf-8' ], [ '404 session not found' ] ];
    }
    $req->session->{counter}++;
    if (not defined $req->session->{auth})
    {
        $req->session->{auth} = 0;
    };

    my $uri = lc($req->path_info);
    $uri =~ s!^/!!o;     # Remove the first slash
    if (!$uri)
    {
        $uri = 'main';
    }
    #warn("uri = [$uri]");

    my @args = split(/\//, $uri);
    $req->env->{args} = \@args;

    my $proc = $funcs{$args[0]};
    if ($proc) {
        return $proc->($req);
    } else {
        return [ 404, [ 'Content-Type' => 'text/html; charset=utf-8' ], [ '404 not found' ] ];
    }
};

builder {
    mount "/static" => builder {
        enable 'ConditionalGET';
        enable 'ETag',
            file_etag => [qw/inode mtime size/],
            ;

        enable 'ContentLength';
        Plack::App::File->new(root => "static")->to_app;
    };
    mount "/" => builder {
        enable 'Session',
            store => Plack::Session::Store::File->new(
                    dir => 'session'
                );

        $app;
    };
};



On Tue, Aug 16, 2016 at 1:40 PM, ivo welch <ivo...@gmail.com> wrote:

I am thinking about writing a new web program in PSGI/Plack.  I purchased the 24-chapter book, but it seems geared towards porting over existing apps, not as a starter tutorial.

as I was looking for more info, I noticed the following is dead on the plack webpage:

Patrick Donelan writes a good introduction for PSGI and Plack from the web application developers point of view.

but did not see an email for the maintainer.

nice starters are in http://search.cpan.org/~leejo/CGI-Alternatives-0.14/lib/CGI/Alternatives.pm and http://savage.net.au/Perl/html/plack.for.beginners.html .

so I really need basic tutorial examples / documentation for a new app.  specifically, I need
  • GET, POST, SERVER read access -- aka php $_GET $_POST $_SERVER
  • http redirect -- aka php header("Location: /elsewhere/file.thml");
  • CGI::Carp fatalstobrowser  -- aka php default
I have been using Text::Template for a while, and it seems fine.  but it could be easily switched.

that's it.

probably 50 lines of code.  would it be possible for someone in the know to post here and on the plack/psgi website, too?

does psgi need/have an application skeleton generator, like Mojolicious::Command::generate::app?


help?

--

---
You received this message because you are subscribed to the Google Groups "psgi-plack" group.
To unsubscribe from this group and stop receiving emails from it, send an email to psgi-plack+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

ivo welch

unread,
Aug 16, 2016, 6:27:40 PM8/16/16
to psgi-plack

thanks for this good starter.  I cpan'ed Plack PSGI Plack::Session, Plack::Middleware::ETag, and Template .  I commented out the loadconfig().  then it compiled.  started

    $ sudo plackup plack.pl  ## needed or sessions and templates directories need to be writeable by nobody

I fire the browser up to localhost:5000, and I get impressive looking error output, that seems to be a mix of amateur and pro.  Specifically, I see

Undefined subroutine &Plack::Sandbox::_2fUsers_2fme_2testsite_2fwww_2fplack_2epl::doIndex called at /Users/me/testsite/www/plack.pl line 44.
 at /Users/ivo/syllabus.space/www/phpi/plack.pl line 43

and then nice code boxes.  I guess _2F needs to be decoded to '/', but this is minor.  I am now feeling my way in the dusk (no longer dark).

ETag were a mystery, too.  what do inode mtime size do?  I am guessing that it is doing exactly what I want---cache expiry if inode, mtime, or file size change.  much more convenient than the old expiries and pragma that I dealt with.

I also discovered common::sense through your example. much more convenient than what I had been using, which was enumerating these cases!


The error was, at first, mystery.  I see the mount that presumably builds up "/", and since I am requesting localhost:5000 in my browser, this is what should be hit.  alas, I started to get somewhere when I defined

    sub doIndex {
     
return [ '200', [ 'Content-Type' => 'text/html' ], [ 42 ], ];
   
}



and then requested localhost:5000/main .  I guess I can now replace the %funcs call `my proc = $funcs{$args[1]}` with something like

 args[0] =~ s/\.//g; ## ignore all attempts to traverse up the hierarchy or use hidden files (-e "/$DOCUMENTROOT$args[0]") or return [ 404, [ 'Content-Type' => 'text/html; charset=utf-8' ], [ "404 file $args[0] not found" ] ];
 
my $perlcode= slurp("/$DOCUMENTROOT/$args[0]"); ## could check that the first line contains perl magic
 
my @rv = eval perlcode;
 
($@) and die "in nice html, say your perlcode in $args[0] died";
 
($#rv != 2) and die "in nice html, say we want three args back from $args[0]";
 
return @rv;



known-to-work starter examples (each showing off a different feature) would be a great help on the Plack http://plackperl.org/ website.  alas, I don't know tatsuhikos' email (who I am assuming maintains it).

thanks again, andy.

regards,

/iaw

Tatsuhiko Miyagawa

unread,
Aug 16, 2016, 7:27:01 PM8/16/16
to psgi-...@googlegroups.com
I read the email.

You're recommended to use frameworks such as Mojolicious or Dancer 2.

Plack/PSGI is not a layer you should consider using when you're at a starter level.


--

---
You received this message because you are subscribed to the Google Groups "psgi-plack" group.
To unsubscribe from this group and stop receiving emails from it, send an email to psgi-plack+...@googlegroups.com.

ivo welch

unread,
Aug 16, 2016, 8:02:35 PM8/16/16
to psgi-...@googlegroups.com

thx, t.  I got it to work for myself.   Mojo and Dancer are very heavyweight.  with some scaffolding, Placks's learning curve becomes a lot lower.  CGI is deprecated and was the only other low-scaffolding solution.  I hate php, but I can write a 10 line lightweight program to record the email of my visitors in a file.

​sorry, I think Plack is a great solution.​  just wanted to help.



Sent with MailTrack

----
Ivo Welch (ivo....@gmail.com)
http://www.ivo-welch.info/
J. Fred Weston Distinguished Professor of Finance
Anderson School at UCLA, C524
Free Finance Textbook, http://book.ivo-welch.info/
Exec Editor, Critical Finance Review, http://www.critical-finance-review.org/
Editor and Publisher, FAMe, http://www.fame-jagazine.com/

To unsubscribe from this group and stop receiving emails from it, send an email to psgi-plack+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to a topic in the Google Groups "psgi-plack" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/psgi-plack/0tS-oI6B__0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to psgi-plack+unsubscribe@googlegroups.com.

Tatsuhiko Miyagawa

unread,
Aug 16, 2016, 8:05:28 PM8/16/16
to psgi-...@googlegroups.com
Back to the original question of loading different .pl files for each URL - https://metacpan.org/pod/Plack::App::PSGIBin would do exactly that. It loads up .psgi files under a directory and creates a map to load them by URL path.

To unsubscribe from this group and stop receiving emails from it, send an email to psgi-plack+...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to a topic in the Google Groups "psgi-plack" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/psgi-plack/0tS-oI6B__0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to psgi-plack+...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--

---
You received this message because you are subscribed to the Google Groups "psgi-plack" group.
To unsubscribe from this group and stop receiving emails from it, send an email to psgi-plack+...@googlegroups.com.

ivo welch

unread,
Aug 16, 2016, 8:47:25 PM8/16/16
to psgi-plack

here is another cool solution, based upon the original skeleton.  the goal is to have a file, say `main/index.pl` be writeable as

#!/usr/bin/perl placked code
use strict;  ## irrelevant, but shows flexibility;

"
<html>
<body style=\"background-color:cyan\">
<h1>hello</h1>
<p>Our session counter is "
.($req->session->{counter})."</p>
<p>We have had good results with this perl file named <tt>"
.$perlfilename."</tt>.</p>
</body>
</html>
"



and we can get all the goodies, with hidden scaffolding of the underlying perl templating code (could add Exporter, etc.).  it solves (almost) all of my problems and then some:

1. prints something to the world.
2. shows session management --- except why does it increase the counter by 2 on every invokation??
3. parameter access via $req->parameters->{'q'} and  $req->path_info
4. the ability to redirect --- could be specially parsed or codable.
5. scaffolding all elsewhere where I no longer have to worry about it.

the "and then some" is huge.  MINIMAL CODE FOR MINIMAL PURPOSES.  my code is now lightweight, just as CGI, but I can get the goodies of, say, plackup.  of course, I have a lot more to learn here, but this is a great start. sorry, tatsuhiko, but I think something like this ( of course more professionally written than what I can do in my first day with plack ) is ideal for a lightweight low-learning curve approach to web programming.

I think I prefer this solution to Plack::App::PSGIBin because (1) the code in each page is lighter-weight; (2) the docs on Plack::App:PSGIBin lack working examples.

#!/usr/bin/perl -w
use warnings FATAL => qw{ uninitialized };  ## or use warnings FATAL => ':all';
use common::sense;
use Plack::Builder;
use Plack::Request;
use Plack::App::File;
use Plack::Session::Store::File;

my $CODEROOT = "./";
my $htmlcontent= [ 'Content-Type' => 'text/html; charset=utf-8' ]; ## standard default

my $app = sub {
 
my $env = shift; # PSGI env
 
my $req = Plack::Request->new($env);

 
($req->session) or return [ 404, [ 'Content-Type' => 'text/html; charset=utf-8' ], [ '404 session not found' ] ];
  $req
->session->{counter}++;
 
(defined($req->session->{auth})) or $req->session->{auth} = 0;
 
my $uri = lc($req->path_info); ## case-insensitive for windows
  $uri
=~ s#^/##o; ## Remove the first slash

 
(defined($uri)) or $uri = 'index.pl';

 
use Cwd;
 
while ($uri =~ s/[^a-z]\.//g) { }   ## ignore all replace, e.g., /./ab or /.ab, but not /asd.cd/as

 
sub findfile {
   
foreach (@_) { ((-r "$CODEROOT/$_") && (-f "$CODEROOT/$_")) and return "$CODEROOT/$_"; }
   
return undef;
 
}
 
my $perlfilename = findfile( "$uri", "$uri.pl", "$uri/index", "$uri/index.pl" );

 
my $fourofour="404 file '$uri'=>'$perlfilename' definitely not found in ".getcwd()
   
." with ".($perlfilename||"")."<p>check that your permissions are set correctly";
     
## ."<pre backgroundcolor=\"orange\">". glob("*")."</pre>"; ## for further checking

 
(defined($perlfilename)) or return [ 404, $htmlcontent, [ $fourofour ] ];

 
use Perl6::Slurp;
 
my $perlcode= slurp($perlfilename); ## could check that the first line contains perl magic

 
($perlcode) or return [ 404, $htmlcontent, "no content in $perlfilename" ];

 
# perl template code...nice...very nice...
 
my @rv = eval "#!/usr/bin/perl -w\nuse common::sense;\nuse warnings FATAL => qw{ uninitialized };\n$perlcode";
 
($@) and return [ 404, $htmlcontent, "<p>your perlcode in $uri died with error <b>$@</b></p>" ];

 
($#rv < 0) and return [ 404, $htmlcontent, "<p>your perlcode in $uri must return at least one (html string) argument" ];
 
($#rv < 1) and return [ 200, $htmlcontent, [ $rv[0] ] ];  ## common html return
 
($#rv < 2) and return [ 200, $rv[0], [ $rv[1] ] ]; ## can this do redirect?

 
return @rv;

};
 
((-d 'session') && (-d 'template')) or die "please have writable session and template directories or change location";

ivo welch

unread,
Aug 19, 2016, 5:45:28 PM8/19/16
to psgi-plack

the answer is that plackup requests not only the file, but also favicon.ico.  thus, it is called twice for each request, and the counter increases by 2.

Tatsuhiko Miyagawa

unread,
Aug 19, 2016, 5:52:56 PM8/19/16
to psgi-plack
plackup does not request favicon. Browsers do.

You just ignore /favicon.ico by looking at the path.

On Fri, Aug 19, 2016 at 2:45 PM ivo welch <ivo...@gmail.com> wrote:

the answer is that plackup requests not only the file, but also favicon.ico.  thus, it is called twice for each request, and the counter increases by 2.

ivo welch

unread,
Aug 19, 2016, 5:59:28 PM8/19/16
to psgi-plack

of course.  mea culpa.  the extra chrome browser request solved my mystery of the counter increasing by 2 vs 1.  I now just trap favicon.ico in the builder, per example.


ivo welch

unread,
Sep 5, 2016, 6:58:21 PM9/5/16
to psgi-plack

andy colson's skeleton starter uses Plack::Builder; Plack::Request; Plack::App::File; Plack::Session::Store::File;  reading the Plack::Request docs now, Plack::Request is for middleware and not app developers.  the docs state that HTTP::Engine is the preferred method.  could someone please post an example skeleton that is recommended for a starter web app with the builder and the engine?  maybe a few web and plack/psgi key features---routing, session storage, header passing, get/post processing, file uploads.  I could put an example together, but it would almost surely make the experts cringe and only make it worse for future starters.

my impression, after getting pretty far in the last weeks, is that Plack/PSGI is the natural replacement for CGI.pm (which is deprecated for new projects).  it is higher-level, cleaner, and smarter than CGI.pm; but lower-level, more stable, and with less of a learning curve than mojo or dancer.  however, unlike the latter full fledged development apps, which commit one to deeper and ever-changing ecosystems, the plack/psgi examples and docs are sparse.  if anything, they should be easier and more stable.  for beginning web app programmers, it would be really helpful to have 20-50 short sample stand-alone web applications that do various things, for use in copy-paste construction.  plack/psgi is puzzlingly close and far...

Reply all
Reply to author
Forward
0 new messages