File Coverage

lib/Perl/Folder.pm
Criterion Covered Total %
statement 12 75 16.0
branch 0 16 0.0
condition n/a
subroutine 4 13 30.7
pod 0 9 0.0
total 16 113 14.1


line stmt bran cond sub pod time code
1             ## Fold and Unfold Blocks in Perl Code
2             #
3             # Using regexps to look for values in Perl code is problematic. This module
4             # makes it much more reliable.
5             #
6             # Say you are looking for a line like this one:
7             #
8             # our $VERSION = '0.10';
9             #
10             # Matching that line is easy. But that line might be in a heredoc, the `DATA`
11             # section, a POD block or a comment. Also if you try to strip out all heredocs
12             # you don't know if they are really part of a POD block of `DATA` section, and
13             # vice versa.
14             #
15             # This module correctly finds these four types of blocks and folds them up,
16             # replacing their content with the SHA1 digest of their content.
17             #
18             # Copyright (c) 2007. Ingy döt Net. All rights reserved.
19             #
20             # Licensed under the same terms as Perl itself.
21              
22             package Perl::Folder;
23 1     1   23355 use 5.006001;
  1         3  
  1         35  
24 1     1   5 use strict;
  1         2  
  1         31  
25 1     1   5 use warnings;
  1         6  
  1         491  
