NAME
    Require::HookChain - Chainable require hooks

VERSION
    This document describes version 0.016 of Require::HookChain (from Perl
    distribution Require-HookChain), released on 2023-12-05.

SYNOPSIS
    NOTE: Please see Require::HookPlugin instead which will supersede this
    project.

    Say you want to create a require hook to prepend some code to the module
    source code that is loaded. In your hook source, in
    Require/HookChain/munge/prepend.pm:

     package Require::HookChain::munge::prepend;

     sub new {

         # our hook accepts one argument: preamble (the string to be prepended)
         my ($class, $preamble) = @_;

         bless { preamble => $preamble }, $class;
     }

     sub Require::HookChain::munge::prepend::INC {

         # instead a filename like a reguler hook, an hook's INC is called by
         # Require::HookChain's main INC and will be passed $r stash
         my ($self, $r) = @_;

         # safety, in case we are not called by Require::HookChain
         return () unless ref $r;

         my $src = $r->src;

         # we only munge source code, when source code has not been loaded by other
         # hooks, we decline.
         return unless defined $src;

         $src = "$self->{preamble};\n$src";
         $r->src($src);
     }

     1;

    In a code to use this hook:

     use Require::HookChain -end=>1, 'munge::prepend' => 'use strict';
     use Foo::Bar; # Foo/Bar.pm will be loaded with added 'use strict;' at the start

    Install other hooks, but put it at the end of @INC instead of at the
    beginning:

     use Require::HookChain -end=>1, 'munge::append' => 'some code';
     use Require::HookChain 'log::stderr'; # log each loading of module to stderr

