1package Module::Install::ReadmeFromPod;
2
3use 5.006;
4use strict;
5use warnings;
6use base qw(Module::Install::Base);
7use vars qw($VERSION);
8
9$VERSION = '0.30';
10
11{
12
13    # these aren't defined until after _require_admin is run, so
14    # define them so prototypes are available during compilation.
15    sub io;
16    sub capture(&;@);
17
18=pod
19
20=begin quiet_pod_coverage
21
22=head2 io
23
24=head2 capture
25
26=end quiet_pod_coverage
27
28=cut
29
30    my $done = 0;
31
32    sub _require_admin {
33
34	# do this once to avoid redefinition warnings from IO::All
35	return if $done;
36
37	require IO::All;
38	IO::All->import( '-binary' );
39
40	require Capture::Tiny;
41	Capture::Tiny->import ( 'capture' );
42
43	return;
44    }
45
46}
47
48sub readme_from {
49  my $self = shift;
50  return unless $self->is_admin;
51
52  _require_admin;
53
54  # Input file
55  my $in_file  = shift || $self->_all_from
56    or die "Can't determine file to make readme_from";
57
58  # Get optional arguments
59  my ($clean, $format, $out_file, $options);
60  my $args = shift;
61  if ( ref $args ) {
62    # Arguments are in a hashref
63    if ( ref($args) ne 'HASH' ) {
64      die "Expected a hashref but got a ".ref($args)."\n";
65    } else {
66      $clean    = $args->{'clean'};
67      $format   = $args->{'format'};
68      $out_file = $args->{'output_file'};
69      $options  = $args->{'options'};
70    }
71  } else {
72    # Arguments are in a list
73    $clean    = $args;
74    $format   = shift;
75    $out_file = shift;
76    $options  = \@_;
77  }
78
79  # Default values;
80  $clean  ||= 0;
81  $format ||= 'txt';
82
83  # Generate README
84  print "readme_from $in_file to $format\n";
85  if ($format =~ m/te?xt/) {
86    $out_file = $self->_readme_txt($in_file, $out_file, $options);
87  } elsif ($format =~ m/html?/) {
88    $out_file = $self->_readme_htm($in_file, $out_file, $options);
89  } elsif ($format eq 'man') {
90    $out_file = $self->_readme_man($in_file, $out_file, $options);
91  } elsif ($format eq 'md') {
92    $out_file = $self->_readme_md($in_file, $out_file, $options);
93  } elsif ($format eq 'pdf') {
94    $out_file = $self->_readme_pdf($in_file, $out_file, $options);
95  }
96
97  if ($clean) {
98    $self->clean_files($out_file);
99  }
100
101  return 1;
102}
103
104
105sub _readme_txt {
106  my ($self, $in_file, $out_file, $options) = @_;
107  $out_file ||= 'README';
108  require Pod::Text;
109  my $parser = Pod::Text->new( @$options );
110  my $io = io->file($out_file)->open(">");
111  my $out_fh = $io->io_handle;
112  $parser->output_fh( *$out_fh );
113  $parser->parse_file( $in_file );
114  return $out_file;
115}
116
117
118sub _readme_htm {
119  my ($self, $in_file, $out_file, $options) = @_;
120  $out_file ||= 'README.htm';
121  require Pod::Html;
122  my ($o) = capture {
123    Pod::Html::pod2html(
124      "--infile=$in_file",
125      "--outfile=-",
126      @$options,
127    );
128  };
129  io->file($out_file)->print($o);
130  # Remove temporary files if needed
131  for my $file ('pod2htmd.tmp', 'pod2htmi.tmp') {
132    if (-e $file) {
133      unlink $file or warn "Warning: Could not remove file '$file'.\n$!\n";
134    }
135  }
136  return $out_file;
137}
138
139
140sub _readme_man {
141  my ($self, $in_file, $out_file, $options) = @_;
142  $out_file ||= 'README.1';
143  require Pod::Man;
144  my $parser = Pod::Man->new( @$options );
145  my $io = io->file($out_file)->open(">");
146  my $out_fh = $io->io_handle;
147  $parser->output_fh( *$out_fh );
148  $parser->parse_file( $in_file );
149  return $out_file;
150}
151
152
153sub _readme_pdf {
154  my ($self, $in_file, $out_file, $options) = @_;
155  $out_file ||= 'README.pdf';
156  eval { require App::pod2pdf; }
157    or die "Could not generate $out_file because pod2pdf could not be found\n";
158  my $parser = App::pod2pdf->new( @$options );
159  $parser->parse_from_file($in_file);
160  my ($o) = capture { $parser->output };
161  io->file($out_file)->print($o);
162  return $out_file;
163}
164
165sub _readme_md {
166  my ($self, $in_file, $out_file, $options) = @_;
167  $out_file ||= 'README.md';
168  require Pod::Markdown;
169  my $parser = Pod::Markdown->new( @$options );
170  my $io = io->file($out_file)->open(">");
171  my $out_fh = $io->io_handle;
172  $parser->output_fh( *$out_fh );
173  $parser->parse_file( $in_file );
174  return $out_file;
175}
176
177
178sub _all_from {
179  my $self = shift;
180  return unless $self->admin->{extensions};
181  my ($metadata) = grep {
182    ref($_) eq 'Module::Install::Metadata';
183  } @{$self->admin->{extensions}};
184  return unless $metadata;
185  return $metadata->{values}{all_from} || '';
186}
187
188'Readme!';
189
190__END__
191
192=head1 NAME
193
194Module::Install::ReadmeFromPod - A Module::Install extension to automatically convert POD to a README
195
196=head1 SYNOPSIS
197
198  # In Makefile.PL
199
200  use inc::Module::Install;
201  author 'Vestan Pants';
202  license 'perl';
203  readme_from 'lib/Some/Module.pm';
204  readme_from 'lib/Some/Module.pm', { clean => 1, format => 'htm', output_file => 'SomeModule.html' };
205
206A C<README> file will be generated from the POD of the indicated module file.
207
208Note that the author will need to make sure
209C<Module::Install::ReadmeFromPod> is installed
210before running the C<Makefile.PL>.  (The extension will be bundled
211into the user-side distribution).
212
213=head1 DESCRIPTION
214
215Module::Install::ReadmeFromPod is a L<Module::Install> extension that generates
216a C<README> file automatically from an indicated file containing POD, whenever
217the author runs C<Makefile.PL>. Several output formats are supported: plain-text,
218HTML, PDF or manpage.
219
220=head1 COMMANDS
221
222This plugin adds the following Module::Install command:
223
224=over
225
226=item C<readme_from>
227
228Does nothing on the user-side. On the author-side it will generate a C<README>
229file.
230
231  readme_from 'lib/Some/Module.pm';
232
233If a second parameter is set to a true value then the C<README> will be removed at C<make distclean>.
234
235  readme_from 'lib/Some/Module.pm', 1;
236
237A third parameter can be used to determine the format of the C<README> file.
238
239  readme_from 'lib/Some/Module.pm', 1, 'htm';
240
241Valid formats for this third parameter are:
242
243=over
244
245=item txt, text
246
247Produce a plain-text C<README> file using L<Pod::Text>. The 'txt' format is the
248default.
249
250=item htm, html
251
252Produce an HTML C<README.htm> file using L<Pod::Html>.
253
254=item man
255
256Produce a C<README.1> manpage using L<Pod::Man>.
257
258=item md
259
260Produce a C<README.md> file using L<Pod::Markdown>.
261
262=item pdf
263
264Produce a PDF C<README.pdf> file with L<App::pod2pdf> if this module is installed.
265
266=back
267
268A fourth parameter can be used to supply an output filename.
269
270  readme_from 'lib/Some/Module.pm', 0, 'pdf', 'SomeModule.pdf';
271
272Finally, you can pass additional arguments to the POD formatter that handles the
273requested format.
274
275  my @options = ( 'release' => 1.03, 'section' => 8 ); # options for Pod::Man
276  readme_from 'lib/Some/Module.pm', 1, 'man', undef, @options;
277
278But instead of passing this long list of optional arguments to readme_from, you
279should probably pass these arguments as a named hashref for clarity.
280
281  my @options = ( 'release' => 1.03, 'section' => 8 );
282  readme_from 'lib/Some/Module.pm', {clean => 1, format => 'man', output_file => undef, options => @options};
283
284If you use the C<all_from> command, C<readme_from> will default to that value.
285
286  all_from 'lib/Some/Module.pm';
287  readme_from;              # Create README from lib/Some/Module.pm
288  readme_from '','clean';   # Put a empty string before 'clean'
289
290=back
291
292=head1 AUTHOR
293
294Chris C<BinGOs> Williams
295
296=head1 LICENSE
297
298Copyright E<copy> Chris Williams
299
300This module may be used, modified, and distributed under the same terms as Perl itself. Please see the license that came with your Perl distribution for details.
301
302=head1 SEE ALSO
303
304L<Module::Install>
305
306L<Pod::Text>
307
308L<Pod::Html>
309
310L<Pod::Man>
311
312L<Pod::Markdown>
313
314L<App::pod2pdf>
315
316=cut
317
318