1# Copyright (C) 2004, Parrot Foundation. 2 3=head1 NAME 4 5Parrot::Docs::Item - Documentation item 6 7=head1 SYNOPSIS 8 9 use Parrot::Docs::Item; 10 11=head1 DESCRIPTION 12 13A documentation I<item> is one or more related paths with some optional 14descriptive text. 15 16Directory paths will be expanded to all the file paths within the 17directory and any subdirectories recursively (see the C<files()> method 18in C<Parrot::IO::Directory>). 19 20If an item has more than one file associated with it, and has no text, 21then an attempt will be made to extract short descriptions from each 22file to place under the file path in the index HTML. 23 24=head2 Class Methods 25 26=over 27 28=cut 29 30package Parrot::Docs::Item; 31 32use strict; 33use warnings; 34 35use Parrot::Docs::Directory; 36use Parrot::Docs::POD2HTML; 37use Parrot::Docs::Text2HTML; 38 39=item C<new_item($text, @paths)> 40 41Returns a new item. 42 43Use this when creating items within a C<Parrot::Docs::Section> 44subclass's C<new()> method. 45 46=cut 47 48sub new_item { 49 my $self = shift; 50 51 return Parrot::Docs::Item->new(@_); 52} 53 54=item C<new($text, @contents)> 55 56Returns a new item. If there is no descriptive text then C<$text> should 57be an empty string. 58 59The paths in C<@contents> will be interpreted as being relative to the 60C<$target> argument in C<write_html()>. There should be at least one 61path otherwise an exception is raised. 62 63=cut 64 65sub new { 66 my $self = ref $_[0] ? ref shift : shift; 67 my $text = shift; 68 my @contents = @_; 69 70 die "No contents ($text).\n" unless @contents; 71 72 $self = bless { 73 TEXT => $text, 74 TITLE => $text, 75 CONTENTS => \@contents, 76 }, $self; 77 78 return $self; 79} 80 81=back 82 83=head2 Instance Methods 84 85=over 4 86 87=item C<set_parent($parent)> 88 89=item C<parent()> 90 91Accessors for the containing section/group for the item. 92 93=cut 94 95sub set_parent { 96 my $self = shift; 97 my $group = shift; 98 99 $self->{PARENT} = $group; 100} 101 102sub parent { 103 my $self = shift; 104 105 return $self->{PARENT}; 106} 107 108=item C<html_navigation($path)> 109 110Returns the HTML navigation bar. 111 112=cut 113 114sub html_navigation { 115 my $self = shift; 116 my $path = shift; 117 my $parent = $self->parent || return ''; 118 119 return join ' » ', grep { length } $parent->html_navigation($path), $parent->html_link($path); 120} 121 122=item C<write_html($source, $target, $silent)> 123 124C<$source> is the directory in which the section's contents will be 125looked for. 126 127C<$target> is directory into which the section's output will be written. 128 129If C<$silent> is true then progress is not reported. 130 131Any POD-formatted text in the item's files is converted to HTML and 132written to a file in C<$target> and an HTML link is created to it. 133 134Alternatively, if a file responds true to C<is_docs_link()> then an HTML 135link is created to the file itself. 136 137Some HTML-formatted text describing the files linked to is returned. 138 139=cut 140 141sub write_html { 142 my $self = shift; 143 my $source = shift || die "No source\n"; 144 my $target = shift || die "No target\n"; 145 my $silent = shift || 0; 146 my $index_html = ''; 147 my @rel_paths = $self->contents_relative_to_source($source); 148 my @short_desc = (); 149 150 foreach my $rel_path (@rel_paths) { 151 my $file = $source->file_with_relative_path($rel_path); 152 153 if ( $file->contains_pod ) { 154 print "\n", $rel_path unless $silent; 155 156 my $formatter = Parrot::Docs::POD2HTML->new; 157 $formatter->no_errata_section(1); # don't dump errors into HTML output 158 $formatter->{TESTING} = 1 if $self->{TESTING}; 159 $formatter->write_html( $source, $target, $rel_path, $self ); 160 161 my $title = $self->{TITLE} || $file->short_description; 162 163 if ($title) { 164 $index_html .= $formatter->html_link( $formatter->append_html_suffix($rel_path), 165 $title ); 166 } 167 else { 168 $index_html .= $formatter->html_link( $formatter->append_html_suffix($rel_path), 169 $source->relative_path( $file->path ) ); 170 } 171 172 $index_html .= "<br>\n"; 173 174 next if $self->{TEXT}; 175 176 my $short_desc = $file->short_description; 177 178 next unless $short_desc; 179 180 next if grep { $_ eq $short_desc } @short_desc; 181 182 push @short_desc, $short_desc; 183 } 184 elsif ( $file->is_docs_link ) { 185 print "\n", $rel_path unless $silent; 186 187 my $formatter = Parrot::Docs::Text2HTML->new; 188 $formatter->write_html( $source, $target, $rel_path, $self ); 189 190 $index_html .= $formatter->html_link( $formatter->append_html_suffix($rel_path), 191 $source->relative_path( $file->path ) ); 192 193 } 194 } 195 196 return '' unless $index_html; 197 198 if ( $self->{DESCRIPTION} ) { 199 $index_html .= "<br>$self->{DESCRIPTION}\n"; 200 } 201 202 $index_html = '<li>' . $index_html . "</li>\n"; 203 204 return $index_html; 205} 206 207=item C<contents_relative_to_source($source)> 208 209Returns the contents of the item interpreted relative to the source 210directory. 211 212=cut 213 214sub contents_relative_to_source { 215 my $self = shift; 216 my $source = shift; 217 my @contents = (); 218 219 foreach my $content ( @{ $self->{CONTENTS} } ) { 220 push @contents, $self->file_paths_relative_to_source( $source, $content ); 221 222 } 223 224 return @contents; 225} 226 227=item C<file_paths_relative_to_source($source, $path)> 228 229If C<$path> is an immediate subdirectory of C<$source>, then this method 230returns all the file paths within the directory and any subdirectories 231recursively, relative to C<$source>. 232 233If C<$path> is a file in C<$source> then C<$path> is returned. 234 235If C<$path> cannot be found then a warning is printed. 236 237=cut 238 239sub file_paths_relative_to_source { 240 my $self = shift; 241 my $source = shift; 242 my $rel_path = shift; 243 my @rel_paths = (); 244 245 if ( $source->relative_path_is_directory($rel_path) ) { 246 my $dir = $source->directory_with_relative_path($rel_path); 247 248 # There may be editor scratch files to ignore. 249 250 foreach my $file ( $dir->files( 1, '^\.' ) ) { 251 push @rel_paths, $source->relative_path( $file->path ); 252 } 253 } 254 elsif ( $source->relative_path_is_file($rel_path) ) { 255 push @rel_paths, $rel_path; 256 } 257 else { 258 warn "Failed to process $rel_path.\n"; 259 } 260 261 return @rel_paths; 262} 263 264=back 265 266=head1 SEE ALSO 267 268=over 4 269 270=item C<Parrot::Docs::Section> 271 272=item C<Parrot::Docs::Group> 273 274=back 275 276=cut 277 2781; 279 280# Local Variables: 281# mode: cperl 282# cperl-indent-level: 4 283# fill-column: 100 284# End: 285# vim: expandtab shiftwidth=4: 286