File Coverage

blib/lib/Pod/HTMLEmbed/Entry.pm
Criterion Covered Total %
statement 100 136 73.5
branch 12 38 31.5
condition 8 21 38.1
subroutine 20 26 76.9
pod 3 3 100.0
total 143 224 63.8


line stmt bran cond sub pod time code
1             package Pod::HTMLEmbed::Entry;
2 4     4   22 use Any::Moose;
  4         8  
  4         32  
3              
4 4     4   2139 use Carp::Clan '^(Mo[ou]se::|Pod::HTMLEmbed(::)?)';
  4         8  
  4         29  
5 4     4   5499 use Pod::Simple::XHTML;
  4         201527  
  4         180  
6 4     4   5703 use HTML::TreeBuilder;
  4         108824  
  4         48  
7 4     4   4249 use URI::Escape ();
  4         5377  
  4         95  
8 4     4   27 use HTML::Entities ();
  4         10  
  4         90  
9 4     4   20 use List::Util qw/min/;
  4         7  
  4         857  
10              
11             has file => (
12             is => 'ro',
13             isa => 'Str',
14             required => 1,
15             );
16              
17             has [qw/name title body toc/] => (
18             is => 'ro',
19             isa => 'Str',
20             lazy_build => 1,
21             );
22              
23             has _tree => (
24             is => 'ro',
25             isa => 'HTML::TreeBuilder',
26             lazy_build => 1,
27             );
28              
29             has _context => (
30             is => 'ro',
31             isa => 'Pod::HTMLEmbed',
32             required => 1,
33             handles => ['url_prefix', 'has_url_prefix'],
34             );
35              
36 4     4   21 no Any::Moose;
  4         28  
  4         41  
