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

Hello

I came across some code and I am grateful please, for some explanation on what the code is doing (for want of a better phrase). I am not understanding how it 'works', but it does. It is a cgi script. Opinions on the quality of it is not as important to me as is the desire to understand the principle of it.

The original is here: http://www.onlamp.com/2008/02/19/library; some code extract is below.

My initial ignorant questions are I suppose:

Generally an overview of what is 'going on' would help me a great deal, as I am struggling (apologies) to understand it.

Thank you. Habs.

extract code:

.... use CGI '3.30', (); my $q = CGI->new; ..... sub GET($$) { my ($path, $code) = @_; return unless $q->request_method eq 'GET' or $q->request_method eq + 'HEAD'; return unless $q->path_info =~ $path; $code->(); exit; } sub POST($$) { my ($path, $code) = @_; return unless $q->request_method eq 'POST'; return unless $q->path_info =~ $path; $code->(); exit; } sub PUT($$) { my ($path, $code) = @_; return unless $q->request_method eq 'PUT'; return unless $q->path_info =~ $path; $code->(); exit; } sub DELETE($$) { my ($path, $code) = @_; return unless $q->request_method eq 'DELETE'; return unless $q->path_info =~ $path; $code->(); exit; } ..... eval { .... GET qr{^/=$} => sub { print $q->header('text/html'); print $q->h1('REST API Documentation'); print $q->p('Here is a list of what you can do:'); ...... }; .... GET qr{^/=/model/book/id/([\d-]+)$} => sub { my $id = $1; # Look up the resource file my $filename = get_local_path($id); if (-f $filename) { # Open and slurp up the file and output the resource ..... }; .... DELETE qr{^/=/model/book/id/([\d-]+)$} => sub { my $id = $1; # Make sure the book actually exists my $resource_path = get_local_path($id); unless (-f $resource_path) { barf 404, 'Where is What?', 'Nothing here to delete.'; } .... }; }; if ($@) { # Handle barfing if (ref $@ and reftype $@ eq 'HASH') { my $ERROR = $@; print $q->header( -status => $ERROR->{status}, -type => 'text/ +html' ); print $q->h1( $ERROR->{title} ); print $q->p( $ERROR->{message} ) if $ERROR->{message}; } # Handle anything else else { my $ERROR = $@; print $q->header( -status => 500, -type => 'text/html' ); print $q->title('Server Error'); print $q->p( $ERROR ); } exit; } # Nothing handles this, throw back a standard 404 print $q->header(-status => 404, -type => 'text/html'); print $q->h1('Resource Not Found');

Replies are listed 'Best First'.
Re: learning by example; please explain what this code is doing?
by Corion (Patriarch) on Mar 30, 2016 at 12:46 UTC

    You can reduce your script to a minimal example for learning:

    use strict; use CGI '3.30', (); my $q = CGI->new; sub GET($$) { my ($path, $code) = @_; return unless $q->request_method eq 'GET' or $q->request_method eq + 'HEAD'; return unless $q->path_info =~ $path; $code->(); exit; } eval { GET qr{^/=$} => sub { print $q->header('text/html'); print $q->h1('REST API Documentation'); }; GET qr{^/=/model/book/id/([\d-]+)$} => sub { my $id = $1; # Look up the resource file my $filename = get_local_path($id); if (-f $filename) { .. }; }; }; if( $@ ) { print "There was an error."; };

    Each call to GET gets passed a regular expression and a piece of code to call. The GET routine will then check whether the request matches that regular expression and if it does, call the callback to produce the result.

    All of this is not related to the CGI module at all, except for where the request comes from.

    To familiarize yourself with the code, I would start with (a copy of) the program and rip out as much of CGI as possible and run it from the command line. Add print statements to see where the code goes through and what it looks at.

      Hello Corion

      Thank you for that; very helpful and good point on slimming down the code.

      I am struggling to see what gets passed and from where, for those routines to get called; I will do as you say and get it to run from the command line and see if that helps me further appreciate.

      Regards

Re: learning by example; please explain what this code is doing?
by Your Mother (Archbishop) on Mar 30, 2016 at 12:39 UTC

    This is not good learning code and it is a lot of code—the whole script you showed the link for must be read to follow it—to review, especially since you aren't interested in a discussion of its quality. Understanding how it works might be good for you. Learning to emulate some of what it's doing would be bad for you.

    Part of what you're asking for now, though you don't know it, is a prototypes, security, and webframeworks lecture. The first of which is easy enough to provide: Far More than Everything You've Ever Wanted to Know about Prototypes in Perl -- by Tom Christiansen.

      Hello 'Your Mother'

      I didn't say I was not interested in the quality, only that it was not as important right now as is my interest in trying to understand what was going on.

      I find this perlmonks environment can be made quite difficult. As an outsider looking in (at various posts) some responses can be interpreted as condescending or high-hatted or just mainly interested in unwarranted criticism. Perhaps it is just not for me and I am too 'soft'.

      Thank you for the link, which I will surely go and read; some direct answers would have helped too (time and willingness permitting of course).

      I am sorry if what I posted has bothered you, or any one, for being long, incorrect or time consuming.

      Peace and best wishes.

        I completely understand that view. From my perspective I'm trying to prevent newcomers from picking up either time-wasting habits or dangerous ones. Which means, I'm trying to help you even if it doesn't quite feel that way.

        DO NOT PLACE THIS SERVICE ON AN OPEN WEB SERVER

        That caveat in the script should be read to mean: the following code is insecure but let's not worry about how. If you emulate it, you could destroy a business, expose yourself to multi-million dollar lawsuits, get yourself fired, or generally make co-workers not enjoy having you on the team.

        I am completely for test code and learning code and reinventing wheels for fun and all that. So I'm not trying to be a downer or criticize. I would be angry if I saw that code at work is all and I'd like to help new hackers avoid that direction as I have no idea how long my career will be. :P

        Corion got you a good response on your direct questions. So, not all monks are bad!

        As an outsider looking in (at various posts) some responses can be interpreted as condescending or high-hatted or just mainly interested in unwarranted criticism.

        I prefer to think of it as "tough love". ;)

        One thing I hope you won't see is criticism without suggestions also. Some are more polite than others, but I can say the intention almost always is to help you improve both by removing bad code/practices/ideas and learning good code/practices/ideas.

        But God demonstrates His own love toward us, in that while we were yet sinners, Christ died for us. Romans 5:8 (NASB)

Re: learning by example; please explain what this code is doing?
by tangent (Parson) on Mar 30, 2016 at 19:05 UTC
    This code is handling and routing CGI requests based on the path (the bit that comes after the script name in a URL). It seems well thought out and covers all possible error cases. However, it is written in a style which is alien to me, and I think most monks here would reccommend against following this style.

    Let's say we call the script like this:
    http://localhost/script.cgi/=
    The request method is GET, and the path is "/=". You get the path by calling CGI method $q->path_info

    The script will call a series of subroutines until it finds a match for the request method and path. In the eval{} block you see this series of calls. One of them is:
    GET qr{^/=$} => sub { print $q->header('text/html'); print $q->h1('REST API Documentation'); print $q->p('Here is a list of what you can do:');
    This could be written as:
    my $regex = qr{^/=$}; my $coderef = sub { print $q->header('text/html'); # etc. }; GET( $regex, $coderef );
    So it sends off the $regex and the $coderef to the GET() subroutine, which then checks if the request method is 'GET' and if the path matches the regular expression. In our case the path "/=" does match the regular expression {^/=$}, so it then calls the subroutine referenced in $coderef and then exits:
    $code->(); exit;
    If the regex didn't match it would go on to the next test in the series, which would try to match the path to the regex {^/=/model/book/id/([\d-]+)$}
    GET qr{^/=/model/book/id/([\d-]+)$} => sub { my $id = $1; # etc.
    If we were to call the script with this URL
    http://localhost/script.cgi/=/model/book/id/10566
    then this regex would match, the code ref will be called and $id will contain the value "10566". If it didn't match the script will continue to test all the possible GET, POST, PUT and DELETE methods in the eval block until it finds a match. If it doesn't find one it returns a 404 Not Found response.

      Hello Tangent

      Many thanks for that. I wish I'd checked back sooner to read your post. As it happens I have spent my evening working out to the same as what you have described (well nearly).

      When you say 'alien style', what do you mean and could you give me an example please (unless the 'could be written as' was an example) of a non-alien approach ?

      To someone who has not experienced Perl, some of the 'folded' code (or is that better worded as more concise, efficient, performant, proficient - I do not know) can make it difficult to read/understand. Therefore I like the alternative you presented in the 'could be written as' piece - that to me is much more presentable if nothing else.

      I have coded in the past (not in Perl), but not to any advanced levels, so is the approach of [crudely dropping out?] 'exit-ing' from a program in a sub routine acceptable ?

      Lastly, for what it is as such, are there 'better' coding approaches to achieve what is being done, or is it fairly decent technique (accepting there is no security etc)?

      Thank you for taking time to respond; thank you to all - I have moved on a little and the references given to me earlier are proving enlightening, if a little challenging also - particularly one that led to a piece that touched on lexical code referencing (lambda style) coupled with OO style in relation to anonymous subs! - well I think that was what it was about :-)

      Perl is *very* flexible! That would seem fair to say.