App-DocKnot

 view release on metacpan or  search on metacpan

lib/App/DocKnot/Spin.pm  view on Meta::CPAN

        $in_tree = 1;
    }

    # Add the end-of-page navbar if we have sitemap information.
    if ($self->{sitemap} && $self->{output}) {
        my $page = $out_path->relative($self->{output});
        $output .= join(q{}, $self->{sitemap}->navbar($page)) . "\n";
    }

    # Figure out the modification dates.  Use the RCS/CVS Id if available,
    # otherwise use the Git repository if available.
    my $modified;
    if (defined($id)) {
        my (undef, undef, $date) = split(q{ }, $id);
        if ($date && $date =~ m{ \A (\d+) [-/] (\d+) [-/] (\d+) }xms) {
            $modified = sprintf('%d-%02d-%02d', $1, $2, $3);
        }
    } elsif ($self->{repository} && $in_tree) {
        $modified = $self->{repository}->run(
            'log', '-1', '--format=%ct', "$source",
        );
        if ($modified) {
            $modified = strftime('%Y-%m-%d', gmtime($modified));
        }
    }
    if (!$modified) {
        $modified = strftime('%Y-%m-%d', gmtime($source->stat()->[9]));
    }
    my $now = strftime('%Y-%m-%d', gmtime());

    # Determine which template to use and substitute in the appropriate times.
    $output .= "<address>\n" . q{ } x 4;
    my $template = ($modified eq $now) ? $templates[0] : $templates[1];
    $template =~ s{ %MOD% }{$modified}xmsg;
    $template =~ s{ %NOW% }{$now}xmsg;
    $template =~ s{ %URL% }{$URL}xmsg;
    $output .= "$template\n";
    $output .= "</address>\n";

    return $output;
}

##############################################################################
# External converters
##############################################################################

# Given the output from a converter, the file to save the output in, and an
# anonymous sub that takes three arguments, the first being the captured
# blurb, the second being the document ID if found, and the third being the
# base name of the output file, and prints out a last modified line, reformat
# the output of an external converter.
sub _write_converter_output {
    my ($self, $page_ref, $output, $footer) = @_;
    my $page = $output->relative($self->{output});
    my $out_fh = $output->openw_utf8();

    # Grab the first few lines of input, looking for a blurb and Id string.
    # Give up if we encounter <body> first.  Also look for a </head> tag and
    # add the navigation link tags before it, if applicable.  Add the
    # navigation bar right at the beginning of the body.
    my ($blurb, $docid);
    while (defined(my $line = shift($page_ref->@*))) {
        if ($line =~ m{ <!-- \s* (\$Id.*?) \s* --> }xms) {
            $docid = $1;
        }
        if ($line =~ m{ <!-- \s* ( (?:Generated|Converted) .*? )\s* --> }xms) {
            $blurb = $1;

            # Only show the date of the output, not the time or time zone.
            $blurb =~ s{ [ ] \d\d:\d\d:\d\d [ ] -0000 }{}xms;

            # Strip the date from the converter version output.
            $blurb =~ s{ [ ] [(] \d{4}-\d\d-\d\d [)] }{}xms;
        }
        if ($self->{sitemap} && $line =~ m{ \A </head> }xmsi) {
            my @links = $self->{sitemap}->links($page);
            if (@links) {
                print_fh($out_fh, $output, @links);
            }
        }
        print_fh($out_fh, $output, $line);
        if ($line =~ m{ <body }xmsi) {
            if ($self->{sitemap}) {
                my @navbar = $self->{sitemap}->navbar($page);
                if (@navbar) {
                    print_fh($out_fh, $output, @navbar);
                }
            }
            last;
        }
    }
    warn "$0 spin: malformed HTML output for $output\n" unless $page_ref->@*;

    # Snarf input and write it to output until we see </body>, which is our
    # signal to start adding things.  We just got very confused if </body> was
    # on the same line as <body>, so don't do that.
    my $line;
    while (defined($line = shift($page_ref->@*))) {
        last if $line =~ m{ </body> }xmsi;
        print_fh($out_fh, $output, $line);
    }

    # Add the footer and finish with the output.
    print_fh($out_fh, $output, $footer->($blurb, $docid));
    if (defined($line)) {
        print_fh($out_fh, $output, $line, $page_ref->@*);
    }
    close($out_fh);
    return;
}

# These methods are all used, but are indirected through a table, so
# perlcritic gets confused.
#
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)

# A wrapper around the cl2xhtml script, used to handle .changelog pointers in
# a tree being spun.  Adds the navigation links and the signature to the
# cl2xhtml output.
sub _cl2xhtml {
    my ($self, $source, $output, $options, $style) = @_;
    $style ||= $self->{style_url} . 'changelog.css';
    my @page = capture("cl2xhtml $options -s $style $source");
    @page = map { decode('utf-8', $_) } @page;
    my $footer = sub {
        my ($blurb, $id) = @_;
        if ($blurb) {
            $blurb =~ s{ cl2xhtml }{\n<a href="$URL">cl2xhtml</a>}xms;
        }
        $self->_footer($source, $output, $id, $blurb, $blurb);
    };
    $self->_write_converter_output(\@page, $output, $footer);
    return;
}

