in reply to Re^2: Multiple Packages in a Module? (%INC)
in thread Multiple Packages in a Module?

I have problems to see a case where my approach doesn't work.°

A use is a require + import and the require part is skipped after checking %INC

(Of course you need to place Foo::Bar before Foo if the latter uses the former.)

But if you have a special example in mind where this breaks, please share.

update

> if a use Foo::Bar ; is executed after the usurping module test_v1.pm has been loaded

as demonstrated you can use Foo::Bar as soon as the BEGIN block sets %INC.

Cheers Rolf
(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery

update

°) well if the module is written in a way that BEGIN blocks expect to be run after the use (i.e. in the inherent do FILE), then yes could get bitten by order problems. The OP didn't sound like his intention was to simulate meta programming magics.

Otherwise he should do a BEGIN { eval $MODULE_CODE; package->import() }

Please note that this code could be encapsulated in a special module.

Something like use FromData "__MODULE_NAME__" could extract the Modules_Code from the DATA section (delimited by __MODULE_NAME__ ) and import it into the upper namespace.

... or ...

Another - probably more elegant - option is to provide a loader function as value in %INC . use would evaluate this function to get the MODULE_CODE.

You can also insert hooks into the import facility by putting Perl code directly into the @INC array. There are three forms of hooks: subroutine references, array references, and blessed objects.

Replies are listed 'Best First'.
Re^4: Multiple Packages in a Module? (%INC)
by AnomalousMonk (Archbishop) on Jun 25, 2018 at 22:13 UTC
    I have problems to see a case where my approach doesn't work.

    I can't see a case where it wouldn't "work", but I also can't see any typical case in which it would do anything useful.

    Assuming an unambiguous file system, if you have a xyzzy.pm file defining a package Foo and its structure/content and a bunch of other namespaces and their contents, the only way to establish the Foo namespace (and all the others) is by a  use xyzzy; statement. Once the  use xyzzy; statement is executed, all the namespaces are established, and xyzzy cannot be use-ed again with any effect because it is present in the  %INC structure. If one then wants to access one of these namespaces, a  packagenamespace; statement will do the trick.

    OTOH, if you have a Perl inclusion directory structure with a slew of Foo.pm files in it, any  use Foo; statement is an open invitation to a major brain tumor. Surely, the best approach is to have a clean, unambiguous inclusion path to begin with. If you have some weird monkey patching/phasing/path resolution/??? problem such as haukex alludes to here, patching  %INC in this way may be your only salvation, but again, isn't studious avoidance rather than acceptance the best way to deal with such an unusual (one would hope) situation?


    Give a man a fish:  <%-{-{-{-<

      > but I also can't see any typical case in which it would do anything useful.

      PAR::Packer and relatives?

      update

      or let's assume a situation where you have to transport/pipe multi-module Perl code to a running script.

      The OP was talking about testing, we can't know what exactly he's trying to test.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

Re^4: Multiple Packages in a Module? (%INC)
by haj (Vicar) on Jun 25, 2018 at 23:08 UTC
    Lanx: I have problems to see a case where my approach doesn't work.
    I have been referring to your initial sentence (emphasis added):
    LanX: After useing test_v1.pm you have to make Perl believe that package Foo and package Foo::Bar have already been loaded ...

    Your approach works in most cases, but you don't have to modify %INC in the cases I've shown. Can I whip up an example where it actually breaks? Probably. I've done some security testing in my career, and to do that you often need to find an economic way to simulate an error situation which "does not happen normally" and check how an application behaves. It isn't really short, but hopefully self-contained and correct enough. Let's consider this application App.pm which needs some critical resource, which in our example is the Perlmonks website:

    # App.pm package App; use Foo; sub new { my $class = shift; bless { ua => Foo->new }, $class; } sub check { my $self = shift; my $res = $self->{ua}->get('https://perlmonks.org'); if ($res->code != 200) { return "Monks are offline"; } return "Everything works just fine"; } 1;
    The Foo module is pretty short.
    # Foo.pm package Foo; use parent 'LWP::UserAgent'; 1;

    Now how do we get test coverage in the branch where it deals with "Monks are offline"? I can't shut down the Perlmonks server, and let's for the moment assume that I can't just manipulate the network, because other parts of the application need their connections. But I can make sure that Foo will behave as if Perlmonks were down. I'll do this with a test class test_v1:

    # test_v1.pm package Foo; use HTTP::Response; sub get { return HTTP::Response->new(500); } # Add more packages ad libidum 1;
    A testfile would look like this:
    # errorcode.t use test_v1; # %INC modification, as prescribed by LanX #BEGIN { # $INC{'Foo.pm'} = __FILE__; #} use Test::More; use App; my $app = App->new; is($app->check,"Monks are offline"); done_testing;

    If you comment out the behaviour modification, then the test will fail (unless Perlmonks are really down). If you uncomment the %INC modification, then the test will fail with Can't locate object method "new" via package "Foo" at App.pm line 7.

    I usually avoid such a construct and instead explicitly use Foo; before doing any behaviour modification. In that case, the %INC modification would be harmless, but still useless and by no means required.

    Cheers,
    haj

    P.S.: Good that I'm typing so slow. You're editing your posts so often that I have to occasionally check what I'm actually responding to.