#   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
#   file: lib/Dist/Zilla/Plugin/Hook/Manual.pod
#

#pod =encoding UTF-8
#pod
#pod =head1 COPYRIGHT AND LICENSE
#pod
#pod Copyright © 2015 Van de Bugger
#pod
#pod This file is part of perl-Dist-Zilla-Plugin-Hook.
#pod
#pod perl-Dist-Zilla-Plugin-Hook is free software: you can redistribute it and/or modify it under
#pod the terms of the GNU General Public License as published by the Free Software Foundation,
#pod either version 3 of the License, or (at your option) any later version.
#pod
#pod perl-Dist-Zilla-Plugin-Hook is distributed in the hope that it will be useful, but WITHOUT ANY
#pod WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
#pod PURPOSE. See the GNU General Public License for more details.
#pod
#pod You should have received a copy of the GNU General Public License along with
#pod perl-Dist-Zilla-Plugin-Hook. If not, see <http://www.gnu.org/licenses/>.
#pod
#pod =cut

# PODNAME: Dist::Zilla::Plugin::Hook::Manual
# ABSTRACT: C<Hook> user manual

#pod =for :this This is C<Hook> user manual. Read this if you want to write C<Dist::Zilla> plugin directly in F<dist.ini>.
#pod
#pod =for :that If you are going to hack or extend C<Dist-Zilla-Plugin-Hook>, read the L<role module
#pod documentation|Dist::Zilla::Role::Hooker>. General topics like getting source, building, installing,
#pod bug reporting and some others are covered in the L<readme|Dist::Zilla::Plugin::Hook::ReadMe>.
#pod
#pod =head1 SYNOPSIS
#pod
#pod In your F<dist.ini>:
#pod
#pod     [Hook/prologue]
#pod         . = use autodie ':all';
#pod         . = use Path::Tiny;
#pod     [Hook::Role]
#pod         . = # Code of your inpline plugin:
#pod         . = $self->log( 'Starting…' );
#pod         . = # …arbitrary Perl code…
#pod         . = …
#pod
#pod where I<Role> is one of C<Dist-Zilla> roles, like C<BeforeBuild>, C<AfterBuild>, C<FileGatherer>,
#pod C<MetaProvider> etc. See complete list of supported roles in L</"DESCRIPTION">.
#pod
#pod =head1 DESCRIPTION
#pod
#pod C<Dist-Zilla-Plugin-Hook> is a set of plugins, like C<Hook::BeforeBuild> and C<Hook::AfterBuild>.
#pod
#pod Every C<Hook> plugin (except C<Hook::Init>, which is a bit special, see below) consumes a role with
#pod the same name, and implements the method required by the consumed role. When C<Dist::Zilla> invokes
#pod the method, it executes the code specified in the plugin's section of F<dist.ini>.
#pod
#pod An example: Plugin C<Hook::BeforeBuild> consumes C<BeforeBuild> role, this role requires
#pod C<before_build> method, which is implemented by the plugin. When C<Dist::Zilla> invokes
#pod C<Hook::BeforeBuild>'s C<before_build> method, it executes the code from the plugin's section of
#pod F<dist.ini>, e. g.:
#pod
#pod     name    = Assa
#pod     version = 0.001
#pod     [Hook::BeforeBuild]
#pod         . = $self->log( [ "Building v%s", $dist->version ] );
#pod     …
#pod
#pod And Perl code inlined into C<[Hook::AfterBuild]> section of F<dist.ini>, prints message "Building
#pod v0.001" to the log.
#pod
#pod The same for all other C<Hook> plugins. Only C<Hook::Init> plugin is a bit special: it implements
#pod C<BUILD> method. It has two features. First: there is no need in consuming a role to implement
#pod C<BUILD> method (and there is no role C<Dist::Zilla::Role::Init>). Second, and more important:
#pod C<BUILD> method is called at very early stage of the build, immediately after reading
#pod C<[Hook::Init]> section of F<dist.ini>. This is useful in some circumstances.
#pod
#pod =head2 Predefined Variables
#pod
#pod Inlined Perl code can use following predefined variables:
#pod
#pod =for :list
#pod = C<$self>
#pod = C<$plugin>
#pod Reference to the plugin object, executing the code.
#pod = C<$zilla>
#pod = C<$dist>
#pod Reference to C<Dist::Zilla> object, the same as C<< $self->zilla >>.
#pod = C<$arg>
#pod The first argument of the method. If C<Dist::Zilla> does not provide argument, the variable will
#pod be set to C<undef>.
#pod = C<@_>
#pod Arguments of the method, as provided by C<Dist::Zilla>. Self-reference is already shifted to
#pod C<$self> (but the first argument is not)!
#pod
#pod C<$plugin> and C<$dist> variables are defined for conformance with template processing plugins
#pod (C<GenarateFile>, C<TemplateFiles>, C<MetaResources::Template>, etc.). C<$arg> is set for
#pod convenience: in many cases C<Dist::Zilla> passes the only argument to the method (which usually is
#pod a C<HashRef>).
#pod
#pod =head2 Arguments and Return Value
#pod
#pod Return value of the code is not ignored but passed back to C<Dist::Zilla>. C<Dist::Zilla>, in turn,
#pod often ignores it, but sometimes return value is important, for example, for "provider" plugins:
#pod C<Hook::VersionProvider>, C<Hook::MetaProvider>, etc.
#pod
#pod Passing arguments and return values actually means you can write your own C<Dist::Zilla> plugin
#pod which code is not in an external C<.pm> file but inlined directly to F<dist.ini>. Of course, such
#pod approach is limited. For example, "inline plugin" cannot define attributes and methods. Anyway the
#pod approach is quite convenient for small fixes which do not require much coding. See L</"EXAMPLES">
#pod section.
#pod
#pod =head2 Prologue
#pod
#pod If F<dist.ini> contains section C<[Hook/prologue]>, the code from this section is executed as
#pod initial part of code of all C<Hook>s. All the predefined variables are available in prologue code
#pod too.
#pod
#pod Prologue may be used for loading frequently used modules, or for debugging:
#pod
#pod     [Hook/prologue]
#pod         . = use autodie ':all';
#pod         . = use Path::Tiny;
#pod         . = use IPC::System::Simple qw{ capture };
#pod         . = $self->log_debug( 'begins' );
#pod     [Hook::BeforeBuild]
#pod         . = # No need in "use autodie" and other modules
#pod         . = # loaded by prologue.
#pod         . = system( … );
#pod
#pod =head2 C<ErrorLogger> role
#pod
#pod Every C<Hook> plugin executes Perl code with help from (by consuming) the C<Hooker> role. The
#pod latter uses C<ErrorLogger> role internally, but it also available to be used in hooks:
#pod
#pod     . = $self->log_error( … );
#pod     . = $self->abort_if_error( … );
#pod     . = $self->abort( … );
#pod
#pod =head2 List of Modules
#pod
#pod This is the complete list of C<Hook> modules/roles and methods:
#pod
#pod     --------------------- + ----------------------
#pod     Module (role)         | Method
#pod     --------------------- + ----------------------
#pod     AfterBuild            | after_build
#pod     AfterMint             | after_mint
#pod     AfterRelease          | after_release
#pod     BeforeArchive         | before_archive
#pod     BeforeBuild           | before_build
#pod     BeforeMint            | before_mint
#pod     BeforeRelease         | before_release
#pod     FileGatherer          | gather_files
#pod     FileMunger            | munge_files
#pod     FilePruner            | prune_files
#pod     Init                  | BUILD
#pod     InstallTool           | setup_installer
#pod     LicenseProvider       | provide_license
#pod     MetaProvider          | metadata
#pod     NameProvider          | provide_name
#pod     ReleaseStatusProvider | provide_release_status
#pod     VersionProvider       | provide_version
#pod     --------------------- + ----------------------
#pod
#pod =option .
#pod
#pod This is multi-value option, i. e. it may be specified multiple time. Each value is a separate line
#pod of Perl code, e. g.:
#pod
#pod     . = if ( $dist->is_trial ) {
#pod     . =     $self->log( 'Trial version' );
#pod     . = };
#pod
#pod Beware of caveats, see L</"dist.ini Parsing">.
#pod
#pod =head1 CAVEATS
#pod
#pod =head2 F<dist.ini> Parsing
#pod
#pod Before code reaches a C<Hook> plugin, it is parsed by C<Dist::Zilla> config file reader (probably,
#pod C<Config::INI::Reader>). Config file reader seems to strip leading and trailing spaces from each
#pod value, and treat semicolon preceded by a space as a comment starter. Usually it is not a problem,
#pod just put semicolon immediately after statement:
#pod
#pod     . = foo(); bar();       # Ok
#pod     . = foo() ; bar() ;     # NOT OK: bar will not be called.
#pod
#pod Note that semicolon starts a F<dist.ini> comment even within Perl string:
#pod
#pod     . = $str = "one; two";  # Ok
#pod     . = $str = "one ; two"; # NOT OK
#pod
#pod And be careful with multi-line strings:
#pod
#pod     . = $str = "first line
#pod     . =     indented line"; # Leading spaces will be lost.
#pod
#pod =head1 EXAMPLES
#pod
#pod Examples below are focused on using C<Hook>, so F<dist.ini> is a primary file in all the examples,
#pod and sometimes is I<only> file. Perl module contains single line C<package Assa; 1;> and generated
#pod on-the-fly with C<GenerateFile> plugin.
#pod
#pod =head2 I<Description> Meta Resource
#pod
#pod Distribution meta information contains such items as I<name>, I<version>, I<abstract> (and many
#pod others). All named items are written to F<META.json> (and maybe F<META.yml>) automatically. All you
#pod need is just use C<MetaJSON> and/or C<MetaYAML> plugin(s).
#pod
#pod Meta information may also include I<description> — a longer, more complete description of the
#pod distribution. However, C<Dist::Zilla> does not provide ability to specify I<description>. It could
#pod be easily fixed with C<Hook>, though.
#pod
#pod F<dist.ini> file:
#pod
#pod     name     = Description
#pod     abstract = Hook demo: Set "description" meta info
#pod     version = 0.001
#pod     [Hook::MetaProvider]    ; <<<=== Look at this
#pod         ;   MetaProvider's metadata method must return HashRef (or undef).
#pod         ;   Multiple MetaProviders can coexist. Metainfo received from
#pod         ;   all providers will be merged by Dist::Zilla. This
#pod         ;   MetaProvider provides only description.
#pod         ;   See Dist::Zilla::Role::MetaProvider.
#pod         . = { description =>
#pod         . =     "This is not short one-line abstract,
#pod         . =     but more detailed description,
#pod         . =     which spans several lines."
#pod         . = }
#pod     [GenerateFile/Assa.pm]
#pod         filename = lib/Assa.pm
#pod         content  = package Assa; 1;
#pod     [MetaJSON]
#pod     [FakeRelease]
#pod
#pod =head2 Template Variables
#pod
#pod In a distribution, I have to duplicate the same pieces of information again and again. For example,
#pod bug report email and web URLs should be written in C<[MetaResources]> section of F<dist.ini> I<and>
#pod in the documentation, like F<BUGS.pod>.
#pod
#pod With a help from C<TemplateFiles> plugin I can eliminate duplication. If F<BUGS.pod> is a template,
#pod I can use email and web URLs defined in F<dist.ini>, e. g.:
#pod
#pod     {{$dist->distmeta->{resources}->{bugtracker}->{mailto};}}
#pod
#pod Err… This works but requires a lot of typing, (and so typo-prone) and looks ugly. With C<Hook> I
#pod can make it not just working, but also elegant. C<[Hook::Init]> section defines few variables in
#pod C<MY> package, which can be used in various templates, including documentation and meta resources.
#pod
#pod F<dist.ini> file:
#pod
#pod     name     = TemplateVariables
#pod     abstract = Hook demo: Define variables for later use in templates.
#pod     version  = 0.001
#pod     [Hook::Init]                ; <<<=== Look at this
#pod         . = package MY;
#pod         . = our $name    = $dist->name;
#pod         . = our $bt_mail = "mailto:bug-$name\@bt.example.org";
#pod         . = our $bt_web  = "https://bt.example.org/display.html?name=$name";
#pod         ;   BTW, Hook::BeforeBuild cannot be used here: it works too late,
#pod         ;   MetaResources::Template will not see the variables.
#pod     [GenerateFile/Assa.pm]
#pod         filename = lib/Assa.pm
#pod         content  = package Assa; 1;
#pod     [GatherDir]
#pod     [PruneCruft]
#pod     [TemplateFiles]             ; <<<=== Look at this
#pod         filename = BUGS.pod
#pod     [MetaResources::Template]   ; <<<=== Look at this
#pod         bugtracker.mailto = {{$MY::bt_mail}}
#pod         bugtracker.web    = {{$MY::bt_web}}
#pod         license           = {{$dist->license->url}}
#pod     [MetaJSON]
#pod     [FakeRelease]
#pod
#pod F<BUGS.pod> file:
#pod
#pod     =head1 BUGS
#pod
#pod     The quickest way to report a bug in C<{{$MY::name}}>
#pod     is by sending email to {{$MY::bt_mail}}.
#pod
#pod     Bug tracker can be used via
#pod     L<web interface|{{$MY::bt_web}}>.
#pod
#pod     =cut
#pod
#pod =head2 Version Handling
#pod
#pod I want the version of my distribution is bumped automatically after each release, and automatically
#pod assigned version should be trial one.
#pod
#pod For example: if I released version "0.001", the version of the next release should be "0.001_001".
#pod When I release "0.001_001", the next should be "0.001_002", the next one — "0.001_003" and so on.
#pod When I decide it is time to non-trial release, I will reset the version to "0.002" manually,
#pod release it, and will have automatically bumped version "0.002_001".
#pod
#pod The implementation is built with two plugins: C<Hook:VersionProvider> and C<Hook::AfterRelease>.
#pod The first one reads version from external file F<VERSION> which contains only version and nothing
#pod more (ok, newline is allowed) — it simplifies implementation, because there is no need in parsing
#pod F<dist.ini> file. The second plugin bumps the version after release.
#pod
#pod F<dist.ini> file:
#pod
#pod     name     = VersionHandling
#pod     abstract = Hook demo: Read version from external file
#pod     [Hook::VersionProvider]     ; <<<=== Look at this
#pod         . = use Path::Tiny;
#pod         . = path( 'VERSION' )->slurp =~ s{\s*\z}{}gr;
#pod     [GenerateFile/Assa.pm]
#pod         filename = lib/Assa.pm
#pod         content  = package Assa; 1;
#pod     [MetaJSON]
#pod     [FakeRelease]
#pod     [Hook::AfterRelease]        ; <<<=== Look at this
#pod         . = use Path::Tiny;
#pod         . = my $next = ( eval( $dist->version ) + 0.000001 ) =~ s{(\d{3})\z}{_$1}r;
#pod         . = path( 'VERSION' )->spew( $next );
#pod         . = $self->log( [ 'Next release will be %s', $next ] );
#pod
#pod F<VERSION> file:
#pod
#pod     0.00
#pod
#pod =head2 Unwanted Dependencies
#pod
#pod C<Data::Printer> is a great module, I often use it for debugging. However, sometimes I forget to
#pod remove
#pod
#pod     use Data::Printer;
#pod
#pod from the code and make a release with this unwanted dependency. C<Hook::BeforeRelease> checks the
#pod distribution does not have unwanted dependencies. If it does, release will be aborted.
#pod
#pod F<dist.ini> file:
#pod
#pod     name     = UnwantedDependencies
#pod     abstract = Hook demo: Check the distro does not have unwanted dependencies
#pod     version = 0.001
#pod     [GenerateFile/Assa.pm]
#pod         filename = lib/Assa.pm
#pod         content  = package Assa; use DDP; 1;
#pod     [AutoPrereqs]
#pod     [MetaJSON]
#pod     [Hook::BeforeRelease]       ; <<<=== Look at this
#pod         . = my @modules = qw{ DDP Data::Printer };  # Unwanted modules.
#pod         . = my $prereqs = $dist->distmeta->{ prereqs };
#pod         . = for my $m ( @modules ) {
#pod         . =     for my $s ( qw{ configure develop runtime test } ) {
#pod         . =         if ( exists( $prereqs->{ $s }->{ requires }->{ $m } ) ) {
#pod         . =             $self->log_error( [ '%s found in %s prereqs ', $m, $s ] );
#pod         . =         };
#pod         . =     };
#pod         . = };
#pod         . = $self->abort_if_error( 'unwanted dependencies found' );
#pod     [FakeRelease]
#pod
#pod
#pod
#pod =head1 SEE ALSO
#pod
#pod =for :list
#pod = L<Dist::Zilla>
#pod = L<Dist::Zilla::Plugin::Run>
#pod = L<Dist::Zilla::Role::Hooker>
#pod = L<Dist::Zilla::Role::ErrorLogger>
#pod = L<Dist::Zilla::Plugin::Hook>
#pod = L<Dist::Zilla::Plugin::Hook::ReadMe>
#pod
#pod =cut

