File Coverage

blib/lib/String/Sections.pm
Criterion Covered Total %
statement 62 79 78.4
branch 13 26 50.0
condition 1 3 33.3
subroutine 17 22 77.2
pod 3 3 100.0
total 96 133 72.1


line stmt bran cond sub pod time code
1 15     15   1136743 use strict;
  15         42  
  15         704  
2 15     15   88 use warnings;
  15         30  
  15         875  
3              
4             package String::Sections;
5             BEGIN {
6 15     15   505 $String::Sections::AUTHORITY = 'cpan:KENTNL';
7             }
8             {
9             $String::Sections::VERSION = '0.3.2';
10             }
11              
12             # ABSTRACT: Extract labeled groups of sub-strings from a string.
13              
14              
15              
16 15     15   396 use 5.010001;
  15         59  
  15         696  
17 15     15   18429 use Moo;
  15         293747  
  15         106  
18 15     15   59832 use Types::Standard qw( RegexpRef Str Bool Maybe FileHandle ArrayRef );
  15         1467151  
  15         252  
19              
20             our $TYPE_REGEXP = RegexpRef;
21             our $TYPE_OPTIONAL_STRING = Maybe [Str];
22             our $TYPE_BOOL = Bool;
23             our $TYPE_FILEHANDLE = FileHandle;
24             our $TYPE_INPUT_LIST = ArrayRef [Str];
25              
26              
27              
28              
29              
30              
31 0     0   0 sub _croak { require Carp; goto &Carp::croak; }
  0         0  
32              
33              
34             sub __add_line {
35              
36 50     50   93 my ( $self, $stash, $line ) = @_;
37              
38 50 100       67 if ( ${$line} =~ $self->header_regex ) {
  50         856  
39 15         548 $stash->set_current("$1");
40 15         6280 $stash->append_data_to_current_section();
41 15         55 return 1; # 1 is next.
42             }
43              
44             # not valid for lists because lists are not likely to have __END__ in them.
45 35 50       890 if ( $self->stop_at_end ) {
46 0 0       0 return 0 if ${$line} =~ $self->document_end_regex;
  0         0  
47             }
48              
49 35 50       933 if ( $self->ignore_empty_prelude ) {
50 35 50 33     544 return 1 if not $stash->has_current and ${$line} =~ $self->empty_line_regex;
  0         0  
51             }
52              
53 35 50       130 if ( not $stash->has_current ) {
54 0         0 _croak( 'bogus data section: text outside named section. line: ' . ${$line} );
  0         0  
55             }
56              
57 35 50       531 if ( $self->enable_escapes ) {
58 0         0 my $regex = $self->line_escape_regex;
59 0         0 ${$line} =~ s{$regex}{}msx;
  0         0  
60             }
61              
62 35         481 $stash->append_data_to_current_section($line);
63              
64 35         73 return 1;
65             }
66              
67              
68             sub load_list {
69 8     8 1 57356 my ( $self, @rest ) = @_;
70 8         66 $TYPE_INPUT_LIST->assert_valid( \@rest );
71 8         6506 require String::Sections::Result;
72 8         65 my $result_ob = String::Sections::Result->new();
73              
74 8 100       18977 if ( $self->default_name ) {
75 2         2358 $result_ob->set_current( $self->default_name );
76 2         1512 $result_ob->append_data_to_current_section();
77             }
78              
79 8         164 for my $line (@rest) {
80 26         97 my $result = $self->__add_line( $result_ob, \$line );
81 26 50       96 next if $result;
82 0 0       0 last if not $result;
83              
84             }
85 8         62 return $result_ob;
86             }
87              
88              
89             sub load_string {
90 0     0 1 0 my ( $self, $string ) = @_;
91 0         0 return _croak('Not Implemented');
92             }
93              
94              
95             sub load_filehandle {
96 7     7 1 47150 my ( $self, $fh ) = @_;
97              
98 7         58 $TYPE_FILEHANDLE->assert_valid($fh);
99 7         5817 require String::Sections::Result;
100 7         51 my $result_ob = String::Sections::Result->new();
101              
102 7 100       16737 if ( $self->default_name ) {
103 2         1398 $result_ob->set_current( $self->default_name );
104 2         1279 $result_ob->append_data_to_current_section();
105             }
106 7         426 while ( defined( my $line = <$fh> ) ) {
107 24         97 my $result = $self->__add_line( $result_ob, \$line, );
108 24 50       229 next if $result;
109 0 0       0 last if not $result;
110             }
111              
112 7         49 return $result_ob;
113              
114             }
115              
116             #
117             # Defines accessors *_regex and _*_regex , that are for public and private access respectively.
118             # Defaults to _default_*_regex.
119             #
120              
121              
122             sub _regex_type {
123 60     60   149 my $name = shift;
124 60         688 return ( is => 'ro', isa => $TYPE_REGEXP, builder => '_default_' . $name, lazy => 1 );
125             }
126              
127              
128             sub _string_type {
129 15     15   41 my $name = shift;
130 15         133 return ( is => 'ro', isa => $TYPE_OPTIONAL_STRING, builder => '_default_' . $name, lazy => 1 );
131             }
132              
133              
134             sub _boolean_type {
135 45     45   103 my $name = shift;
136 45         436 return ( is => 'ro', isa => $TYPE_BOOL, builder => '_default_' . $name, lazy => 1 );
137             }
138              
139              
140             has 'header_regex' => _regex_type('header_regex');
141              
142              
143             has 'empty_line_regex' => _regex_type('empty_line_regex');
144              
145              
146             has 'document_end_regex' => _regex_type('document_end_regex');
147              
148              
149             has 'line_escape_regex' => _regex_type('line_escape_regex');
150              
151              
152             has 'default_name' => _string_type('default_name');
153              
154             # Boolean Accessors
155              
156              
157             has 'stop_at_end' => _boolean_type('stop_at_end');
158              
159              
160             has 'ignore_empty_prelude' => _boolean_type('ignore_empty_prelude');
161              
162              
163             has 'enable_escapes' => _boolean_type('enable_escapes');
164              
165             # Default values for various attributes.
166              
167              
168             sub _default_header_regex {
169 13     13   9048 return qr{
170             \A # start
171             _+\[ # __[
172             \s* # any whitespace
173             ([^\]]+?) # this is the actual name of the section
174             \s* # any whitespace
175             \]_+ # ]__
176             [\x0d\x0a]{1,2} # possible cariage return for windows files
177             \z # end
178             }msx;
179             }
180              
181              
182             sub _default_empty_line_regex {
183 0     0   0 return qr{
184             ^
185             \s* # Any empty lines before the first section get ignored.
186             $
187             }msx;
188             }
189              
190              
191             sub _default_document_end_regex {
192 0     0   0 return qr{
193             ^ # Start of line
194             __END__ # Document END matcher
195             }msx;
196             }
197              
198              
199             sub _default_line_escape_regex {
200 0     0   0 return qr{
201             \A # Start of line
202             \\ # A literal \
203             }msx;
204             }
205              
206              
207 11     11   7597 sub _default_default_name { return }
208              
209              
210 11     11   7043 sub _default_stop_at_end { return }
211              
212              
213 11     11   13094 sub _default_ignore_empty_prelude { return 1 }
214              
215              
216 11     11   8266 sub _default_enable_escapes { return }
217              
218             1;
219              
220             __END__