26             our $VERSION = '0.10';
27              
28             ## Synopsis:
29             # use Perl::Folder;
30             # my $folded = Perl::Folder->fold_blocks($perl);
31             # my $unfolded = Perl::Folder->unfold_blocks($folded);
32              
33             # A map of digests to code blocks
34             my $digest_map = {};
35              
36             # Regexp fragments for matching heredoc, pod section, comment block and
37             # data section.
38             my $re_here = qr/
39             (?: # Heredoc starting line
40             ^ # Start of some line
41             ((?-s:.*?)) # $2 - text before heredoc marker
42             <<(?!=) # heredoc marker
43             [\t\x20]* # whitespace between marker and quote
44             ((?>['"]?)) # $3 - possible left quote
45             ([\w\-\.]*) # $4 - heredoc terminator
46             (\3 # $5 - possible right quote
47             (?-s:.*\n)) # and rest of the line
48             (.*?\n) # $6 - Heredoc content
49             (?
50             (\4\n) # $7 - Heredoc terminating line
51             )
52             /xsm;
53              
54             my $re_pod = qr/
55             (?:
56             (?-s:^=(?!cut\b)\w+.*\n) # Pod starter line
57             .*? # Pod lines
58             (?:(?-s:^=cut\b.*\n)|\z) # Pod terminator
59             )
60             /xsm;
61              
62             my $re_comment = qr/
63             (?:
64             (?m-s:^[^\S\n]*\#.*\n)+ # one or more comment lines
65             )
66             /xsm;
67              
68             my $re_data = qr/
69             (?:
70             ^(?:__END__|__DATA__)\n # DATA starter line
71             .* # Rest of lines
72             )
73             /xsm;
74              
75             # Fold each heredoc, pod section, comment block and data section, each
76             # into a single line containing a digest of the original content.
77             #
78             # This makes further dividing of Perl code less troublesome.
79             sub fold_blocks {
80 0     0 0   my ($class, $source) = @_;
81              
82 0           $$source =~ s/(~{3,})/$1~/g;
83 0           $$source =~ s/(^'{3,})/$1'/gm;
84 0           $$source =~ s/(^`{3,})/$1`/gm;
85 0           $$source =~ s/(^={3,})/$1=/gm;
86              
87 0           while (1) {
88 1     1   4 no warnings;
  1         8  
  1         955  
89 0 0         $$source =~ s/
90             (
91             $re_pod |
92             $re_comment |
93             $re_here |
94             $re_data
95             )
96             /
97 0           my $result = $1;
98 0 0         $result =~ m{\A($re_data)} ? $class->fold_data() :
    0          
    0          
    0          
99             $result =~ m{\A($re_pod)} ? $class->fold_pod() :
100             $result =~ m{\A($re_comment)} ? $class->fold_comment() :
101             $result =~ m{\A($re_here)} ? $class->fold_here() :
102             die "'$result' didn't match '$re_comment'";
103             /ex or last;
104             }
105              
106 0           $$source =~ s/(?
107 0           $$source =~ s/^'''(?!') /__DATA__\n/gm;
108 0           $$source =~ s/^```(?!`)/#/gm;
109 0           $$source =~ s/^===(?!=)/=/gm;
110              
111 0           $$source =~ s/^(={3,})=/$1/gm;
112 0           $$source =~ s/^('{3,})'/$1/gm;
113 0           $$source =~ s/^(`{3,})`/$1/gm;
114 0           $$source =~ s/(~{3,})~/$1/g;
115              
116 0           return $source;
117             }
118              
119             sub unfold_blocks {
120 0     0 0   my ($class, $source) = @_;
121              
122 0           $$source =~ s/
123             (
124             ^__DATA__\n[0-9a-fA-F]{40}\n
125             |
126             ^=pod\s[0-9a-fA-F]{40}\n=cut\n
127             |
128             ^\#\s[0-9a-fA-F]{40}\n
129             )
130             /
131 0           my $match = $1;
132 0 0         $match =~ s!.*?([0-9a-fA-F]{40}).*!$1!s or die;
133 0           $digest_map->{$match}
134             /xmeg;
135              
136 0           return $source;
137             }
138              
139             sub unfold_comment_blocks {
140 0     0 0   my ($class, $source) = @_;
141              
142 0           $$source =~ s/
143             (?:
144             ^\#\s([0-9a-fA-F]{40})\n
145             )
146             /
147 0           $digest_map->{$1}
148             /xmeg;
149              
150 0           return $source;
151             }
152              
153             # Fold a heredoc's content but don't fold other heredocs from the
154             # same line.
155             sub fold_here {
156 0     0 0   my $class = shift;
157 0           my $result = "$2~~~$3$4$5";
158 0           my $preface = '';
159 0           my $text = $6;
160 0           my $stop = $7;
161 0           while (1) {
162 0 0         if ($text =~ s!^(([0-9a-fA-F]{40})\n.*\n)!!) {
163 0 0         if (defined $digest_map->{$2}) {
164 0           $preface .= $1;
165 0           next;
166             }
167             else {
168 0           $text = $1 . $text;
169 0           last;
170             }
171             }
172 0           last;
173             }
174 0           my $digest = $class->fold($text);
175 0           $result = "$result$preface$digest\n$stop";
176 0           $result;
177             }
178              
179             sub fold_pod {
180 0     0 0   my $class = shift;
181 0           my $text = $1;
182 0           my $digest = $class->fold($text);
183 0           return qq{===pod $digest\n===cut\n};
184             }
185              
186             sub fold_comment {
187 0     0 0   my $class = shift;
188 0           my $text = $1;
189 0           my $digest = $class->fold($text);
190 0           return qq{``` $digest\n};
191             }
192              
193             sub fold_data {
194 0     0 0   my $class = shift;
195 0           my $text = $1;
196 0           my $digest = $class->fold($text);
197 0           return qq{''' $digest\n};
198             }
199              
200             # Fold a piece of code into a unique string.
201             sub fold {
202 0     0 0   require Digest::SHA1;
203 0           my ($class, $text) = @_;
204 0           my $digest = Digest::SHA1::sha1_hex($text);
205 0           $digest_map->{$digest} = $text;
206 0           return $digest;
207             }
208              
209             # Expand folded code into original content.
210             sub unfold {
211 0     0 0   my ($class, $digest) = @_;
212 0           return $digest_map->{$digest};
213             }
214              
215             1;
216              
217             =for perldoc
218             This POD generated by Perldoc-0.21.
219             DO NOT EDIT. Your changes will be lost.
220              
221             =head1 NAME
222              
223             Perl::Folder - Fold and Unfold Blocks in Perl Code
224              
225             =head1 SYNOPSIS
226              
227             use Perl::Folder;
228             my $folded = Perl::Folder->fold_blocks($perl);
229             my $unfolded = Perl::Folder->unfold_blocks($folded);
230              
231             =head1 DESCRIPTION
232              
233             Using regexps to look for values in Perl code is problematic. This module
234             makes it much more reliable.
235              
236             Say you are looking for a line like this one:
237              
238             our $VERSION = '0.10';
239              
240             Matching that line is easy. But that line might be in a heredoc, the C
241             section, a POD block or a comment. Also if you try to strip out all heredocs
242             you don't know if they are really part of a POD block of C section, and
243             vice versa.
244              
245             This module correctly finds these four types of blocks and folds them up,
246             replacing their content with the SHA1 digest of their content.
247              
248             =head1 AUTHOR
249              
250             Ingy döt Net
251              
252             =head1 COPYRIGHT
253              
254             Copyright (c) 2007. Ingy döt Net. All rights reserved.
255              
256             This program is free software; you can redistribute it and/or modify it
257             under the same terms as Perl itself.
258              
259             See http://www.perl.com/perl/misc/Artistic.html
260              
261             =cut