# doc/what.pod #

#pod =encoding UTF-8
#pod
#pod =head1 WHAT?
#pod
#pod C<Dist-Zilla-Plugin-Hook> (or just C<Hook>) is a set of C<Dist-Zilla> plugins. Every plugin executes Perl
#pod code inlined into F<dist.ini> at particular stage of build process.
#pod
#pod =cut

# end of file #
# doc/why.pod #

#pod =encoding UTF-8
#pod
#pod =head1 WHY?
#pod
#pod There is C<Dist::Zilla::Plugin::Run> on CPAN, which allows to run Perl code from within
#pod F<dist.ini>, why I wrote one more? Let us consider two examples.
#pod
#pod The first one executes external commands:
#pod
#pod     $cat dist.ini
#pod     name     = RunShell
#pod     abstract = RunShell demo
#pod     version  = 0.001_001
#pod     [Run::BeforeBuild]
#pod         run            = echo "1. begin"
#pod         run_if_release = echo "2. release"
#pod         run_no_release = echo "3. not release"
#pod         run_if_trial   = echo "4. trial"
#pod         run_no_trial   = echo "5. not trial"
#pod         run            = echo "6. end"
#pod     [GenerateFile/Assa.pm]
#pod         filename = lib/Assa.pm
#pod         content  = package Assa; 1;
#pod     [FakeRelease]
#pod
#pod     $ dzil build
#pod     [Run::BeforeBuild] executing: echo "1. begin"
#pod     [Run::BeforeBuild] 1. begin
#pod     [Run::BeforeBuild] executing: echo "6. end"
#pod     [Run::BeforeBuild] 6. end
#pod     [Run::BeforeBuild] executing: echo "5. not trial"
#pod     [Run::BeforeBuild] 5. not trial
#pod     [Run::BeforeBuild] executing: echo "3. not release"
#pod     [Run::BeforeBuild] 3. not release
#pod     [DZ] beginning to build RunShell
#pod     [DZ] writing RunShell in RunShell-0.001_001
#pod     [DZ] building archive with Archive::Tar::Wrapper
#pod     [DZ] writing archive to RunShell-0.001_001-TRIAL.tar.gz
#pod     [DZ] built in RunShell-0.001_001
#pod
#pod
#pod Execution order is err… non-linear. Of course there is an explanation why command were executed in
#pod this particular order, but looking at F<dist.ini> it is not obvious. (It is also unclear why C<Run>
#pod consider the build is I<not> trial, but it may be just a bug.)
#pod
#pod Another example executes Perl code:
#pod
#pod     $cat dist.ini
#pod     name     = RunPerl
#pod     abstract = RunPerl demo
#pod     version  = 0.001_001
#pod     [Run::BeforeBuild]
#pod         eval = my $self = shift( @_ );
#pod         eval = my $dist = $self->zilla;
#pod         eval = $self->log( [ '%s v%s', $dist->name, $dist->version ] );
#pod     [GenerateFile/Assa.pm]
#pod         filename = lib/Assa.pm
#pod         content  = package Assa; 1;
#pod     [FakeRelease]
#pod
#pod     $ dzil build
#pod     [Run::BeforeBuild] evaluating: my $self = shift( @_ );
#pod     [Run::BeforeBuild] my $dist = $self->zilla;
#pod     [Run::BeforeBuild] $self->log( [ '0.001_001 v', $dist->name, $dist->version ] );
#pod     [Run::BeforeBuild] 0.001_001 v
#pod     [DZ] beginning to build RunPerl
#pod     [DZ] writing RunPerl in RunPerl-0.001_001
#pod     [DZ] building archive with Archive::Tar::Wrapper
#pod     [DZ] writing archive to RunPerl-0.001_001-TRIAL.tar.gz
#pod     [DZ] built in RunPerl-0.001_001
#pod
#pod
#pod Look at the last message from C<Run::BeforeBuild> plugin. Surprising? Where is the distribution
#pod name? Why is the character "v" printed after version number? Ah! C<%s> is a special conversion
#pod specifier which was replaced with "something retained for backward compatibility". There is a bunch
#pod of other conversion specifiers: C<%a>, C<%d>, C<%n>,C<%p>, C<%t>, C<%v>, C<%x>,… That effectively
#pod means I cannot use printf-like functions and hashes, because every percent will be replaced with
#pod something or cause error "unknown conversion".
#pod
#pod There is (undocumented) method to avoid it — every percent sign should be doubled:
#pod
#pod     my %%args;
#pod
#pod However, this is err… not quite Perl.
#pod
#pod Let me cite "Philosophy" section of the great C<Text::Template> module:
#pod
#pod     When people make a template module like this one, they almost always
#pod     start by inventing a special syntax for substitutions. For example,
#pod     they build it so that a string like %%VAR%% is replaced with the
#pod     value of $VAR. Then they realize the need extra formatting, so they
#pod     put in some special syntax for formatting. Then they need a loop, so
#pod     they invent a loop syntax. Pretty soon they have a new little
#pod     template language.
#pod
#pod     This approach has two problems: First, their little language is
#pod     crippled. If you need to do something the author hasn't thought of,
#pod     you lose. Second: Who wants to learn another language? You already
#pod     know Perl, so why not use it?
#pod
#pod Look: C<Run> plugin introduced a bunch of F<dist.ini> options: C<run_if_trial>, C<run_no_trial>
#pod (BTW, why not C<run_if_not_trial>?), C<run_if_release>, C<run_no_release>, C<eval>,
#pod C<censor_commands>, C<fatal_errors>, C<quiet>; a bunch of "conversion specifiers": C<%a>, C<%d>,
#pod C<%n>, C<%p>, C<%v>, C<%t>, C<%x>, C<%s>; and bunch of poorly documented rules. It's "a little
#pod crippled language", isn't it?
#pod
#pod Compared to C<Run>, C<Hook> is rather minimalistic: It provides only one option, and it executes
#pod only Perl. All other C<Run> features can be easily implemented in Perl, for example:
#pod
#pod Running external commands:
#pod
#pod     . = system( … );
#pod
#pod Making errors in external commands fatal:
#pod
#pod     . = use autodie ':all';
#pod     . = system( … );
#pod
#pod Making errors in Perl code non-fatal:
#pod
#pod     . = use Try::Tiny;
#pod     . = try { … };
#pod
#pod Checking trial status:
#pod
#pod     . = … if $dist->is_trial;
#pod     ; or
#pod     . = if ( $dist->is_trial ) { … };
#pod
#pod Checking release build:
#pod
#pod     . = … if $ENV{ DZIL_RELEASING };
#pod     ; or
#pod     . = if ( $ENV{ DZIL_RELEASING } ) { … };
#pod
#pod The code is a little bit longer than C<Run> counterparts, but it is well-known full-featured Perl.
#pod
#pod What if you need to pass to an external command something the C<Run> authors have not thought of?
#pod For example, abstract or licence name. There are no conversion specifiers for it, so you lose. But
#pod with C<Hook> it is trivial:
#pod
#pod     . = system( …, $dist->abstract, …, $dist->license->name, … );
#pod
#pod BTW, there are two minor (at the first look) C<Hook> features:
#pod
#pod =for :list
#pod 1. C<Hook> passes arguments provided by C<Dist::Zilla> to the code.
#pod 2. C<Hook> passes return value from the code back to C<Dist::Zilla>.
#pod
#pod which bring a new quality: with C<Hook> you can write inline plugins. For example, a plugin which
#pod reads distribution version from an external file:
#pod
#pod     [Hook::VersionProvider]
#pod         . = use Path::Tiny; path('VERSION' )->slurp;
#pod
#pod (Actually, every hook I<is> an inline plugin.) See more in Examples.
#pod
#pod =cut