37              
38             sub sections {
39 2     2 1 5 my $self = shift;
40 2         22 map { $_->content_list } $self->_tree->find('h2');
  8         329  
41             }
42              
43             sub section {
44 16     16 1 2540 my ($self, $section_name) = @_;
45 16 50       48 croak 'section_name is required' unless $section_name;
46              
47             my $section = $self->_tree->look_down(
48             _tag => 'h2',
49 55     55   2457 sub { $_[0]->content->[0] eq $section_name },
50 16         125 );
51              
52 16         182 my $content = q[];
53 16   33     85 while ($section and $section = $section->right and $section->tag ne 'h2') {
      66        
54 16         784 $content .= $section->as_XML . "\n";
55             }
56              
57 16         6031 $content;
58             }
59              
60             sub DEMOLISH {
61 3     3 1 746 my $self = shift;
62              
63 3 50       22 if ($self->_has_tree) {
64 3         24 $self->_tree->delete;
65             }
66             }
67              
68             sub _build_name {
69 2     2   1527 my $self = shift;
70              
71 2         10 (my $name = $self->section('NAME')) =~ s/\s*-.*$//s;
72 2         10 $name =~ s/<.*?>//gs;
73 2         33 $name;
74             }
75              
76             sub _build_title {
77 2     2   5 my $self = shift;
78              
79 2 50       10 my ($title) = $self->section('NAME') =~ / - (.*)/ or return '';
80 2         9 $title =~ s/<.*?>//g;
81 2         17 $title;
82             }
83              
84             sub _build_body {
85 0     0   0 my $self = shift;
86              
87 0         0 my $body = q[];
88 0         0 for my $content ($self->_tree->find('body')->content_list) {
89 0         0 $body .= $content->as_XML . "\n";
90             }
91 0         0 $body;
92             }
93              
94             sub _build_toc {
95 0     0   0 my $self = shift;
96              
97 0         0 my $toc = '
    ';
98              
99 0         0 for my $section ($self->sections) {
100 0         0 $toc .= '
  • ' . $self->_section_link($section);
  • 101 0         0 $toc .= $self->_toc_in_section($section);
    102 0         0 $toc .= '';
    103             }
    104 0         0 $toc .= '';
    105              
    106 0         0 $toc;
    107             }
    108              
    109             sub _section_link {
    110 0     0   0 my ($self, $section) = @_;
    111              
    112 0 0       0 unless (ref $section) {
    113             $section = $self->_tree->look_down(
    114 0     0   0 _tag => 'h2', sub { $_[0]->content->[0] eq $section },
    115 0 0       0 ) or return;
    116             }
    117              
    118 0         0 my $text = HTML::Entities::encode_entities($section->as_trimmed_text, '<>&"');
    119 0         0 my $escaped_text = URI::Escape::uri_escape($section->as_trimmed_text);
    120              
    121 0         0 return join '', '', $text, '';
    122             }
    123              
    124             sub _toc_in_section {
    125 0     0   0 my ($self, $section_name) = @_;
    126              
    127             my $section = $self->_tree->look_down(
    128             _tag => 'h2',
    129 0     0   0 sub { $_[0]->content->[0] eq $section_name },
    130 0 0       0 ) or return;
    131              
    132 0         0 my $toc = q[];
    133 0         0 my $num = 2;
    134              
    135 0   0     0 while ($section and $section = $section->right and $section->tag ne 'h2') {
          0        
    136 0 0       0 next unless $section->tag =~ /^h[2-6]$/;
    137 0         0 my ($n) = $section->tag =~ /(\d)/;
    138              
    139 0 0       0 if ($num < $n) {
        0          
    140 0         0 $toc .= join '', '
    • ', $self->_section_link($section);
    141             }
    142             elsif ($num == $n) {
    143 0         0 $toc .= join '', '
  • ', $self->_section_link($section);
  • 144             }
    145             else {
    146 0         0 $toc .= join '', '
  • ', $self->_section_link($section);
  • 147             }
    148              
    149 0         0 $num = $n;
    150             }
    151 0 0       0 return '' unless $toc;
    152              
    153 0         0 $toc .= '' while $num-- > 2;
    154 0         0 $toc;
    155             }
    156              
    157             sub _build__tree {
    158 3     3   6 my $self = shift;
    159              
    160 3         17 my $html = $self->_parse_pod($self->file);
    161              
    162 3         32 my $tree = HTML::TreeBuilder->new;
    163 3         905 $tree->parse_content( $html );
    164              
    165             # remove white spaces in codeblocks
    166 3     7   26980 my @codes = $tree->look_down( _tag => 'code', sub { $_[0]->parent->tag eq 'pre' } );
      7         701  
    167 3         124 $self->_strip_tree($_) for @codes;
    168              
    169             # remove first num strings in
      tag
    170 3     12   66 my @list = $tree->look_down( _tag => 'li', sub { $_[0]->parent->tag eq 'ol' } );
      12         480  
    171 3         522 for my $li (@list) {
    172 4 50       50 my $first_child = shift @{ $li->content_array_ref } or next;
      4         10  
    173 4 50       21 $first_child =~ s/^\d+\.\s+// unless ref $first_child;
    174 4         11 $li->unshift_content($first_child);
    175             }
    176              
    177             # remove first

    from lists

    178 3         27 @list = $tree->look_down( _tag => 'li' );
    179 3         707 for my $li (@list) {
    180 12         141 my @children = $li->content_list;
    181 12         52 my $num_element = grep { ref $_ } @children;
      16         23  
    182              
    183 12 50 66     55 if (1 == $num_element and my $p = $children[0] and $children[0]->tag eq 'p') {
          66        
    184 8         57 $p->replace_with_content;
    185             }
    186             }
    187              
    188             # shift header level, and add id attr
    189 3         30 my @header = $tree->look_down( _tag => qr/^h[1-5]$/ );
    190 3         875 for my $header (@header) {
    191 19         684 my ($n) = $header->tag =~ /(\d)/;
    192 19         168 $header->tag( 'h' . ++$n );
    193 19         158 $header->attr( id => $header->as_trimmed_text );
    194             }
    195              
    196 3         173 $tree;
    197             }
    198              
    199             sub _parse_pod {
    200 3     3   7 my ($self, $file) = @_;
    201              
    202 3         30 my $p = Pod::Simple::XHTML->new;
    203 3         505 $p->html_header('');
    204 3         28 $p->html_footer('');
    205              
    206 3         39 $p->output_string(\my $html);
    207 3 50       7294 $p->perldoc_url_prefix( $self->url_prefix ) if $self->has_url_prefix;
    208              
    209 3 50       84 $p->parse_file($file)
    210             or croak "Pod parse error: $!";
    211              
    212 3         18792 $html;
    213             }
    214              
    215             sub _strip_tree {
    216 4     4   19 my ($self, $code) = @_;
    217 4         20 my $stripped = _strip($code->content_list);
    218 4 50       27 $stripped .= "\n" unless $stripped =~ /\n$/;
    219              
    220 4         19 $code->delete_content;
    221 4         76 $code->push_content($stripped);
    222             }
    223              
    224             # copy from String::TT::strip
    225             sub _strip($){
    226 4     4   31 my $lines = shift;
    227              
    228 4         12 my $trailing_newline = ($lines =~ /\n$/s);# perl silently throws away data
    229 4         17 my @lines = split "\n", $lines;
    230 4 50       36 shift @lines if $lines[0] eq ''; # strip empty leading line
    231              
    232             # determine indentation level
    233 4 50 33     9 my @spaces = map { /^(\040+)/ and length $1 or 0 } grep { !/^\s*$/ } @lines;
      4         51  
      4         22  
    234              
    235 4         38 my $indentation_level = min(@spaces);
    236              
    237             # strip off $indentation_level spaces
    238 4         8 my $stripped = join "\n", map {
    239 4         10 my $copy = $_;
    240 4         10 substr($copy,0,$indentation_level) = "";
    241 4         14 $copy;
    242             } @lines;
    243              
    244 4 50       14 $stripped .= "\n" if $trailing_newline;
    245 4         11 return $stripped;
    246             }
    247              
    248             __PACKAGE__->meta->make_immutable;
    249              
    250             __END__