File Coverage

script/podify.pl
Criterion Covered Total %
statement 74 82 90.2
branch 36 48 75.0
condition 17 26 65.3
subroutine 11 12 91.6
pod n/a
total 138 168 82.1


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2 4     4   2955 use Applify;
  4         9665  
  4         14  
3              
4 4     4   1164 use File::Find;
  4         4  
  4         173  
5 4     4   13 use File::Spec;
  4         4  
  4         90  
6              
7 4     4   1994 use if -e '.ship.conf', lib => 'lib';
  4         28  
  4         44  
8              
9             option bool => i => 'Replace source', 0;
10             option bool => recursive => 'Recurse into directories in source list',
11             0, (alias => 'r');
12             option str => eopm => 'End of perl module marker', '^1;';
13              
14             documentation 'App::podify';
15              
16             sub check_pod {
17 0     0   0 my $self = shift;
18              
19 0         0 for my $section (qw(attrs subs)) {
20 0 0       0 for my $name (keys %{$self->{$section} || {}}) {
  0         0  
21 0         0 warn "Missing $section in pod: $name\n";
22             }
23             }
24              
25 0         0 return $self;
26             }
27              
28             sub find_files {
29 2     2   8436 my ($self, $path) = @_;
30 2         2 my @files;
31              
32             File::Find::find(
33             {
34             no_chdir => 1,
35             wanted => sub {
36 19 100 100 19   118 $File::Find::prune = 1 if -d $File::Find::name and !$self->recursive;
37 19 100       45 $File::Find::prune = 0 if $File::Find::name eq $path;
38 19 100       29 return if $File::Find::prune;
39 18 100       264 push @files, $File::Find::name if $File::Find::name =~ m/[.] pm \z/msx;
40             }
41             },
42 2         123 $path
43             );
44              
45 2         12 return sort { length $a <=> length $b } @files;
  4         16  
46             }
47              
48             sub generate {
49 3     3   2253 my ($self, $OUT) = @_;
50              
51 3 100       5 $self->{pod} = $self->pod_template unless @{$self->{pod}};
  3         14  
52              
53 3 50       10 if ($self->i) {
    50          
54 0 0       0 open $OUT, '>', $self->{perl_module} or die "Write $self->{perl_module}: $!\n";
55             }
56             elsif (!$OUT) {
57 0         0 $OUT = \*STDOUT;
58             }
59              
60 3         21 my $code = join '', @{$self->{code}};
  3         20  
61 3         11 $code =~ s!\n\n\n!\n\n!g;
62 3         14 $code =~ s!\n+use!\nuse!s;
63 3         23 $code =~ s!\n+$!\n\n!;
64              
65 3         9 print $OUT "${code}1;\n\n";
66 3 50       12 print $OUT "=encoding utf8\n\n" unless $self->{pod_has_encoding};
67 3         4 print $OUT $_ for grep { !/^=cut/ } @{$self->{pod}};
  71         84  
  3         19  
68 3         4 print $OUT "=cut\n";
69 3 100       3 print $OUT "\n" . join '', @{$self->{data}} if @{$self->{data}};
  1         3  
  3         12  
70             }
71              
72             sub init {
73 3     3   26274 my $self = shift;
74 3         22 $self->{$_} = [] for qw(code data pod);
75 3         11 $self->{$_} = {} for qw(attrs subs);
76 3         6 $self;
77             }
78              
79             sub parse {
80 3     3   9 my $self = shift;
81 3         7 my $eopm = $self->eopm;
82 3         13 my %has;
83              
84 3 50       127 open my $IN, '<', $self->{perl_module} or die "Read $self->{perl_module}: $!\n";
85 3         38 $eopm = qr{$eopm};
86              
87 3         35 while (<$IN>) {
88 106         60 my $pod;
89 106 100       132 next if /^=encoding\s/;
90 105 100       123 $self->{attrs}{$1} = $1 if /^has\s+([a-z]\w*)/;
91 105 100       122 $self->{subs}{$1} = $1 if /^sub\s+([a-z]\w*)/;
92 105 100       120 $self->{documented}{$1} = $1 if /^=head2\s([a-z]\w*)/;
93 105 100 33     139 $self->{module_name} ||= $1 if /^package\s+([^\s;]+)/;
94 105 50 0     113 $self->{module_version} ||= $1 if /^VERSION.*([\d\.]+)/;
95 105 100       149 $pod = push @{$self->{pod}}, $_ if /^=head/ .. /=cut/;
  61         67  
96 105 100 100     59 push @{$self->{data}}, $_ if @{$self->{data}} or /^__DATA__$/;
  2         3  
  105         284  
97 105 100 100     62 push @{$self->{code}}, $_ unless @{$self->{data}} or $pod or $_ =~ $eopm;
  39   100     99  
  105         421  
98             }
99              
100 3         30 return $self;
101             }
102              
103             sub pod_template {
104 1     1   2 my $self = shift;
105              
106             return [
107             sprintf("=head1 NAME\n\n%s - TODO\n\n", $self->{module_name} || 'Unknown'),
108             $self->{module_version} ? printf("=head1 VERSION\n\n$%s\n\n", $self->{module_version}) : (),
109             sprintf("=head1 SYNOPSIS\n\nTODO\n\n"),
110             sprintf("=head1 DESCRIPTION\n\nTODO\n\n"),
111             sprintf("=head1 ATTRIBUTES\n\n"),
112 1 50       3 map({ sprintf "=head2 %s\n\n", delete $self->{attrs}{$_} } sort keys %{$self->{attrs} || {}}),
  1         5  
113             sprintf("=head1 METHODS\n\n"),
114 1 50       7 map({ sprintf "=head2 %s\n\n", delete $self->{subs}{$_} } sort keys %{$self->{subs} || {}}),
  1         6  
115 1 50 50     6 sprintf("=head1 AUTHOR\n\n%s\n\n", $ENV{PODIFY_AUTHOR} || (getpwuid $<)[6] || (getpwuid $<)[0]),
      33        
116             sprintf("=head1 COPYRIGHT AND LICENSE\n\nTODO\n\n"),
117             sprintf("=head1 SEE ALSO\n\nTODO\n\n"),
118             ];
119             }
120              
121             sub post_process {
122 3     3   849 my $self = shift;
123 3   66     3 delete $self->{attrs}{$_} or delete $self->{subs}{$_} for keys %{$self->{documented}};
  3         19  
124             }
125              
126             app {
127             my ($self, @paths) = @_;
128              
129             unless (@paths) {
130             die $self->_script->print_help, "No input files specified.\n";
131             }
132              
133             while (my $path = File::Spec->canonpath(shift @paths)) {
134             if (-d $path) {
135             push @paths, $self->find_files($path);
136             }
137             else {
138             $self->init;
139             $self->{perl_module} = $path;
140             $self->parse;
141             $self->post_process;
142             $self->generate;
143             $self->check_pod;
144             }
145             }
146              
147             return 0;
148             };