# end of file #


# end of file #

__END__

=pod

=encoding UTF-8

=head1 NAME

Dist::Zilla::Plugin::Hook::Manual - C<Hook> user manual

=head1 VERSION

Version 0.002, released on 2015-07-23 22:08 UTC.

=head1 WHAT?

C<Dist-Zilla-Plugin-Hook> (or just C<Hook>) is a set of C<Dist-Zilla> plugins. Every plugin executes Perl
code inlined into F<dist.ini> at particular stage of build process.

This is C<Hook> user manual. Read this if you want to write C<Dist::Zilla> plugin directly in F<dist.ini>.

If you are going to hack or extend C<Dist-Zilla-Plugin-Hook>, read the L<role module
documentation|Dist::Zilla::Role::Hooker>. General topics like getting source, building, installing,
bug reporting and some others are covered in the L<readme|Dist::Zilla::Plugin::Hook::ReadMe>.

=head1 SYNOPSIS

In your F<dist.ini>:

    [Hook/prologue]
        . = use autodie ':all';
        . = use Path::Tiny;
    [Hook::Role]
        . = # Code of your inpline plugin:
        . = $self->log( 'Starting…' );
        . = # …arbitrary Perl code…
        . = …

where I<Role> is one of C<Dist-Zilla> roles, like C<BeforeBuild>, C<AfterBuild>, C<FileGatherer>,
C<MetaProvider> etc. See complete list of supported roles in L</"DESCRIPTION">.

