Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

cleaning up code with "do" ?

by schweini (Friar)
on Aug 18, 2004 at 21:50 UTC ( [id://384124] : perlquestion . print w/replies, xml ) Need Help??

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

hi, oh ye enlightened ones that absorb the light of knowledge,

i've got this big messy perlTk program that i did. I'm trying to clean it up just a little, just to make my head spin a little bit less.
i figured that i'd simply cut 'n paste the "core" functions into a seperate file, and include that file in the main program using "do".
of course i *should* use "use" or "require", but since i'm doing some quite messy namespace-stuff (plugins, global dbh's, eval-ed strings that call subs, etc.), that would be a major pain in the behind.
so, after doing all this, the main program doesn't quite work as expected - Tk's WaitVariable() and some other stuff "kind of" work, but overall, the program just doesn't function exactly the same way it used to.
now, in the docs for "do" it says that:
... It also differs in that code evaluated with "do FILENAME" cannot see lexicals in the enclosing scope ...

which seems to me, is the only major difference to a eval `cat`, but i don't quite understand what that means, or how it could affect monster-modules like Tk
soooooo: what is the best way to simply "include" a file, so that the including program exactly(!) functions as if as if the code contained in that file would be 'inline'?

- schweini

Update: i tried eval `type`, and it gives me the same problems as 'do'

e.g. i tie() a Tk::Text widget to a file-handle like this:
tie(*INVOICEVIEW, 'Tk::Text', $invoiceView);
and, after the "cut 'n paste", the line
print INVOICEVIEW translate("[ Factura vacia ]")."\n";

in the 'main' file gives me the error
Can't locate object method "INVOICEVIEW" via package "translate" (perhaps you forgot to load "translate"?) at C:\wwwroot\protopos\ line 773.

(although it worked as expected before the cut 'n paste. sigh.)
Update3: plesae note that i'm not simply trying to include a config-file or something. i have to include a whole lotta subs and all.

Edit by tye, replace PRE with CODE

Replies are listed 'Best First'.
Re: cleaning up code with "do" ?
by Zaxo (Archbishop) on Aug 18, 2004 at 22:03 UTC

    That says that subs in your file cannot participate in lexical closures with variables in the including file. Big packages often have such closures, and subs imported with do will fail. Make sure all subs get all their parameters from arguments passed to them.

    My own practice is to only do for subs like those and for a single configuration hashref as the return value for the file when done.

    my $config = do "$ENV{HOME}/.foobarc";

    After Compline,

Re: cleaning up code with "do" ?
by revdiablo (Prior) on Aug 18, 2004 at 22:41 UTC
    i *should* use "use" or "require", but since i'm doing some quite messy namespace-stuff (plugins, global dbh's, eval-ed strings that call subs, etc.), that would be a major pain in the behind.

    Moving this stuff into an external file will not make your code less messy. In fact, I think it will make it more so. Most of the advantage of modules is not having separate files, but having clean interfaces. I'd even venture to say the clean interface is much more important than the separate files.

    Here's what I recommend: clean up the interfaces between your core functions and main code, move the core functions into a package, and then think about making a module out of that package. It may seem like a daunting task, but just pick away at it, and, before you know, it will be tolerable and clean.

    If you have any questions about how to clean up something specific, you can always post a new question at the Monastery. If there's anything the Monks can do, it's rework a piece of code. :^)

Re: cleaning up code with "do" ?
by ysth (Canon) on Aug 18, 2004 at 22:24 UTC
    Sounds to me like the difference is that the do'd code hasn't been compiled yet when later parts of the "main" code are compiled, and perl makes different parsing choices because not all the same symbol table entries are defined.

    Try only cutting out subroutines or things you can reduce to subroutines. Then load them with BEGIN{require ""} and call the subroutines as needed.

    Alternatively, disambiguate all the places that you have problems; in the example case, try print INVOICEVIEW (translate(...)."\n").

Re: cleaning up code with "do" ?
by Plankton (Vicar) on Aug 18, 2004 at 22:13 UTC
Re: cleaning up code with "do" ?
by ikegami (Patriarch) on Aug 18, 2004 at 23:03 UTC

    Why do you think require won't work? The only catch is the "1;" needed at the end of the required file.

    --- File: --- use DBI; use MIME::Base64; BEGIN { require ''; } $VARA = 'a'; local $\ = "\n"; testb(); # Never imported. sub testa { print("and we're back"); print($VARB); # Never imported. print(catfile('1', '0')); # Never imported in this file. } --- File: --- use File::Spec::Functions; $VARB = 'b'; sub testb { print("magical newlines"); # $\ taking effect. print($DBI::VERSION); # Never used DBI in this file. print(encode_base64("Hello World")); # Never imported in this file. print($VARA); # Never imported. testa(); # Never imported. } 1; --- output --- magical newlines 1.28 SGVsbG8gV29ybGQ= a and we're back b 1/0
Re: cleaning up code with "do" ?
by schweini (Friar) on Aug 18, 2004 at 22:47 UTC
    replacing "do" or "eval `cat`" with "require" things got a lot better.
    but, for future reference: i think(!) the main problem is/was Tk's waitVariable. if i create the widget in one file, and watch a waitvariable with code from another file, i get to wait forever, it seems. the "waiter"-code simply doesnt get it.
    i'll try to create a simple test-case, and ask the Tk-guys about that....
      I hope you mean BEGIN {require ...}. Can you show a short but complete example that fails?