Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Is this a good approach for reducing number of global variables?

by ybiC (Prior)
on Mar 19, 2002 at 02:54 UTC ( [id://152637]=perlquestion: print w/replies, xml ) Need Help??

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

I recently postulated in the CB that some perl scripts might be trivial enough for one not to be concerned about presence of global variables.   After being soundly pelted with a barrage of rotton fruit, I took the advice of the wise (sist|breth)ren to reduce the number of global vars from 23 to 2 in the following scriptlet.

So my question is: are my efforts below at reducing globals via blocks, subroutines, and return values properly idiomatic, or profoundly idiotic?

    cheers,
    Don
    striving toward Perl Adept
    (it's pronounced "why-bick")



#! /usr/bin/perl -w ## dhlogs.pl ## pod at tail use strict; use Date::Calc qw(Today Add_Delta_Days); use LWP::UserAgent; print "\n Fetching log...\n\n"; my $yesterday = Yesterday(); my @msg = Main( 'joe@modem.com', # remote domain 'yosef', # remote id 'secret', # remote password "access.log.$yesterday", # remote logfile input 'c:/logs', # local dir to write to 'access.log', # local logfile output ); print "$_\n" for(@msg); sub Main { my ($dom, $id, $pw, $logweb, $logdir, $loglocal) = @_; die $! unless (-d $logdir && -w _); my $proxy = 'http://host.dom:port'; my $proxyuse = 1; my $url = "ftp://$id:$pw\@$dom/logs/"; my $ua = new LWP::UserAgent; $ua->proxy(['http', 'ftp'] => $proxy) if ($proxyuse == 1); $url = new URI::URL("$url/$logweb"); my $req = new HTTP::Request "GET" => ("$url"); my $res = $ua->request($req); if ($res->is_success) { my $rescont = $res->content; open OUT, "> $logdir/$loglocal" or die $!; print OUT $rescont or die $!; close OUT or die $!; my @return = ( " Program run successful\n", " Website: $dom", " Web log: $logweb", " Local dir: $logdir", " Local log: $loglocal\n", ); } else { my $resmsg = $res->message; } } sub Yesterday { my $Dd = -1; my($ty, $tm, $td) = Today(); my($yy, $ym, $yd) = Add_Delta_Days($ty, $tm, $td, $Dd); my $yesterday = sprintf "%04d-%02d-%02d", $yy, $ym, $yd; } END { print " Date::Calc $Date::Calc::VERSION\n", " LWP::UserAgent $LWP::UserAgent::VERSION\n", " Perl $]\n", " OS $^O\n", "\n"; my $stayopen = 0; # 0 is no, 1 is yes my $closedelay = 5; # seconds to wait before closing window if ($stayopen == 1) { print "\n Hit <enter> to exit\n\n"; <STDIN>; } else { print "\n This window will close in $closedelay seconds...\n"; sleep $closedelay; } } =head1 NAME dhlogs.pl =head1 DESCRIPTION Automate fetch of yesterday's DreamHost web logfile =head1 SYNOPSIS dhlogs.pl<enter> No arguments or options - all params set in code =head1 TESTED ActivePerl 5.6.1-631 Windows 5.0.2195 sp2 (Win2k pro) Date::Calc 5.0 LWP::UserAgent 1.77 =head1 UPDATE 2002-03-18 20:05 CS Check for write perms in logdir sub Main{ ... } Test with ActivePerl launching from icon and from command-line 'print' instead of 'warn' in END{...} 2002-03-16 21:50 CST Test on Win2k against Inet-accessed FTP+Apache access log Test on Win2k against local Debian FTP+Apache access log Debug LWP::UserAgent FTP w/proxy Leading-zero pad Yesterday() return value via sprintf Reduce number of global vars {...} blocks subamify find yesterday's date, return @yesterday END{...} instead of &e2e() Variableize dom, id, pw Date::Calc snippet for yesterday's date Pause after (success|failure) 2002-03-13 Initial working code =head1 TODO Runlog along with print to console Test on Cygwin against Inet-accessed FTP+Apache access log Test on Debian against Inet-accessed FTP+Apache access log Test on big brudder's Win98 box =head1 AUTHOR ybiC =cut

Replies are listed 'Best First'.
(jeffa) Re: Is this a good approach for reducing number of global variables?
by jeffa (Bishop) on Mar 19, 2002 at 04:24 UTC
    I will agree with the assessment that some Perl scripts are indeed trivial enough to not be concerned with globals - but not 23 globals! ;)

    I think that the stucture of this script is excellent! Adding command line argument support should be a breeze now (hint! hint!). However, i would change the name of the subroutine Main to something more descriptive like fetch_message or just fetch. (and lower case is nicer!)

    Off topic, i advise against re-using variables like you do with this snippet:

    my $url = "ftp://$id:$pw\@$dom/logs/"; $url = new URI::URL("$url/$logweb");
    Taken out of context like that, you hopefully see that this really is not good. It was a string, now it's an object - this is the kind of practice that results in hard to track bugs. Better to just be safe and decouple:
    my $site = "ftp://$id:$pw\@$dom/logs"; my $url = new URI::URL("$site/$logweb");
    Also (and i say this because i have been corrected on this as well), if a subroutine returns a value, return the value:
    sub Yesterday { # sidetrack: following golf just a thought ... my($yy, $ym, $yd) = Add_Delta_Days(Today(), -1); return sprintf "%04d-%02d-%02d", $yy, $ym, $yd; }
    And this doesn't just go for this subroutine, adding returns to your Main function is highly recommended. Sure Perl evaluates the last expression and returns it, but saying that with an explicit return says a lot more to others trying to understand the script (especially if they are Perl newbies coming from another language).

    Other than those nit-picks, i say nice work ybiC...

    UPDATE
    ...but also heed chromatic's wisdom!

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: Is this a good approach for reducing number of global variables?
by chromatic (Archbishop) on Mar 19, 2002 at 04:32 UTC
    For something this small, I wouldn't worry. Besides that, you're only using lexical variables anyway.

    The reason to avoid globals is to prevent action at a distance. You have two control branches and a handful of error checks. There's no reason to have subroutines, either. Let glue code be glue code.

    I'm a big believer in putting off unnecessary complexity. Your code as it is is simpler without these hoops. If you decide to turn it into a module someday, you won't have too much work to do to generalize it. (It looks awfully specific for that, though.)

Re: Is this a good approach for reducing number of global variables?
by Zaxo (Archbishop) on Mar 19, 2002 at 05:40 UTC

    Very nice, you've got them all scoped as lexicals. None of the following is criticism, you've chosen a style which is very good for maintainance.

    There are a few things you could do to limit scopes further. The simplest and least obtrusive would be a bare block around my $yesterday' and my @msg; like this:

    use LWP::UserAgent; { print "\n Fetching log...\n\n"; my $yesterday = Yesterday(); my @msg = Main( 'joe@modem.com', # remote domain 'yosef', # remote id 'secret', # remote password "access.log.$yesterday", # remote logfile input 'c:/logs', # local dir to write to 'access.log', # local logfile output ); print "$_\n" for(@msg); }
    I included the initial print statement in the block, just in case you want to localize $\ and kin.

    My own inclination is to do away with named variables as much as is consistent with maintainability and readability, so I might have written that as:

    { local $\="\n"; print "\n Fetching log...\n"; print for Main( 'joe@modem.com', # remote domain 'yosef', # remote id 'secret', # remote password "access.log.".Yesterday(), # remote logfile input 'c:/logs', # local dir to write to 'access.log', # local logfile output ); }
    but that is less useful for debugging. Its readability is a matter of what one's used to. Similarly, I might write Yesterday() as:
    sub Yesterday { sprintf "%04d-%02d-%02d", Add_Delta_Days(Today(),-1); }
    If you want to really gild the lily, you could define a private namespace for your sub names: package myscript; sub Main{} sub Yesterday {} package main;. That would be really fussy though ;-)

    After Compline,
    Zaxo

Re: Is this a good approach for reducing number of global variables?
by Desdinova (Friar) on Mar 19, 2002 at 06:21 UTC
    Just on the idea that some scripts are too trivial to worry about globals.. I have had several 'trivial' scripts grow well beyond thier orignal intent. In those cases having the proper structure has been very helpful as I add more and more features to it.

    Just my 1/50th of a dollar
      "I've had several 'trivial' scripts grow well beyond thier original intent."

      I continually find myself fending off the implicit patina of triviality assigned to Perl by users, evoked by the word 'script' (further bolstered by the impression, however accurate, that Perl is to UNIX what Visual Basic (et al) is to Windows).

      Nearly all the so-called 'one-off's I've ever written have somehow wound up either running in Production, or at least used on a regular basis ('soft' production, as it were). It has reached the point where I no longer use the word 'script' to refer to them when speaking to users, instead using the more substantive, 'program.'

      Toward that end I try ALWAYS to write my scripts/programs, however trivial, as though they are full-fledged applications (or soon will be). Which is to say I eschew globals, at the expense of passing values to subroutines more than perhaps efficiency would dictate.

      dmm

      If you GIVE a man a fish you feed him for a day
      But,
      TEACH him to fish and you feed him for a lifetime
      There are very few reasons to not do things the right way. If it's no skin off your back, then don't cut corners. "Shortcuts make for long journeys."

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

Re: Is this a good approach for reducing number of global variables?
by erikharrison (Deacon) on Mar 19, 2002 at 16:49 UTC

    I think that the use of globals increases as the program increases in size and complexity - it becomes simpler and sometimes clearer to pass half a dozen variables througha global hash than directly to the subroutine.

    Perl gives us alot of power for quick hacks, simple tools, fast fixes. For example, returning the last thing evaluated, providing a default variable ( $_ ) for just about everything means I can construct, say, a script which changes every url in a couple of HTML files, in about five minutes. But most of those features are inappropriate for a program of any size where your spending days working on it anyway, the twenty minute time gain of using them isn't worth in terms of a lost of readability and reuse.

    Cheers,
    Erik

        I think that the use of globals increases as the program increases in size and complexity - it becomes simpler and sometimes clearer to pass half a dozen variables through a global hash than directly to the subroutine.

      GYAH! No, don't do that. It'll be clearer for the first week or so, then you'll need extra comments to figure out where that hash is coming from. Then you'll make a few changes, and forget to update the comments. (Trust me, you will forget.) Then you'll want to process many widgets instead of just one, so your hash will become an array of hashrefs, and you're totally screwed.

      Globals are good for things like program options (i.e. don't pass around the args hash you got back from Getopt). Globals are good for collecting the data you're operating on: if you're modelling TCP connections, then a global array of connection objects is perfectly reasonable. Globals are sometimes reasonable for the outermost scope of your problem space. If you're writing a state machine, for instance, and you know somehow that you'll never want to model more than one state machine, then it's sometimes okay to put your state in the global namespace rather than packaging it up into a class (or even a hash) and accessing it opaquely.

      Most of the time, though, the little time you save by making stuff global rather than packaging it up just a little bit is completely overwhelmed by the time it takes you to refactor your code when your problem expands, even if you're just prototyping something. Slinging around globals is false laziness.

      (Anyone else want the soapbox? :-)

      --
      :wq

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://152637]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others wandering the Monastery: (4)
As of 2024-04-20 02:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found