=head1 DESCRIPTION

C<Dist-Zilla-Plugin-Hook> is a set of plugins, like C<Hook::BeforeBuild> and C<Hook::AfterBuild>.

Every C<Hook> plugin (except C<Hook::Init>, which is a bit special, see below) consumes a role with
the same name, and implements the method required by the consumed role. When C<Dist::Zilla> invokes
the method, it executes the code specified in the plugin's section of F<dist.ini>.

An example: Plugin C<Hook::BeforeBuild> consumes C<BeforeBuild> role, this role requires
C<before_build> method, which is implemented by the plugin. When C<Dist::Zilla> invokes
C<Hook::BeforeBuild>'s C<before_build> method, it executes the code from the plugin's section of
F<dist.ini>, e. g.:

    name    = Assa
    version = 0.001
    [Hook::BeforeBuild]
        . = $self->log( [ "Building v%s", $dist->version ] );
    …

And Perl code inlined into C<[Hook::AfterBuild]> section of F<dist.ini>, prints message "Building
v0.001" to the log.

The same for all other C<Hook> plugins. Only C<Hook::Init> plugin is a bit special: it implements
C<BUILD> method. It has two features. First: there is no need in consuming a role to implement
C<BUILD> method (and there is no role C<Dist::Zilla::Role::Init>). Second, and more important:
C<BUILD> method is called at very early stage of the build, immediately after reading
C<[Hook::Init]> section of F<dist.ini>. This is useful in some circumstances.