# A wrapper around the cvs2xhtml script, used to handle .log pointers in a
# tree being spun.  Adds the navigation links and the signature to the
# cvs2xhtml output.
sub _cvs2xhtml {
    my ($self, $source, $output, $options, $style) = @_;
    $style ||= $self->{style_url} . 'cvs.css';

    # Separate the source file into a directory and filename.
    my $name = $source->basename();
    my $dir = $source->parent();

    # Construct the options to cvs2xhtml.
    if ($options !~ m{ -n [ ] }xms) {
        $options .= " -n $name";
    }
    $options .= " -s $style";

    # Run the converter and write the output.
    my @page = capture("(cd $dir && cvs log $name) | cvs2xhtml $options");
    @page = map { decode('utf-8', $_) } @page;
    my $footer = sub {
        my ($blurb, $id, $file) = @_;
        if ($blurb) {
            $blurb =~ s{ cvs2xhtml }{\n<a href="$URL">cvs2xhtml</a>}xms;
        }
        $self->_footer($source, $output, $id, $blurb, $blurb);
    };
    $self->_write_converter_output(\@page, $output, $footer);
    return;

lib/App/DocKnot/Spin.pm  view on Meta::CPAN

##############################################################################

# Given a pointer file, read the master file name and any options, returning
# them as a list with the newlines chomped off.
#
# $file - Path::Tiny for the file to read
#
# Returns: List of the master file, any command-line options, and the style
#          sheet to use, as strings
#  Throws: Text exception if no master file is present in the pointer
#          autodie exception if the pointer file could not be read
sub _read_pointer {
    my ($self, $file) = @_;

    # Read the pointer file.
    my ($master, $options, $style) = $file->lines_utf8();

    # Clean up the contents.
    if (!$master) {
        die "no master file specified in $file\n";
    }
    chomp($master);
    if (defined($options)) {
        chomp($options);
    } else {
        $options = q{};
    }
    if (defined($style)) {
        chomp($style);
    }

    # Return the details.
    return (path($master), $options, $style);
}

# Convert an input path to an output path.
#
# $input     - Path::Tiny input path
# $extension - If given, remove this extension and add .html in its place
sub _output_for_file {
    my ($self, $input, $extension) = @_;
    my $output = $input->relative($self->{source})->absolute($self->{output});
    if ($extension) {
        my $output_file = $input->basename($extension) . '.html';
        $output = $output->sibling($output_file);
    }
    return $output;
}

# Report an action to standard output.
#
# $action - String description of the action
# $output - Output file generated
sub _report_action {
    my ($self, $action, $output) = @_;
    my $shortout = $output->relative($self->{output});
    print_checked("$action .../$shortout\n");
    return;
}

# This routine is called for every file in the source tree.  It decides what
# to do with each file, whether spinning it or copying it.
#
# $input - Path::Tiny path to the input file
#
# Throws: Text exception on any processing error
#         autodie exception if files could not be accessed or written
sub _process_file {
    my ($self, $input) = @_;

    # Conversion rules for pointers.  The key is the extension, the first
    # value is the name of the command for the purposes of output, and the
    # second is the name of the method to run.
    #<<<
    my %rules = (
        changelog => ['cl2xhtml',   '_cl2xhtml'],
        faq       => ['faq2html',   '_faq2html'],
        log       => ['cvs2xhtml',  '_cvs2xhtml'],
        rpod      => ['pod2thread', '_pod2html'],
    );
    #>>>

    # Figure out what to do with the input.
    if ($input->is_dir()) {
        my $output = $self->_output_for_file($input);
        $self->{generated}{"$output"} = 1;
        if ($output->exists() && !$output->is_dir()) {
            die "cannot replace $output with a directory\n";
        } elsif (!$output->is_dir()) {
            $self->_report_action('Creating', $output);
            $output->mkpath();
        }
    } elsif ($input->basename() =~ m{ [.] spin \z }xms) {
        my $output = $self->_output_for_file($input, '.spin');
        $self->{generated}{"$output"} = 1;
        if ($self->{pointer}->is_out_of_date($input, $output)) {
            $self->_report_action('Converting', $output);
            $self->{pointer}->spin_pointer($input, $output);
        }
    } elsif ($input->basename() =~ m{ [.] th \z }xms) {
        my $output = $self->_output_for_file($input, '.th');
        $self->{generated}{"$output"} = 1;

        # See if we're forced to regenerate the file because it is affected by
        # a software release.
        if ($output->exists() && $self->{versions}) {
            my $relative = $input->relative($self->{source});
            my $time = $self->{versions}->latest_release($relative);
            return
              if is_newer($output, $input) && $output->stat()->[9] >= $time;
        } else {
            return if is_newer($output, $input);
        }

        # The output file is not newer.  Respin it.
        $self->_report_action('Spinning', $output);
        $self->{thread}->spin_thread_file($input, $output);
    } else {
        my ($extension) = ($input->basename =~ m{ [.] ([^.]+) \z }xms);
        if (defined($extension) && $rules{$extension}) {
            my ($name, $sub) = $rules{$extension}->@*;



( run in 1.062 second using v1.01-cache-2.11-cpan-39bf76dae61 )