DESCRIPTION
    This module lets you create chainable require hooks. As one already
    understands, Perl lets you put a coderef or object in @INC. In the case
    of object, its "INC" method will be called by Perl:

     package My::INCHandler;
     sub new { ... }
     sub My::INCHandler::INC {
         my ($self, $filename) = @_;
         ...
     }

    The method is passed itself then filename (which is what is passed to
    require()) and is expected to return nothing or a list of up to four
    values: a scalar reference containing source code, filehandle, reference
    to subroutine, optional state for subroutine (more information can be
    read from the perlfunc manpage). As soon as the first hook in @INC
    returns non-empty value then the search for source code is stopped.

    With "Require::HookChain", you can put multiple hooks in @INC that all
    get executed. When "use"'d, "Require::HookChain" will install its own
    hook at the beginning of @INC which will search for source code in @INC
    as well as execute "INC" method of all the other hooks which are
    instances of "Require::HookChain::*" class. Instead of filename, the
    method is passed a "Require::HookChain::r" object ($r). The method can
    do things on $r, for example retrieve source code via "$r->src" or
    modify source code via "$r->src($new_content)". After the method
    returns, the next "Require::HookChain::*" hook is executed, and so on.
    The final source code will be retrieved from "$r->src" and returned for
    Perl.

    This lets one chainable hook munge the result of the previous chainable
    hook.

    To create your own chainable require hook, see example in "SYNOPSIS".
    First you create a module under the "Require::HookChain::*" namespace,
    then create a constructor as well as "INC" handler.

  Import options
    Options must be specified at the beginning, before specifying

    *   -end

        Bool. If set to true, then hooks will be put at the end of @INC
        instead of at the beginning (after Require::HookChain's own hook).
        Regardless, Require::HookChain's own hook will be put at the
        beginning to allow executing all the other hooks.

    *   -debug

        Bool. If set to true, then debug messages will be printed to stderr.

  Hook ordering
    The order of execution of hooks by Require::HookChain is by their order
    in @INC, so you should set the ordering yourself by way of the (reverse)
    ordering of "use Require::HookChain" statements. Each time you do this:

     use Require::HookChain 'hook1';

    then Require::HookChain will (re)install its own hook to the beginning
    of @INC, then insert "hook1" as the second element in @INC. Then when
    you load another hook:

     use Require::HookChain 'hook2';

    then Require::HookChain will (re)install its own hook to the beginning
    of @INC, then insert "hook2" as the second element in @INC, while
    "hook1" will be at the third element of @INC. So the order of hook
    execution will be: "hook2, hook1". When another hook, "hook3", is loaded
    afterwards, the order of execution will be "hook3, hook2, hook1".

    Some hooks should be loaded at the end of other hooks (and sources),
    e.g. debug::dump_source::stderr, so you should install such hooks using
    something like:

     use Require::HookChain -end=>1, 'hook4';

    in which case Require::HookChain will again (re)install its own hook to
    the beginning of @INC, then insert "hook4" as the last element in @INC.
    The order of execution of hooks will then be: "hook3, hook2, hook1,
    hook4". If you install another hook at the end:

     use Require::HookChain -end=>1, 'hook5';

    then the order of execution of hooks will then be: "hook3, hook2, hook1,
    hook4, hook5".

  Subnamespace organization
    *   Require::HookChain::debug::

        Hooks that do debugging-related stuffs. See also: "log::"
        subnamespace, "timestamp::" subnamespace.

    *   Require::HookChain::log::

        Hooks that add logging to module loading process. See also:
        "debug::" subnamespace.

    *   Require::HookChain::munge::

        Hooks that modify source code.

    *   Require::HookChain::postcheck::

        Hooks that perform checks after the source code is loaded (eval-ed).
        See also "precheck::" subnamespace.

    *   Require::HookChain::precheck::

        Hooks that perform checks before the source code is loaded
        (eval-ed). See also "postcheck::" subnamespace.

    *   Require::HookChain::source::

        Hooks that allow loading module source from alternative sources.

    *   Require::HookChain::test::

        Testing-related, particularly testing the Require::HookCHain hook
        module itself.

    *   Require::HookChain::timestamp::

        Hooks that add timestamps during module loading process.

Require::HookChain::r OBJECT
  Methods
   filename
    Usage:

     my $filename = $r->filename;

   src
    Usage:

     my $src = $r->src;
     $r->src($new_src);

    Get or set source code content. Will return undef if source code has not
    been found or set.

FAQ
  Loading a hook does nothing!
    Make sure you use a hook this way:

     use Require::HookChain 'hookname'; # correct

    instead of:

     use Require::HookChain::hookname; # INCORRECT, this does not install the hook to @INC

  The order of execution of hooks is incorrect!
    You control the ordering by putting the hooks in @INC in your preferred
    order. See "Hook ordering" for more details.

  What are the differences between Require::HookChain and Require::HookPlugin?
    Require::HookChain (RHC) and Require::HookPlugin (RHP) are both
    frameworks to add custom behavior to the module loading process. The
    following are the comparison between the two:

    RHC and RHP both work by installing its own handler (a coderef) at the
    beginning of @INC. They then evaluate the rest of @INC like Perl does,
    with some differences.

    Perl stops at the first @INC element where it finds the source code,
    while RHC's handler evaluates all the entries of @INC looking for hooks
    in the form of objects of the class under the "Require::HookChain::"
    namespace.

    RHP's plugins, on the other hand, are not installed directly in @INC but
    at another array (@Require::HookPlugin::Plugin_Instances), so the only
    entry installed in @INC is RHP's own handler.

    RHC evaluates hooks in @INC in order, so you have to install the hooks
    in the right order to get the correct behavior. On the other hand, RHP
    evaluates plugins based on events, plugins' priority, and activation
    order. Plugins have a default priority value (though you can override
    it). In general you can activate plugins in whatever order and generally
    they will do the right thing. RHP is more flexible and powerful than
    RHC, but is slightly more complex.

    Writing hooks for RHC (or plugins for RHP) are roughly equally easy.

HOMEPAGE
    Please visit the project's homepage at
    <https://metacpan.org/release/Require-HookChain>.

SOURCE
    Source repository is at
    <https://github.com/perlancar/perl-Require-HookChain>.

SEE ALSO
    RHC for convenience of using on the command-line or one-liners.

    Require::Hook (RH) is an older framework and is superseded by
    Require::HookChain (RHC).

    Require::HookPlugin (RHP) is a newer framework that aims to be more
    flexible and comes with sensible default of ordering, to avoid the trap
    of installing hooks at the wrong order. RHP might supersede RHC in the
    future.

AUTHOR
    perlancar <perlancar@cpan.org>

CONTRIBUTING
    To contribute, you can send patches by email/via RT, or send pull
    requests on GitHub.

    Most of the time, you don't need to build the distribution yourself. You
    can simply modify the code, then test via:

     % prove -l

    If you want to build the distribution (e.g. to try to install it locally
    on your system), you can install Dist::Zilla,
    Dist::Zilla::PluginBundle::Author::PERLANCAR,
    Pod::Weaver::PluginBundle::Author::PERLANCAR, and sometimes one or two
    other Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps
    required beyond that are considered a bug and can be reported to me.

COPYRIGHT AND LICENSE
    This software is copyright (c) 2023, 2022, 2020, 2017 by perlancar
    <perlancar@cpan.org>.

    This is free software; you can redistribute it and/or modify it under
    the same terms as the Perl 5 programming language system itself.

BUGS
    Please report any bugs or feature requests on the bugtracker website
    <https://rt.cpan.org/Public/Dist/Display.html?Name=Require-HookChain>

    When submitting a bug or request, please include a test-file or a patch
    to an existing test-file that illustrates the bug or desired feature.