=head2 Predefined Variables

Inlined Perl code can use following predefined variables:

=over 4

=item C<$self>

=item C<$plugin>

Reference to the plugin object, executing the code.

=item C<$zilla>

=item C<$dist>

Reference to C<Dist::Zilla> object, the same as C<< $self->zilla >>.

=item C<$arg>

The first argument of the method. If C<Dist::Zilla> does not provide argument, the variable will
be set to C<undef>.

=item C<@_>

Arguments of the method, as provided by C<Dist::Zilla>. Self-reference is already shifted to
C<$self> (but the first argument is not)!

=back

C<$plugin> and C<$dist> variables are defined for conformance with template processing plugins
(C<GenarateFile>, C<TemplateFiles>, C<MetaResources::Template>, etc.). C<$arg> is set for
convenience: in many cases C<Dist::Zilla> passes the only argument to the method (which usually is
a C<HashRef>).

=head2 Arguments and Return Value

Return value of the code is not ignored but passed back to C<Dist::Zilla>. C<Dist::Zilla>, in turn,
often ignores it, but sometimes return value is important, for example, for "provider" plugins:
C<Hook::VersionProvider>, C<Hook::MetaProvider>, etc.

Passing arguments and return values actually means you can write your own C<Dist::Zilla> plugin
which code is not in an external C<.pm> file but inlined directly to F<dist.ini>. Of course, such
approach is limited. For example, "inline plugin" cannot define attributes and methods. Anyway the
approach is quite convenient for small fixes which do not require much coding. See L</"EXAMPLES">
section.

