Alien-Selenium

 view release on metacpan or  search on metacpan

inc/Pod/Snippets.pm  view on Meta::CPAN


=head2 as_data ()

Returns the snippets in "data" format: that is, the return value is
ragged to the left by suppressing a constant number of space
characters at the beginning of each snippet.  (If tabs are present in
the POD, they are treated as being of infinite length; that is, the
ragging algorithm does not eat them or replace them with spaces.)

A snippet is defined as a series of subsequent verbatim POD paragraphs
with only B<Pod::Snippets> markup, if anything, intervening in
between.  That is, I<as_data()>, given the following POD in input:

=for metatests "as_data multiple blocks input" begin

    my $a = new The::Brain;

  =begin test

      # Just kidding. We can't do that, it's too dangerous.
      $a = new Pinky;

  =end test

  =for test ignore

    system("/sbin/reboot");

  and all of a sudden, we have:

  =for test

        if ($a->has_enough_cookies()) {
          $a->conquer_world();
        }

=for metatests "as_data multiple blocks input" end

would return (in list context)

=for metatests "as_data multiple blocks return" begin

  (<<'FIRST_SNIPPET', <<'SECOND_SNIPPET');
  my $a = new The::Brain;



    # Just kidding. We can't do that, it's too dangerous.
    $a = new Pinky;
  FIRST_SNIPPET
  if ($a->has_enough_cookies()) {
    $a->conquer_world();
  }
  SECOND_SNIPPET

=for metatests "as_data multiple blocks return" end

Notice how the indentation is respected snippet-by-snippet; also,
notice that the FIRST_SNIPPET has been padded with an appropriate
number of carriage returns to replace the B<Pod::Snippets> markup, so
that the return value is line-synchronized with the original POD.
However, leading and trailing whitespace is trimmed, leaving only
strings that starts with a nonblank line and end with a single
newline.

In scalar context, returns the blocks joined with a single newline
character ("\n"), thus resulting in a single piece of text where the
blocks are joined by exactly one empty line (and which as a whole is
no longer line-synchronized with the source code, of course).

=cut

sub as_data {
    my ($self) = @_;
    $self->_block_access_if_errors();

    my @retval = map {
        # This may be a pedestrian and sub-optimal way of doing the
        # ragging, but it sure is concise:
        until (m/^\S/m) { s/^ //gm or last; };
        "$_";
    } ($self->_merged_snippets);

    return wantarray ? @retval : join("\n", @retval);
}

=head2 as_code ()

Returns the snippets formatted as code, that is, like L</as_data>,
except that each block is prepended with an appropriate C<#line>
statement that Perl can interpret to renumber lines.  For instance,
these statements would cause Perl to Do The Right Thing if one
compiles the snippets as code with L<perlfunc/eval> and then runs it
under the Perl debugger.

=cut

sub as_code {
    my ($self) = @_;
    $self->_block_access_if_errors();
    my @retval = $self->as_data;

    foreach my $i (0..$#retval) {
        my $file = $self->filename;
        my $line = ($self->_merged_snippets)[$i]->line() +
            $self->{start_line} - 1;
        $retval[$i] = <<"LINE_MARKUP" . $retval[$i];
#line $line "$file"
LINE_MARKUP
    }
    return wantarray ? @retval : join("\n", @retval);
}

=head2 named ($name)

Returns a clone of this B<Pod::Snippet> object, except that it only
knows about the snippet (or snippets) that are named $name.  In the
most lax settings for the parser, this means: any and all snippets
where an C<=for test "$name" begin> (or C<=begin test "$name">) had
been open, but not yet closed with C<=for test "$name" end> (or C<=end
test "$name">).  Returns undef if no snippet named $name was seen at
all.

=cut

sub named {
    my ($self, $name) = @_;
    $self->_block_access_if_errors();
    my @snippets_with_this_name = grep {

inc/Pod/Snippets.pm  view on Meta::CPAN

         } (@{$self->{unmerged_snippets}});
    return if ! grep { defined } @snippets_with_this_name;
    return bless
        {
         unmerged_snippets => \@snippets_with_this_name,
         map { exists $self->{$_} ? ($_ => $self->{$_}) : () }
         (qw(warnings errors filename start_line) )
         # Purposefully do not transfer other fields such as
         # ->{merged_snippets}
        }, ref($self);
}

=begin internals

=head2 _block_access_if_errors ()

Throws an exception if L</errors> returns a nonzero value.  Called by
every read accessor except L</warnings> and I<errors()>.

=cut

sub _block_access_if_errors {
    die <<"MESSAGE" if shift->errors;
Cannot fetch parse results from Pod::Snippets with errors.
MESSAGE
}

=head2 _merged_snippets ()

Returns roughly the same thing as L</pod_snippets> in
L</Pod::Snippets::_Parser>, except that leading and trailing
whitespace is trimmed (updating the line counters appropriately),
names are discarded and snippets are merged together (with appropriate
padding using $/) according to the semantics set forth in L</as_data>.
This method has a cache.

=cut

sub _merged_snippets {
    my ($self) = @_;

    $self->{merged_snippets} ||= do {
        my @snippets;
        foreach my $snip (@{$self->{unmerged_snippets}}) {
            if (! defined($snip)) {
                push @snippets, undef if defined $snippets[-1];
            } elsif (! @snippets) {
                push @snippets, $snip;
            } elsif (! defined($snippets[-1])) {
                $snippets[-1] = $snip;
            } else {
                # The merger case.
                my $prevstartline = $snippets[-1]->line();
                my $newlines_to_add = $snip->line - $prevstartline
                    - _number_of_newlines_in($snippets[-1]);
                if ($newlines_to_add < 0) {
                    my $filename = $self->filename();
                    warn <<"ASSERTION_FAILED" ;
Pod::Snippets: problem counting newlines at $filename
near line $prevstartline (trying to skip $newlines_to_add lines)
Output will be desynchronized.
ASSERTION_FAILED
                    $newlines_to_add = 0;
                }
                $snippets[-1] = $snippets[-1] . $/ x $newlines_to_add .
                    $snip;
            }
        }

        pop @snippets if ! defined $snippets[-1];

        # Trim leading and trailing whitespace.
        foreach my $i (0..$#snippets) {
            my $text = "$snippets[$i]";
            my $line = $snippets[$i]->line();
            my $nl = $/; # Foils smarter-than-thou regex parser
            while($text =~ s|^\s*$nl||) { $line++ };
            # This is disturbingly asymetric.
            $text =~ s|(^\s*$nl)*\Z||m;
            $snippets[$i] = Pod::Snippets::_Snippet->new
                ($line, $text, $snippets[$i]->names_set);
        }

        \@snippets;
    };

    return @{$self->{merged_snippets}};
}

=head2 _number_of_newlines_in($string)

This function (B<not> a method) returns the number of times $/ is
found in $string.

=cut

sub _number_of_newlines_in {
    my @occurences = shift =~ m|($/)|gs;
    return scalar @occurences;
}

=head1 Pod::Snippets::_Parser

This class is a subclass to L<Pod::Parser>, that builds appropriate
state on behalf of a I<Pod::Snippets> object.

=cut

package Pod::Snippets::_Parser;

use base "Pod::Parser";

=head2 new_for_pod_snippets (-opt1 => $val1, ...)

An alternate constructor with a different syntax suited for calling
from I<Pod::Snippets>.  Available named options are:

=over

=item B<< -markup => $string >>



( run in 0.675 second using v1.01-cache-2.11-cpan-0068ddc7af1 )