Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Accessing the __DATA__ blocks of other packages

by caedes (Pilgrim)
on Dec 11, 2002 at 21:18 UTC ( #219176=perlquestion: print w/ replies, xml ) Need Help??
caedes has asked for the wisdom of the Perl Monks concerning the following question:

I have a subroutine that is called from many other packages. I need this subroutine to read the contents of __DATA__ from the package that called it and then do something with it (__DATA__ will contain a default template for html output). I have tried something like this:
package TMPL; sub fodus { my $caller = (caller(0))[0]; my $out = join('',<{$caller .'::DATA'}>); # do stuff to $out return $out; }
but that didn't work. I even tried this just to see if it would work if I hard-coded in the caller's package name:
package TMPL; sub fodus { my $out = join('',<Caller::DATA>); # do stuff to $out return $out; }
But it still complained that Caller::DATA was only used once. In both of these examples I'm using strict with warnings. So how exactly can I access the contents of __DATA__ in a different package?

-caedes

Comment on Accessing the __DATA__ blocks of other packages
Select or Download Code
Re: Accessing the __DATA__ blocks of other packages
by Ovid (Cardinal) on Dec 11, 2002 at 21:58 UTC

    I would recommend moving __DATA__ into a proper file. Otherwise, you can use a typeglob in your file with __DATA__ to export the data filehandle. You could also read the data from the handle, cache it, and return it to whomever wants it. Lastly, here's the answer to your question.

    package Foo; 1; __DATA__ line 1 line 2

    Save the above as Foo.pm and use the following test program:

    #!/usr/bin/perl -w use strict; use Foo; print while (<Foo::DATA>);

    I did not get any warnings running this, but if you do, you can always turn off warnings locally.

    { local $^W; print while (<Foo::Data>); }

    Or better yet, if you use the warnings pragma:

    { now warnings 'once'; print while (<Foo::Data>); }

    This is much better as it will only turn off the one warning, but leave others enabled.

    Cheers,
    Ovid

    New address of my CGI Course.
    Silence is Evil (feel free to copy and distribute widely - note copyright text)

      While I don't doubt that your code works, I think you might have misunderstood me. To make your example like my problem, the package Foo would have a subroutine with the while(<main::DATA>), and the main script would have the __DATA__ token. This is really too simplified though because Foo doesn't know what it's caller is ahead of time (it's not always main).

      -caedes

Re: Accessing the __DATA__ blocks of other packages
by pfaut (Priest) on Dec 11, 2002 at 22:03 UTC

    Why not have the calling code pass a reference to its DATA handle instead?

    #Used.pm package Used; use strict; 1; sub called { my $fh = shift; my $data = <$fh>; print "Used::called read: $data\n"; } #User.pl #!/usr/bin/perl -w use strict; use Used; Used::called(\*DATA); __DATA__ This is a test

    BTW, is there a better way to pick up the DATA handle reference?

      I originally had the calling code slurp the __DATA__ into a scalar and then pass that to the other package's subroutine, however I don't want to have to do this for every package that wants to call the package that uses __DATA__.

      -caedes

        I would say that what you are doing is probably not a very good software design. If you need something from your caller, it's probably a better idea to have the caller pass it as an argument. That way, it's documented that the called routine is using it. Using backdoor methods such as this to get information from the caller leads to unmaintainable code. Make sure you document this behavior so that anyone that uses this code in the future knows their DATA handle is going to be used when they call this method.

        With that said, if you really need to do it without modifying the calling code, try this.

        sub called { my $caller = (caller)[0]; my $fh = eval '\*'.$caller.'::DATA'; my $data = <$fh>; print "read: $data\n"; }
Re: Accessing the __DATA__ blocks of other packages
by Aristotle (Chancellor) on Dec 14, 2002 at 23:53 UTC
    No need for eval or other heavy artillery.
    package TMPL; sub fodus { local $/; my $out = <($main::{+(caller)[0]."::"}->{DATA})>; # ... return $out; }
    The key here is that Perl makes the symbol table of each package available as a hash called %%packagename:: - with %main:: containing keys for all packages including itself, the value being a glob pointing in turn to that package's symbol table. So I use %main:: to get the glob to the caller's package, then dereference it as a hash, fishing the DATA key from it in turn.

    Makeshifts last the longest.

      Sneaky. (I've stashed it away for possible future use.)

      --- print map { my ($m)=1<<hex($_)&11?' ':''; $m.=substr('AHJPacehklnorstu',hex($_),1) } split //,'2fde0abe76c36c914586c';
Re: Accessing the __DATA__ blocks of other packages
by archangelq (Novice) on Aug 02, 2004 at 20:18 UTC
    I belive the solution to your problem is much simpler than implied in most of these posts. They all point out ways to accomplish what your code is trying to do, but they seem to miss what you are trying to do with your code. My solution? Read __DATA__ into a package variable, and have your subs act on it, and not try to read it inside of the sub itself. Package:
    package Foo; use strict; my $data; { local $/; $data = <DATA>; } sub foo{ return $data; } __DATA__ Foo Bar Baz
    Calling Code:
    use Foo; print $Foo::foo;
    The above prints:
    Foo
    Bar
    Baz
    

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (16)
As of 2014-08-01 14:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Who would be the most fun to work for?















    Results (25 votes), past polls