=head2 Prologue

If F<dist.ini> contains section C<[Hook/prologue]>, the code from this section is executed as
initial part of code of all C<Hook>s. All the predefined variables are available in prologue code
too.

Prologue may be used for loading frequently used modules, or for debugging:

    [Hook/prologue]
        . = use autodie ':all';
        . = use Path::Tiny;
        . = use IPC::System::Simple qw{ capture };
        . = $self->log_debug( 'begins' );
    [Hook::BeforeBuild]
        . = # No need in "use autodie" and other modules
        . = # loaded by prologue.
        . = system( … );

=head2 C<ErrorLogger> role

Every C<Hook> plugin executes Perl code with help from (by consuming) the C<Hooker> role. The
latter uses C<ErrorLogger> role internally, but it also available to be used in hooks:

    . = $self->log_error( … );
    . = $self->abort_if_error( … );
    . = $self->abort( … );

=head2 List of Modules

This is the complete list of C<Hook> modules/roles and methods:

    --------------------- + ----------------------
    Module (role)         | Method
    --------------------- + ----------------------
    AfterBuild            | after_build
    AfterMint             | after_mint
    AfterRelease          | after_release
    BeforeArchive         | before_archive
    BeforeBuild           | before_build
    BeforeMint            | before_mint
    BeforeRelease         | before_release
    FileGatherer          | gather_files
    FileMunger            | munge_files
    FilePruner            | prune_files
    Init                  | BUILD
    InstallTool           | setup_installer
    LicenseProvider       | provide_license
    MetaProvider          | metadata
    NameProvider          | provide_name
    ReleaseStatusProvider | provide_release_status
    VersionProvider       | provide_version
    --------------------- + ----------------------

=head1 OPTIONS

=head2 .

This is multi-value option, i. e. it may be specified multiple time. Each value is a separate line
of Perl code, e. g.:

    . = if ( $dist->is_trial ) {
    . =     $self->log( 'Trial version' );
    . = };

Beware of caveats, see L</"dist.ini Parsing">.

=head1 EXAMPLES

Examples below are focused on using C<Hook>, so F<dist.ini> is a primary file in all the examples,
and sometimes is I<only> file. Perl module contains single line C<package Assa; 1;> and generated
on-the-fly with C<GenerateFile> plugin.

=head2 I<Description> Meta Resource

Distribution meta information contains such items as I<name>, I<version>, I<abstract> (and many
others). All named items are written to F<META.json> (and maybe F<META.yml>) automatically. All you
need is just use C<MetaJSON> and/or C<MetaYAML> plugin(s).

Meta information may also include I<description> — a longer, more complete description of the
distribution. However, C<Dist::Zilla> does not provide ability to specify I<description>. It could
be easily fixed with C<Hook>, though.

F<dist.ini> file:

    name     = Description
    abstract = Hook demo: Set "description" meta info
    version = 0.001
    [Hook::MetaProvider]    ; <<<=== Look at this
        ;   MetaProvider's metadata method must return HashRef (or undef).
        ;   Multiple MetaProviders can coexist. Metainfo received from
        ;   all providers will be merged by Dist::Zilla. This
        ;   MetaProvider provides only description.
        ;   See Dist::Zilla::Role::MetaProvider.
        . = { description =>
        . =     "This is not short one-line abstract,
        . =     but more detailed description,
        . =     which spans several lines."
        . = }
    [GenerateFile/Assa.pm]
        filename = lib/Assa.pm
        content  = package Assa; 1;
    [MetaJSON]
    [FakeRelease]

=head2 Template Variables

In a distribution, I have to duplicate the same pieces of information again and again. For example,
bug report email and web URLs should be written in C<[MetaResources]> section of F<dist.ini> I<and>
in the documentation, like F<BUGS.pod>.

With a help from C<TemplateFiles> plugin I can eliminate duplication. If F<BUGS.pod> is a template,
I can use email and web URLs defined in F<dist.ini>, e. g.:

    {{$dist->distmeta->{resources}->{bugtracker}->{mailto};}}

Err… This works but requires a lot of typing, (and so typo-prone) and looks ugly. With C<Hook> I
can make it not just working, but also elegant. C<[Hook::Init]> section defines few variables in
C<MY> package, which can be used in various templates, including documentation and meta resources.

