package Pod::Readme;
=head1 NAME
Pod::Readme - Intelligently generate a README file from POD
=for readme plugin version
=head1 SYNOPSIS
In a module's POD:
=head1 NAME
MyApp - my nifty app
=for readme plugin version
=head1 DESCRIPTION
This is a nifty app.
=begin :readme
=for readme plugin requires
=head1 INSTALLATION
...
=end :readme
=for readme stop
=head1 METHODS
...
Then from the command-line:
pod2readme lib/MyModule.pm README
=for readme stop
From within Perl:
use Pod::Readme;
my $prf = Pod::Readme->new(
input_file => 'lib/MyModule.pm',
translate_to_file => $dest,
translation_class => 'Pod::Simple::Text',
);
$prf->run();
=for readme start
=head1 DESCRIPTION
This module filters POD to generate a F<README> file, by using POD
commands to specify which parts are included or excluded from the
F<README> file.
=begin :readme
See the L<Pod::Readme> documentation for more details on the POD
syntax that this module recognizes.
See L<pod2readme> for command-line usage.
=head1 INSTALLATION
See
L<How to install CPAN modules|http://www.cpan.org/modules/INSTALL.html>.
=for readme plugin requires heading-level=2 title="Required Modules"
=for readme plugin changes
=end :readme
=for readme stop
=head1 POD COMMANDS
=head2 C<=for readme stop>
Stop including the POD that follows in the F<README>.
=head2 C<=for readme start>
=head2 C<=for readme continue>
Start (or continue to) include the POD that follows in the F<README>.
Note that the C<start> command was added as a synonym in version
1.0.0.
=head2 C<=for readme include>
=for readme include file="INSTALL" type="text"
Include a text or POD file in the F<README>. It accepts the following
options:
=over
=item C<file>
Required. This is the file name to include.
=item C<type>
Can be "text" or "pod" (default).
=item C<start>
An optional regex of where to start including the file.
=item C<stop>
An optional regex of where to stop including the file.
=back
=head2 C<=for readme plugin>
Loads a plugin, e.g.
=for readme plugin version
Note that specific plugins may add options, e.g.
=for readme plugin changes title='CHANGES'
See L<Pod::Readme::Plugin> for more information.
Note that the C<plugin> command was added in version 1.0.0.
=head2 C<=begin :readme>
=head2 C<=end :readme>
Specify a block of POD to include only in the F<README>.
You can also specify a block in another format:
=begin readme text
...
=end readme text
This will be translated into
=begin text
...
=end text
and will only be included in F<README> files of that format.
Note: earlier versions of this module suggested using
=begin readme
...
=end readme
While this version supports that syntax for backwards compatibility,
it is not standard POD.
=cut
use v5.10.1;
use Moo;
extends 'Pod::Readme::Filter';
our $VERSION = 'v1.2.3';
use Carp;
use IO qw/ File Handle /;
use List::Util 1.33 qw/ any /;
use Module::Load qw/ load /;
use Path::Tiny qw/ path tempfile /;
use Pod::Simple;
use Types::Standard qw/ Bool Maybe Str /;
use Pod::Readme::Types qw/ File WriteIO /;
# RECOMMEND PREREQ: Pod::Man
# RECOMMEND PREREQ: Pod::Markdown
# RECOMMEND PREREQ: Pod::Markdown::Github
# RECOMMEND PREREQ: Pod::Simple::HTML
# RECOMMEND PREREQ: Pod::Simple::LaTeX
# RECOMMEND PREREQ: Pod::Simple::RTF
# RECOMMEND PREREQ: Pod::Simple::Text
# RECOMMEND PREREQ: Pod::Simple::XHTML
=head1 ATTRIBUTES
This module extends L<Pod::Readme::Filter> with the following
attributes:
=head2 C<translation_class>
The class used to translate the filtered POD into another format,
e.g. L<Pod::Simple::Text>.
If it is C<undef>, then there is no translation.
Only subclasses of L<Pod::Simple> are supported.
=cut
has translation_class => (
is => 'ro',
isa => Maybe [Str],
default => undef,
);
=head2 C<translate_to_fh>
The L<IO::Handle> to save the translated file to.
=cut
has translate_to_fh => (
is => 'ro',
isa => WriteIO,
lazy => 1,
builder => '_build_translate_to_fh',
coerce => sub { WriteIO->coerce(@_) },
);
sub _build_translate_to_fh {
my ($self) = @_;
if ( $self->translate_to_file ) {
$self->translate_to_file->openw;
}
else {
my $fh = IO::Handle->new;
if ( $fh->fdopen( fileno(STDOUT), 'w' ) ) {
return $fh;
}
else {
croak "Cannot get a filehandle for STDOUT";
}
}
}
=head2 C<translate_to_file>
The L<Path::Tiny> filename to save the translated file to. If omitted,
then it will be saved to C<STDOUT>.
=cut
has translate_to_file => (
is => 'ro',
isa => File,
coerce => sub { File->coerce(@_) },
lazy => 1,
builder => 'default_readme_file',
);
=head2 C<output_file>
The L<Pod::Readme::Filter> C<output_file> will default to a temporary
file.
=cut
has '+output_file' => (
lazy => 1,
default => sub { tempfile( SUFFIX => '.pod', UNLINK => 1 ); },
);
around '_build_output_fh' => sub {
my ( $orig, $self ) = @_;
if ( defined $self->translation_class ) {
$self->$orig();
}
else {
$self->translate_to_fh;
}
};
=head2 C<force>
For a new F<README> to be generated, even if the dependencies have not
been updated.
See L</dependencies_updated>.
=cut
has 'force' => (
is => 'ro',
isa => Bool,
default => 0,
);
=head2 C<zilla>
For use with L<Dist::Zilla> plugins.
This allows plugins which normally depend on files in the distribution
to use metadata from here instead.
=cut
=head1 METHODS
This module extends L<Pod::Readme::Filter> with the following methods:
=head2 C<default_readme_file>
The default name of the F<README> file, which depends on the
L</translation_class>.
=cut
sub default_readme_file {
my ($self) = @_;
my $name = uc( $self->target );
state $extensions = {
'Pod::Man' => '.1',
'Pod::Markdown' => '.md',
'Pod::Simple::HTML' => '.html',
'Pod::Simple::LaTeX' => '.tex',
'Pod::Simple::RTF' => '.rtf',
'Pod::Simple::Text' => '',
'Pod::Simple::XHTML' => '.xhtml',
};
my $class = $self->translation_class;
if ( defined $class ) {
if ( my $ext = $extensions->{$class} ) {
$name .= $ext;
}
}
else {
$name .= '.pod';
}
path( $self->base_dir, $name );
}
=head2 C<translate_file>
This method runs translates the resulting POD from C<filter_file>.
=cut
sub translate_file {
my ($self) = @_;
if ( my $class = $self->translation_class ) {
load $class;
my $converter = $class->new()
or croak "Cannot instantiate a ${class} object";
if ( $converter->isa('Pod::Simple') ) {
my $tmp_file = $self->output_file->stringify;
close $self->output_fh
or croak "Unable to close file ${tmp_file}";
$converter->output_fh( $self->translate_to_fh );
$converter->parse_file($tmp_file);
}
else {
croak "Don't know how to translate POD using ${class}";
}
}
}
=head2 C<dependencies_updated>
Used to determine when the dependencies have been updated, and a
translation can be run.
Note that this only returns a meaningful value after the POD has been
processed, since plugins may add to the dependencies. A side-effect
of this is that when generating a POD formatted F<README> is that it
will always be updated, even when L</force> is false.
=cut
sub dependencies_updated {
my ($self) = @_;
my $dest = $self->translate_to_file;
if ( $dest and $self->input_file) {
return 1 unless -e $dest;
my $stat = $dest->stat;
return 1 unless $stat;
my $time = $stat->mtime;
return any { $_->mtime > $time } ( map { $_->stat } $self->depends_on );
}
else {
return 1;
}
}
=head2 C<run>
This method runs C<filter_file> and then L</translate_file>.
=cut
around 'run' => sub {
my ( $orig, $self ) = @_;
$self->$orig();
if ( $self->force or $self->dependencies_updated ) {
$self->translate_file();
}
};
=head2 C<parse_from_file>
my $parser = Pod::Readme->new();
$parser->parse_from_file( 'README.pod', 'README' );
Pod::Readme->parse_from_file( 'README.pod', 'README' );
This is a class method that acts as a L<Pod::Select> compatibility
shim for software that is designed for versions of L<Pod::Readme>
prior to v1.0.
Its use is deprecated, and will be deleted in later versions.
=cut
sub parse_from_file {
my ( $self, $source, $dest ) = @_;
my $class = ref($self) || __PACKAGE__;
my $prf = $class->new(
input_file => $source,
translate_to_file => $dest,
translation_class => 'Pod::Simple::Text',
force => 1,
);
$prf->run();
}
=head2 C<parse_from_filehandle>
Like L</parse_from_file>, this exists as a compatibility shim.
Its use is deprecated, and will be deleted in later versions.
=cut
sub parse_from_filehandle {
my ( $self, $source_fh, $dest_fh ) = @_;
my $class = ref($self) || __PACKAGE__;
my $src_io =
IO::Handle->new_from_fd( ( defined $source_fh ) ? fileno($source_fh) : 0,
'r' );
my $dest_io =
IO::Handle->new_from_fd( ( defined $dest_fh ) ? fileno($dest_fh) : 1,
'w' );
my $prf = $class->new(
input_fh => $src_io,
translate_to_fh => $dest_io,
translation_class => 'Pod::Simple::Text',
force => 1,
);
$prf->run();
}
use namespace::autoclean;
1;
=for readme start
=head1 CAVEATS
This module is intended to be used by module authors for their own
modules. It is not recommended for generating F<README> files from
arbitrary Perl modules from untrusted sources.
=head1 SEE ALSO
See L<perlpod>, L<perlpodspec> and L<podlators>.
=head1 AUTHORS
The original version was by Robert Rothenberg <rrwo@cpan.org> until
2010, when maintenance was taken over by David Precious
<davidp@preshweb.co.uk>.
In 2014, Robert Rothenberg rewrote the module to use filtering instead
of subclassing a POD parser.
=head2 Acknowledgements
Thanks to people who gave feedback and suggestions to posts about the
rewrite of this module on L<http://blogs.perl.org>.
=head2 Suggestions, Bug Reporting and Contributing
This module is developed on GitHub at
L<http://github.com/bigpresh/Pod-Readme>
=head1 LICENSE
Copyright (c) 2005-2014 Robert Rothenberg. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=cut