Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Re^2: require() @INC hooks problem

by kcott (Archbishop)
on Dec 28, 2020 at 10:22 UTC ( [id://11125857]=note: print w/replies, xml ) Need Help??


in reply to Re: require() @INC hooks problem
in thread require() @INC hooks problem

"I understand now what's happening, ..."

That's great. In "Re^6: require() @INC hooks problem [non-Moose]", I wrote: "The mechanism behind this is a mystery to me. Why is it calling the subroutine with all the previous CODEREF values then the newly created CODEREF?" Perhaps you would share your understanding.

"... but I have trouble getting what your intention is... (???)"

The subclasses, in this instance, are to be used for exception objects. There are multiple ways to handle this; this seemed like a handy way to do it. It follows the DRY principle and abstracts any required code to a single statement.

"Why are you installing multiple hooks?"

That's not actually a requirement at the present; although, I can see that it might be useful as code development progresses. The problem (1st hook working; 2nd hook not working) came up while testing. I don't like loose ends, or sweeping problems under the carpet, so I followed up with an investigation into this.

"Why do you even need a hook if your require happens right away?"

Technically, it's not a requirement; it's just, as I said above, "a handy way to do it". I don't need to write individual X/Y/Z.pm modules for require X::Y::Z statements.

"I'm puzzled ..."

Hopefully, less so now. :-)

— Ken

Replies are listed 'Best First'.
Re^3: require() @INC hooks problem
by LanX (Saint) on Dec 28, 2020 at 13:14 UTC
    First things first

    > > "Why do you even need a hook if your require happens right away?"

    a require is basically a

    • load file into string
    • eval string
    • update %INC to avoid further loads
    instead of a hook you can simply eval your template and update %INC.

    > I don't need to write individual X/Y/Z.pm modules for require X::Y::Z statements.

    you don't need to save them to a file.

    Just eval the code and update %INC

    > > "Why are you installing multiple hooks?"

    A hook is an abstraction of a directory path in @INC, which are searched sequentially.

    Either a path includes a requested module (SUCCESS) or the next is searched (FAIL)

    But your hook in the OP always returned the same code of the first module, no matter which module was requested. It should have signaled a FAIL, to allow the next path/hook to handle the request.

    And each of your hooks is only returning one file, which is an overkill and "littering" @INC with hooks.

    > Perhaps you would share your understanding.

    If you want you can look into the working example I wrote for HaukeX' webperl project:

    [WEBPERL] dynamically importing non-bundled modules via http

    you just have to adapt the coderef $fetch and %INC_FETCH to your needs.

    BUT as I said, you don't even need hooks if you eval those modules right away.

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

      I got a few ideas from the code to which you linked. Thanks for that. Here's a modified MooselessRequireHookTest.pm:

      package MooselessRequireHookTest; use 5.032; use warnings; sub new { my ($class) = @_; return bless {} => $class; } sub dynamic_require { my ($self, $ns_extension) = @_; state $hook_source = {}; state $inc_updated = 0; my $class = join '::', __PACKAGE__, $ns_extension; my $filename_key = $class =~ s{::}{/}gr . '.pm'; unless (exists $hook_source->{$filename_key}) { my $source = <<~EOF; package $class; use parent 'MooselessRequireHookTest'; 1; EOF $hook_source->{$filename_key} = \$source; } state sub inc_hook { my ($coderef, $filename) = @_; warn "\$coderef[$coderef] \$filename[$filename]\n"; return $hook_source->{$filename}; }; unless ($inc_updated) { push @INC, \&inc_hook; $inc_updated = 1; } eval "require $class;"; return; } 1;

      I added this to the end of sscce_mooseless_require_hook_test.t:

      warn '-' x 10, ' @INC ', '-' x 10, "\n"; warn "$_\n" for @INC; warn '-' x 26, "\n";

      Here's a new, sample prove run:

      $ prove -v sscce_mooseless_require_hook_test.t sscce_mooseless_require_hook_test.t .. 1..8 ok 1 - Test MooselessRequireHookTest::->new() ok 2 - 'Test MooselessRequireHookTest::->new() ISA' isa 'MooselessRequ +ireHookTest' ok 3 - Test MooselessRequireHookTest::Test1->new() ok 4 - 'Test MooselessRequireHookTest::Test1->new() ISA' isa 'Mooseles +sRequireHookTest::Test1' ok 5 - Test MooselessRequireHookTest::Test2->new() ok 6 - 'Test MooselessRequireHookTest::Test2->new() ISA' isa 'Mooseles +sRequireHookTest::Test2' ok 7 - Test MooselessRequireHookTest::A::B::C->new() ok 8 - 'Test MooselessRequireHookTest::A::B::C->new() ISA' isa 'Moosel +essRequireHookTest::A::B::C' $coderef[CODE(0x600867f00)] $filename[MooselessRequireHookTest/Test1.p +m] $coderef[CODE(0x600867f00)] $filename[MooselessRequireHookTest/Test2.p +m] $coderef[CODE(0x600867f00)] $filename[MooselessRequireHookTest/A/B/C.p +m] ---------- @INC ---------- /home/ken/tmp/pm_sscce_require_hook/lib /home/ken/perl5/perlbrew/perls/perl-5.32.0/lib/site_perl/5.32.0/cygwin +-thread-multi /home/ken/perl5/perlbrew/perls/perl-5.32.0/lib/site_perl/5.32.0 /home/ken/perl5/perlbrew/perls/perl-5.32.0/lib/5.32.0/cygwin-thread-mu +lti /home/ken/perl5/perlbrew/perls/perl-5.32.0/lib/5.32.0 CODE(0x600867f00) -------------------------- ok All tests successful. Files=1, Tests=8, 0 wallclock secs ( 0.02 usr 0.01 sys + 0.08 cusr + 0.05 csys = 0.15 CPU) Result: PASS

      So, the CODEREF is only run once for each invocation and @INC only has one hook. Compare that output with what I previously showed in "Re^6: require() @INC hooks problem [non-Moose]".

      I put the changes (not the warn statements) into the original module: all (98) tests passed without any problems.

      With regard to your suggestion of "Just eval the code and update %INC", I'm not following you in terms of how that might be implemented. Could you provide a code example?

      I notice you're referencing the OP code. Please see the sub-thread starting at "Re^4: require() @INC hooks problem [non-Moose]". Moose was never an issue here but, given your request, and stated aversion to Moose, that provides a non-Moose version of the SSCCE code.

      I was hoping for an answer to "Why is it calling the subroutine with all the previous CODEREF values then the newly created CODEREF?" (see "Re^2: require() @INC hooks problem").

      — Ken

        > "Why is it calling the subroutine with all the previous CODEREF values then the newly created CODEREF?"

        I don't know what that means.

        > With regard to your suggestion of "Just eval the code and update %INC", I'm not following you in terms of how that might be implemented. Could you provide a code example?

        something like (totally untested)

        sub create_class { my ($class) = @_; my $source = <<~EOF; package $class; use parent 'MooselessRequireHookTest'; 1; EOF eval $source; my $name = $class =~ s(::)(\/)gr; $INC{"$name.pm"} = 'imported via eval'; }

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

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others avoiding work at the Monastery: (4)
As of 2024-04-25 13:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found