F<dist.ini> file:

    name     = TemplateVariables
    abstract = Hook demo: Define variables for later use in templates.
    version  = 0.001
    [Hook::Init]                ; <<<=== Look at this
        . = package MY;
        . = our $name    = $dist->name;
        . = our $bt_mail = "mailto:bug-$name\@bt.example.org";
        . = our $bt_web  = "https://bt.example.org/display.html?name=$name";
        ;   BTW, Hook::BeforeBuild cannot be used here: it works too late,
        ;   MetaResources::Template will not see the variables.
    [GenerateFile/Assa.pm]
        filename = lib/Assa.pm
        content  = package Assa; 1;
    [GatherDir]
    [PruneCruft]
    [TemplateFiles]             ; <<<=== Look at this
        filename = BUGS.pod
    [MetaResources::Template]   ; <<<=== Look at this
        bugtracker.mailto = {{$MY::bt_mail}}
        bugtracker.web    = {{$MY::bt_web}}
        license           = {{$dist->license->url}}
    [MetaJSON]
    [FakeRelease]

F<BUGS.pod> file:

    =head1 BUGS

    The quickest way to report a bug in C<{{$MY::name}}>
    is by sending email to {{$MY::bt_mail}}.

    Bug tracker can be used via
    L<web interface|{{$MY::bt_web}}>.

    =cut

=head2 Version Handling

I want the version of my distribution is bumped automatically after each release, and automatically
assigned version should be trial one.

For example: if I released version "0.001", the version of the next release should be "0.001_001".
When I release "0.001_001", the next should be "0.001_002", the next one — "0.001_003" and so on.
When I decide it is time to non-trial release, I will reset the version to "0.002" manually,
release it, and will have automatically bumped version "0.002_001".

The implementation is built with two plugins: C<Hook:VersionProvider> and C<Hook::AfterRelease>.
The first one reads version from external file F<VERSION> which contains only version and nothing
more (ok, newline is allowed) — it simplifies implementation, because there is no need in parsing
F<dist.ini> file. The second plugin bumps the version after release.

F<dist.ini> file:

    name     = VersionHandling
    abstract = Hook demo: Read version from external file
    [Hook::VersionProvider]     ; <<<=== Look at this
        . = use Path::Tiny;
        . = path( 'VERSION' )->slurp =~ s{\s*\z}{}gr;
    [GenerateFile/Assa.pm]
        filename = lib/Assa.pm
        content  = package Assa; 1;
    [MetaJSON]
    [FakeRelease]
    [Hook::AfterRelease]        ; <<<=== Look at this
        . = use Path::Tiny;
        . = my $next = ( eval( $dist->version ) + 0.000001 ) =~ s{(\d{3})\z}{_$1}r;
        . = path( 'VERSION' )->spew( $next );
        . = $self->log( [ 'Next release will be %s', $next ] );

F<VERSION> file:

    0.00

=head2 Unwanted Dependencies

C<Data::Printer> is a great module, I often use it for debugging. However, sometimes I forget to
remove

    use Data::Printer;

from the code and make a release with this unwanted dependency. C<Hook::BeforeRelease> checks the
distribution does not have unwanted dependencies. If it does, release will be aborted.

F<dist.ini> file:

    name     = UnwantedDependencies
    abstract = Hook demo: Check the distro does not have unwanted dependencies
    version = 0.001
    [GenerateFile/Assa.pm]
        filename = lib/Assa.pm
        content  = package Assa; use DDP; 1;
    [AutoPrereqs]
    [MetaJSON]
    [Hook::BeforeRelease]       ; <<<=== Look at this
        . = my @modules = qw{ DDP Data::Printer };  # Unwanted modules.
        . = my $prereqs = $dist->distmeta->{ prereqs };
        . = for my $m ( @modules ) {
        . =     for my $s ( qw{ configure develop runtime test } ) {
        . =         if ( exists( $prereqs->{ $s }->{ requires }->{ $m } ) ) {
        . =             $self->log_error( [ '%s found in %s prereqs ', $m, $s ] );
        . =         };
        . =     };
        . = };
        . = $self->abort_if_error( 'unwanted dependencies found' );
    [FakeRelease]

=head1 CAVEATS

=head2 F<dist.ini> Parsing

Before code reaches a C<Hook> plugin, it is parsed by C<Dist::Zilla> config file reader (probably,
C<Config::INI::Reader>). Config file reader seems to strip leading and trailing spaces from each
value, and treat semicolon preceded by a space as a comment starter. Usually it is not a problem,
just put semicolon immediately after statement:

    . = foo(); bar();       # Ok
    . = foo() ; bar() ;     # NOT OK: bar will not be called.

Note that semicolon starts a F<dist.ini> comment even within Perl string:

    . = $str = "one; two";  # Ok
    . = $str = "one ; two"; # NOT OK

And be careful with multi-line strings:

    . = $str = "first line
    . =     indented line"; # Leading spaces will be lost.

=head1 WHY?

There is C<Dist::Zilla::Plugin::Run> on CPAN, which allows to run Perl code from within
F<dist.ini>, why I wrote one more? Let us consider two examples.

The first one executes external commands:

    $cat dist.ini
    name     = RunShell
    abstract = RunShell demo
    version  = 0.001_001
    [Run::BeforeBuild]
        run            = echo "1. begin"
        run_if_release = echo "2. release"
        run_no_release = echo "3. not release"
        run_if_trial   = echo "4. trial"
        run_no_trial   = echo "5. not trial"
        run            = echo "6. end"
    [GenerateFile/Assa.pm]
        filename = lib/Assa.pm
        content  = package Assa; 1;
    [FakeRelease]

    $ dzil build
    [Run::BeforeBuild] executing: echo "1. begin"
    [Run::BeforeBuild] 1. begin
    [Run::BeforeBuild] executing: echo "6. end"
    [Run::BeforeBuild] 6. end
    [Run::BeforeBuild] executing: echo "5. not trial"
    [Run::BeforeBuild] 5. not trial
    [Run::BeforeBuild] executing: echo "3. not release"
    [Run::BeforeBuild] 3. not release
    [DZ] beginning to build RunShell
    [DZ] writing RunShell in RunShell-0.001_001
    [DZ] building archive with Archive::Tar::Wrapper
    [DZ] writing archive to RunShell-0.001_001-TRIAL.tar.gz
    [DZ] built in RunShell-0.001_001

