Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

I present to you... Horrible code!

by BUU (Prior)
on Jan 08, 2005 at 08:40 UTC ( #420485=perlmeditation: print w/ replies, xml ) Need Help??

I just thought I'd share this lovely gem I ran across:
sub termgrades { my $period = shift; my $quarter = shift; if ( $quarter == "1" ) { $sth = $dbh->prepare_cached('SELECT * FROM firstquarter WHERE +userid = ? AND period = ? AND gradetype = ?') || &error( "2", "$userdata[0] first quarter grades database +search failed: $DBI::errstr" ); } elsif ( $quarter == "2" ) { $sth = $dbh->prepare_cached('SELECT * FROM secondquarter WHERE + userid = ? AND period = ? AND gradetype = ?') || &error( "2", "$userdata[0] second quarter grades database + search failed: $DBI::errstr" ); } elsif ( $quarter == "3" ) { $sth = $dbh->prepare_cached('SELECT * FROM thirdquarter WHERE +userid = ? AND period = ? AND gradetype = ?') || &error( "2", "$userdata[0] third quarter grades database +search failed: $DBI::errstr" ); } elsif ( $quarter == "4" ) { $sth = $dbh->prepare_cached('SELECT * FROM fourthquarter WHERE + userid = ? AND period = ? AND gradetype = ?') || &error( "2", "$userdata[0] fourth quarter grades database + search failed: $DBI::errstr" ); } else { } $sth->execute( $userdata[0], $period, 2 ) || &error( "2", "$userdata[0] quarter grades database search fai +led: $DBI::errstr" ); }
I bet you could golf it down in to, oh, 200 characters?

Comment on I present to you... Horrible code!
Download Code
Re: I present to you... Horrible code!
by cchampion (Curate) on Jan 08, 2005 at 09:29 UTC

    Not golfing, because you won't get decent code from golfing horrible code.

    I'd rather see this code as a spot-the-mistakes execise.

    Thus, here come my changes:

    my @quarters = qw/none first second third fourth/; sub termgrades { my ($period, $quarter) = @_; $sth = $dbh->prepare_cached( qq{SELECT * FROM $quarters[$quarter]quarter WHERE userid = ? AND period = ? AND gradetype = ?} ) or &error( "2", "$userdata[0] $quarters[$quarter] quarter " . "grades database search failed: $DBI::errstr" ); $sth->execute( $userdata[0], $period, 2 ) or &error( "2", "$userdata[0] quarter grades " ." database search failed: $DBI::errstr" ); }

    update
    Changed the @quarters array to avoid a minor bug. Thanks to itub.

    However, the first blunder, the one that eventually led to the current horrible code, is that there are four database tables instead of one.

    Thus the first order of business would be to merge those four tables into one, adding a "quarter" column.

    With that in mind, you could rewrite the above function as follows:

    sub termgrades { my ($period, $quarter) = @_; $sth = $dbh->prepare_cached( qq{SELECT * FROM quarters WHERE userid = ? AND period = ? AND gradetype = ? and quarter = ?} ) or &error( "2", "$userdata[0] quarter $quarter grades " . "database search failed: $DBI::errstr" ); $sth->execute( $userdata[0], $period, 2, $quarter ) or &error( "2", "$userdata[0] quarter grades " ." database search failed: $DBI::errstr" ); return $sth; # perhaps ... see text }

    That said, this function, in its original form, is completely useless, because it is executing a SELECT request from a locally scoped statement handler, and its result would be simply lost. A returning statement for such a handler is needed, if you want to pursue any practical purpose.

    What else? Ah, yes, the global @userdata array, of which we have just to assume it exists and has valid data in it. It should be passed as an argument as well.

    FInally, that naked numeric literal "2" used as an argument to "execute" and "error" should be turned into a variable, so that whoever has to maintain the code knows what it is about.

      There is one minor error: your @quarters array is zero-based, but the quarter as passed to the method counts from 1 ($quarters[1] is "second").

      Should the "2" passed to "error" be a named constant instead of a magic number? Who knows, perhaps if this is an error level number a number is not that bad. If you turn the error levels into names, make sure that they are clear, unlike the US Homeland Security Advisory System (I always have trouble remembering whether "high" is higher than "elevated" or the other way around ;-) )

      That said, this function, in its original form, is completely useless, because it is executing a SELECT request from a locally scoped statement handler, and its result would be simply lost.

      Oh, there's more fun than that, my friend! There are some databases where you can set up a view in such a way that a SELECT against it actually makes changes to stuff. In Oracle 9, you can actually have it do literally anything, including executing Perl code.

      Being right, does not endow the right to be rude; politeness costs nothing.
      Being unknowing, is not the same as being stupid.
      Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
      Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

        That's some messy programming-by-side-effect there. Sure, calling an external database is already a side-effect, but it's the sort you pretty much have to live with. If the posted code is using such a view, I wouldn't consider it an improvement.

        "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

Re: I present to you... Horrible code!
by Errto (Vicar) on Jan 08, 2005 at 17:48 UTC

    I read the first line of cchampion's reply but not the rest, so I thought I'd take a stab myself.

    Like the OP, I presume the variable $dbh, the array @userdata and the subroutine error() to be predefined. However, it appears that the OP is using this subroutine to set a global variable called $sth from which results are later retrieved. I find this upsetting so I'm going to have the subroutine termgrades() return it instead, and return undef in case of errors. I also changed the messages so that an prepare error would not return the same message as an execute error.

    my %quarternames = (1 => 'first', 2 => 'second', 3 => 'third', 4 => ' +fourth'); my %tablenames = map { $_ => "$quarternames{$_}quarter" } keys %quarternames; my %queries = map { $_ => " SELECT * FROM $tablenames{$_} WHERE userid = ? AND period = ? AND gradetype = +? " } keys %tablenames; sub termgrades { my $period = shift; my $quarter = shift; my $sth; return undef unless exists $queries{$quarter}; $sth = $dbh->prepare_cached($queries{$quarter}) or do { error ("2", "Could not prepare query for $quarterna +mes{$quarter} quarter for $userdata[0]: $dbh->errstr"); return undef; }; $sth->execute($userdata[0], $period, 2) or error("2", "Could not execute query for $quarternames{$qua +rter} quarter for $userdata[0]: $sth->errstr"); return $sth; }

    Update: Made hash initialization slightly more clever.

www.thedailywtf.com
by Anonymous Monk on Jan 09, 2005 at 16:10 UTC
    You should post this (and other code samples like it) on The Daily WTF. It's a pretty fun site, even if most of the time it's not perl.
      WTF? There's this .NET stack trace staring back at me. Only slightly ironic :)
      Server Error in '/' Application. Timeout expired. The timeout period elapsed prior to completion of the + operation or the server is not responding. General network error. Check your net +work documentation.
      It's like those recurring Java stack traces over at Artima

      /J

Re: I present to you... Horrible code!
by bradcathey (Prior) on Jan 09, 2005 at 18:04 UTC

    While I get a sick kind of pleasure ripping on someone else's work, I have to admit that I used to write stuff like that. In fact, I still might. But I wouldn't know it unless I published it at a place like the monastery and had the good Monks critique it--which has happened many times, which is why I'm a better programmer than I was 3000 XP ago. It's how I learned about taint, and strict, and references, and placeholders.

    I'm sure this has come up around here before, but I wish there was an designated "safe" area here at PM's to post code and have it critiqued. That's how ya learn. It would be invaluable to me who is passionate about doing it correctly, but not always understanding how.

    And it would be faster. I remember asking a teacher back in college why I needed a teacher. He admitted I could probably learn it myself, but it would take 10 times longer.

    Would anyone else benefit from this?


    —Brad
    "Don't ever take a fence down until you know the reason it was put up." G. K. Chesterton

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://420485]
Approved by AcidHawk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (9)
As of 2014-12-22 07:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (112 votes), past polls