Execution order is err… non-linear. Of course there is an explanation why command were executed in
this particular order, but looking at F<dist.ini> it is not obvious. (It is also unclear why C<Run>
consider the build is I<not> trial, but it may be just a bug.)

Another example executes Perl code:

    $cat dist.ini
    name     = RunPerl
    abstract = RunPerl demo
    version  = 0.001_001
    [Run::BeforeBuild]
        eval = my $self = shift( @_ );
        eval = my $dist = $self->zilla;
        eval = $self->log( [ '%s v%s', $dist->name, $dist->version ] );
    [GenerateFile/Assa.pm]
        filename = lib/Assa.pm
        content  = package Assa; 1;
    [FakeRelease]

    $ dzil build
    [Run::BeforeBuild] evaluating: my $self = shift( @_ );
    [Run::BeforeBuild] my $dist = $self->zilla;
    [Run::BeforeBuild] $self->log( [ '0.001_001 v', $dist->name, $dist->version ] );
    [Run::BeforeBuild] 0.001_001 v
    [DZ] beginning to build RunPerl
    [DZ] writing RunPerl in RunPerl-0.001_001
    [DZ] building archive with Archive::Tar::Wrapper
    [DZ] writing archive to RunPerl-0.001_001-TRIAL.tar.gz
    [DZ] built in RunPerl-0.001_001

Look at the last message from C<Run::BeforeBuild> plugin. Surprising? Where is the distribution
name? Why is the character "v" printed after version number? Ah! C<%s> is a special conversion
specifier which was replaced with "something retained for backward compatibility". There is a bunch
of other conversion specifiers: C<%a>, C<%d>, C<%n>,C<%p>, C<%t>, C<%v>, C<%x>,… That effectively
means I cannot use printf-like functions and hashes, because every percent will be replaced with
something or cause error "unknown conversion".

There is (undocumented) method to avoid it — every percent sign should be doubled:

    my %%args;

However, this is err… not quite Perl.

Let me cite "Philosophy" section of the great C<Text::Template> module:

    When people make a template module like this one, they almost always
    start by inventing a special syntax for substitutions. For example,
    they build it so that a string like %%VAR%% is replaced with the
    value of $VAR. Then they realize the need extra formatting, so they
    put in some special syntax for formatting. Then they need a loop, so
    they invent a loop syntax. Pretty soon they have a new little
    template language.

    This approach has two problems: First, their little language is
    crippled. If you need to do something the author hasn't thought of,
    you lose. Second: Who wants to learn another language? You already
    know Perl, so why not use it?

Look: C<Run> plugin introduced a bunch of F<dist.ini> options: C<run_if_trial>, C<run_no_trial>
(BTW, why not C<run_if_not_trial>?), C<run_if_release>, C<run_no_release>, C<eval>,
C<censor_commands>, C<fatal_errors>, C<quiet>; a bunch of "conversion specifiers": C<%a>, C<%d>,
C<%n>, C<%p>, C<%v>, C<%t>, C<%x>, C<%s>; and bunch of poorly documented rules. It's "a little
crippled language", isn't it?

Compared to C<Run>, C<Hook> is rather minimalistic: It provides only one option, and it executes
only Perl. All other C<Run> features can be easily implemented in Perl, for example:

Running external commands:

    . = system( … );

Making errors in external commands fatal:

    . = use autodie ':all';
    . = system( … );

Making errors in Perl code non-fatal:

    . = use Try::Tiny;
    . = try { … };

Checking trial status:

    . = … if $dist->is_trial;
    ; or
    . = if ( $dist->is_trial ) { … };

Checking release build:

    . = … if $ENV{ DZIL_RELEASING };
    ; or
    . = if ( $ENV{ DZIL_RELEASING } ) { … };

The code is a little bit longer than C<Run> counterparts, but it is well-known full-featured Perl.

What if you need to pass to an external command something the C<Run> authors have not thought of?
For example, abstract or licence name. There are no conversion specifiers for it, so you lose. But
with C<Hook> it is trivial:

    . = system( …, $dist->abstract, …, $dist->license->name, … );

BTW, there are two minor (at the first look) C<Hook> features:

=over 4

=item 1

C<Hook> passes arguments provided by C<Dist::Zilla> to the code.

=item 2

C<Hook> passes return value from the code back to C<Dist::Zilla>.

=back

which bring a new quality: with C<Hook> you can write inline plugins. For example, a plugin which
reads distribution version from an external file:

    [Hook::VersionProvider]
        . = use Path::Tiny; path('VERSION' )->slurp;

(Actually, every hook I<is> an inline plugin.) See more in Examples.

=head1 SEE ALSO

=over 4

=item L<Dist::Zilla>

=item L<Dist::Zilla::Plugin::Run>

=item L<Dist::Zilla::Role::Hooker>

=item L<Dist::Zilla::Role::ErrorLogger>

=item L<Dist::Zilla::Plugin::Hook>

=item L<Dist::Zilla::Plugin::Hook::ReadMe>

=back

=head1 AUTHOR

Van de Bugger <van.de.bugger@gmail.com>

=head1 COPYRIGHT AND LICENSE

Copyright © 2015 Van de Bugger

This file is part of perl-Dist-Zilla-Plugin-Hook.

perl-Dist-Zilla-Plugin-Hook is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.

perl-Dist-Zilla-Plugin-Hook is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
perl-Dist-Zilla-Plugin-Hook. If not, see <http://www.gnu.org/licenses/>.

=cut
