| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
14
|
|
|
14
|
|
1141417
|
use warnings; |
|
|
14
|
|
|
|
|
28
|
|
|
|
14
|
|
|
|
|
848
|
|
|
2
|
14
|
|
|
14
|
|
307
|
use 5.020; |
|
|
14
|
|
|
|
|
60
|
|
|
3
|
14
|
|
|
14
|
|
6712
|
use true; |
|
|
14
|
|
|
|
|
179897
|
|
|
|
14
|
|
|
|
|
115
|
|
|
4
|
14
|
|
|
14
|
|
18732
|
use experimental qw( signatures ); |
|
|
14
|
|
|
|
|
16499
|
|
|
|
14
|
|
|
|
|
89
|
|
|
5
|
14
|
|
|
14
|
|
10978
|
use stable qw( postderef ); |
|
|
14
|
|
|
|
|
6422
|
|
|
|
14
|
|
|
|
|
90
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
package Data::Section::Pluggable 0.08 { |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# ABSTRACT: Read structured data from __DATA__ |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
14
|
|
|
14
|
|
9552
|
use Class::Tiny qw( package prefer_filesystem filename _formats _cache ); |
|
|
14
|
|
|
|
|
41594
|
|
|
|
14
|
|
|
|
|
88
|
|
|
13
|
14
|
|
|
14
|
|
14061
|
use Exporter qw( import ); |
|
|
14
|
|
|
|
|
37
|
|
|
|
14
|
|
|
|
|
654
|
|
|
14
|
14
|
|
|
14
|
|
8258
|
use Ref::Util qw( is_ref is_plain_hashref is_coderef is_plain_arrayref is_blessed_ref ); |
|
|
14
|
|
|
|
|
39803
|
|
|
|
14
|
|
|
|
|
1763
|
|
|
15
|
14
|
|
|
14
|
|
15990
|
use MIME::Base64 qw( decode_base64 encode_base64 ); |
|
|
14
|
|
|
|
|
11505
|
|
|
|
14
|
|
|
|
|
1224
|
|
|
16
|
14
|
|
|
14
|
|
13102
|
use Path::Tiny 0.130 (); |
|
|
14
|
|
|
|
|
237746
|
|
|
|
14
|
|
|
|
|
609
|
|
|
17
|
14
|
|
|
14
|
|
166
|
use Carp (); |
|
|
14
|
|
|
|
|
30
|
|
|
|
14
|
|
|
|
|
20807
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our @EXPORT_OK = qw( get_data_section ); |
|
20
|
|
|
|
|
|
|
|
|
21
|
26
|
|
|
26
|
0
|
1758199
|
sub BUILDARGS ($class, @args) { |
|
|
26
|
|
|
|
|
118
|
|
|
|
26
|
|
|
|
|
54
|
|
|
|
26
|
|
|
|
|
46
|
|
|
22
|
26
|
100
|
|
|
|
192
|
if(@args == 1) { |
|
23
|
8
|
100
|
|
|
|
48
|
return $args[0] if is_plain_hashref $args[0]; |
|
24
|
7
|
|
|
|
|
65
|
return { package => $args[0] }; |
|
25
|
|
|
|
|
|
|
} else { |
|
26
|
18
|
|
|
|
|
74
|
my %args = @args; |
|
27
|
18
|
|
|
|
|
76
|
return \%args; |
|
28
|
|
|
|
|
|
|
} |
|
29
|
|
|
|
|
|
|
} |
|
30
|
|
|
|
|
|
|
|
|
31
|
26
|
|
|
26
|
0
|
607
|
sub BUILD ($self, $) { |
|
|
26
|
|
|
|
|
75
|
|
|
|
26
|
|
|
|
|
43
|
|
|
32
|
26
|
100
|
|
|
|
967
|
unless(defined $self->package) { |
|
33
|
17
|
|
|
|
|
242
|
my $package = caller 2; |
|
34
|
17
|
|
|
|
|
373
|
$self->package($package); |
|
35
|
|
|
|
|
|
|
} |
|
36
|
26
|
|
|
|
|
201
|
foreach my $attr (qw( prefer_filesystem filename )) { |
|
37
|
52
|
100
|
|
|
|
1210
|
if(defined $self->$attr) { |
|
38
|
2
|
100
|
66
|
|
|
28
|
unless(is_blessed_ref($self->$attr) && $self->$attr->isa('Path::Tiny')) { |
|
39
|
1
|
|
|
|
|
22
|
$self->$attr(Path::Tiny->new($self->$attr)->absolute); |
|
40
|
|
|
|
|
|
|
} |
|
41
|
|
|
|
|
|
|
} |
|
42
|
|
|
|
|
|
|
} |
|
43
|
26
|
|
|
|
|
712
|
$self->_formats({}); |
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
|
|
47
|
32
|
|
|
32
|
1
|
657922
|
sub get_data_section ($self=undef, $name=undef) { |
|
|
32
|
|
|
|
|
95
|
|
|
|
32
|
|
|
|
|
77
|
|
|
|
32
|
|
|
|
|
66
|
|
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# handle being called as a function instead of |
|
50
|
|
|
|
|
|
|
# a method. |
|
51
|
32
|
100
|
|
|
|
121
|
unless(is_ref $self) { |
|
52
|
4
|
|
|
|
|
19
|
$name = $self; |
|
53
|
4
|
|
|
|
|
57
|
$self = __PACKAGE__->new(scalar caller); |
|
54
|
|
|
|
|
|
|
} |
|
55
|
|
|
|
|
|
|
|
|
56
|
32
|
|
|
|
|
150
|
my $all = $self->_get_all_data_sections; |
|
57
|
32
|
100
|
|
|
|
390
|
return undef unless $all; |
|
58
|
|
|
|
|
|
|
|
|
59
|
31
|
100
|
|
|
|
109
|
if (defined $name) { |
|
60
|
17
|
100
|
|
|
|
53
|
if(exists $all->{$name}) { |
|
61
|
16
|
|
|
|
|
66
|
return $self->_format($name, $all->{$name}); |
|
62
|
|
|
|
|
|
|
} |
|
63
|
1
|
|
|
|
|
5
|
return undef; |
|
64
|
|
|
|
|
|
|
} else { |
|
65
|
14
|
|
|
|
|
59
|
return $self->_format_all($all); |
|
66
|
|
|
|
|
|
|
} |
|
67
|
|
|
|
|
|
|
} |
|
68
|
|
|
|
|
|
|
|
|
69
|
14
|
|
|
14
|
|
32
|
sub _format_all ($self, $all) { |
|
|
14
|
|
|
|
|
27
|
|
|
|
14
|
|
|
|
|
23
|
|
|
|
14
|
|
|
|
|
35
|
|
|
70
|
14
|
|
|
|
|
24
|
my %new; |
|
71
|
14
|
|
|
|
|
60
|
foreach my $key (keys %$all) { |
|
72
|
28
|
|
|
|
|
101
|
$new{$key} = $self->_format($key, $all->{$key}); |
|
73
|
|
|
|
|
|
|
} |
|
74
|
14
|
|
|
|
|
72
|
\%new; |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
|
|
77
|
44
|
|
|
44
|
|
71
|
sub _format ($self, $name, $content) { |
|
|
44
|
|
|
|
|
63
|
|
|
|
44
|
|
|
|
|
76
|
|
|
|
44
|
|
|
|
|
88
|
|
|
|
44
|
|
|
|
|
62
|
|
|
78
|
44
|
|
|
|
|
138
|
$content = $self->_decode($content->@*); |
|
79
|
44
|
100
|
|
|
|
303
|
if($name =~ /\.(.*?)\z/ ) { |
|
80
|
42
|
|
|
|
|
99
|
my $ext = $1; |
|
81
|
42
|
100
|
|
|
|
873
|
if($self->_formats->{$ext}) { |
|
82
|
19
|
|
|
|
|
452
|
$content = $_->($self, $content) for $self->_formats->{$ext}->@*; |
|
83
|
|
|
|
|
|
|
} |
|
84
|
|
|
|
|
|
|
} |
|
85
|
44
|
|
|
|
|
4077
|
return $content; |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
|
|
88
|
44
|
|
|
44
|
|
64
|
sub _decode ($self, $content, $encoding) { |
|
|
44
|
|
|
|
|
95
|
|
|
|
44
|
|
|
|
|
81
|
|
|
|
44
|
|
|
|
|
82
|
|
|
|
44
|
|
|
|
|
62
|
|
|
89
|
44
|
100
|
|
|
|
153
|
return $content unless $encoding; |
|
90
|
6
|
50
|
|
|
|
33
|
if($encoding ne 'base64') { |
|
91
|
0
|
|
|
|
|
0
|
Carp::croak("unknown encoding: $encoding"); |
|
92
|
|
|
|
|
|
|
} |
|
93
|
6
|
|
|
|
|
23
|
return decode_base64($content); |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
|
|
96
|
33
|
|
|
33
|
|
51
|
sub _get_all_data_sections ($self) { |
|
|
33
|
|
|
|
|
55
|
|
|
|
33
|
|
|
|
|
51
|
|
|
97
|
33
|
100
|
|
|
|
835
|
return $self->_cache if $self->_cache; |
|
98
|
|
|
|
|
|
|
|
|
99
|
23
|
|
|
|
|
157
|
my $fh; |
|
100
|
|
|
|
|
|
|
|
|
101
|
23
|
100
|
|
|
|
435
|
if($self->filename) { |
|
102
|
1
|
|
|
|
|
15
|
$fh = $self->filename->openr_raw; |
|
103
|
|
|
|
|
|
|
} else { |
|
104
|
14
|
|
|
14
|
|
193
|
$fh = do { no strict 'refs'; \*{$self->package."::DATA"} }; |
|
|
14
|
|
|
|
|
32
|
|
|
|
14
|
|
|
|
|
24899
|
|
|
|
22
|
|
|
|
|
152
|
|
|
|
22
|
|
|
|
|
39
|
|
|
|
22
|
|
|
|
|
446
|
|
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
|
|
107
|
23
|
100
|
|
|
|
379
|
return undef unless defined fileno $fh; |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# Question: does this handle corner case where perl |
|
110
|
|
|
|
|
|
|
# file is just __DATA__ section? turns out, yes! |
|
111
|
|
|
|
|
|
|
# added test t/data_section_pluggable__data_only.t |
|
112
|
22
|
|
|
|
|
253
|
seek $fh, 0, 0; |
|
113
|
22
|
|
|
|
|
47
|
my $content = do { local $/; <$fh> }; |
|
|
22
|
|
|
|
|
130
|
|
|
|
22
|
|
|
|
|
942
|
|
|
114
|
22
|
|
|
|
|
431
|
$content =~ s/^.*\n__DATA__\n/\n/s; # for win32 |
|
115
|
22
|
|
|
|
|
147
|
$content =~ s/\n__END__\n.*$/\n/s; |
|
116
|
|
|
|
|
|
|
|
|
117
|
22
|
|
|
|
|
355
|
my @data = split /^@@\s+(.+?)\s*\r?\n/m, $content; |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# extra at start whitespace, or __DATA_ for data only file |
|
120
|
22
|
|
|
|
|
63
|
shift @data; |
|
121
|
|
|
|
|
|
|
|
|
122
|
22
|
|
|
|
|
94
|
my $all = {}; |
|
123
|
22
|
|
|
|
|
116
|
while (@data) { |
|
124
|
42
|
|
|
|
|
169
|
my ($name_encoding, $content) = splice @data, 0, 2; |
|
125
|
42
|
|
|
|
|
77
|
my ($name, $encoding); |
|
126
|
42
|
100
|
|
|
|
228
|
if($name_encoding =~ /^(.*)\s+\((.*?)\)$/) { |
|
127
|
4
|
|
|
|
|
24
|
$name = $1; |
|
128
|
4
|
|
|
|
|
11
|
$encoding = $2; |
|
129
|
|
|
|
|
|
|
} else { |
|
130
|
38
|
|
|
|
|
66
|
$name = $name_encoding; |
|
131
|
|
|
|
|
|
|
} |
|
132
|
42
|
100
|
100
|
|
|
957
|
if($self->prefer_filesystem && -f (my $path = $self->prefer_filesystem->child($name))) { |
|
133
|
2
|
100
|
|
|
|
178
|
$content = $encoding ? encode_base64($path->slurp_raw) : $path->slurp_utf8; |
|
134
|
|
|
|
|
|
|
} |
|
135
|
42
|
|
|
|
|
876
|
$all->{$name} = [ $content, $encoding ]; |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
|
|
138
|
22
|
|
|
|
|
515
|
return $self->_cache($all); |
|
139
|
|
|
|
|
|
|
} |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
|
|
142
|
19
|
|
|
19
|
1
|
862
|
sub add_format ($self, $ext, $cb) { |
|
|
19
|
|
|
|
|
54
|
|
|
|
19
|
|
|
|
|
34
|
|
|
|
19
|
|
|
|
|
41
|
|
|
|
19
|
|
|
|
|
30
|
|
|
143
|
19
|
50
|
|
|
|
55
|
Carp::croak("callback is not a code reference") unless is_coderef $cb; |
|
144
|
19
|
|
|
|
|
494
|
push $self->_formats->{$ext}->@*, $cb; |
|
145
|
19
|
|
|
|
|
267
|
return $self; |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
|
|
149
|
13
|
|
|
13
|
1
|
15656
|
sub add_plugin ($self, $name, %args) { |
|
|
13
|
|
|
|
|
28
|
|
|
|
13
|
|
|
|
|
31
|
|
|
|
13
|
|
|
|
|
30
|
|
|
|
13
|
|
|
|
|
24
|
|
|
150
|
|
|
|
|
|
|
|
|
151
|
13
|
50
|
|
|
|
95
|
Carp::croak("plugin name must match [a-z][a-z0-9_]+, got $name") |
|
152
|
|
|
|
|
|
|
unless $name =~ /^[a-z][a-z0-9_]+\z/; |
|
153
|
|
|
|
|
|
|
|
|
154
|
13
|
|
|
|
|
99
|
my $class = join '::', 'Data', 'Section', 'Pluggable', 'Plugin', ucfirst($name =~ s/_(.)/uc($1)/egr); |
|
|
6
|
|
|
|
|
57
|
|
|
155
|
13
|
|
|
|
|
88
|
my $pm = ($class =~ s!::!/!gr) . ".pm"; |
|
156
|
|
|
|
|
|
|
|
|
157
|
13
|
100
|
|
|
|
61
|
require $pm unless $self->_valid_plugin($class); |
|
158
|
|
|
|
|
|
|
|
|
159
|
13
|
|
|
|
|
2103
|
my $plugin; |
|
160
|
13
|
100
|
|
|
|
121
|
if($class->can("new")) { |
|
161
|
6
|
|
|
|
|
32
|
$plugin = $class->new(%args); |
|
162
|
|
|
|
|
|
|
} else { |
|
163
|
7
|
50
|
|
|
|
26
|
if(%args) { |
|
164
|
0
|
|
|
|
|
0
|
Carp::croak("extra arguments are not allowed for class plugins (hint create constructor)"); |
|
165
|
|
|
|
|
|
|
} |
|
166
|
7
|
|
|
|
|
17
|
$plugin = $class; |
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
|
|
169
|
13
|
50
|
|
|
|
1382
|
Carp::croak("$class is not a valid Data::Section::Pluggable plugin") |
|
170
|
|
|
|
|
|
|
unless $self->_valid_plugin($plugin); |
|
171
|
|
|
|
|
|
|
|
|
172
|
13
|
50
|
|
|
|
209
|
if($plugin->does('Data::Section::Pluggable::Role::ContentProcessorPlugin')) { |
|
173
|
13
|
|
|
|
|
308
|
my @extensions = $plugin->extensions; |
|
174
|
13
|
100
|
|
|
|
10606
|
@extensions = $extensions[0]->@* if is_plain_arrayref $extensions[0]; |
|
175
|
13
|
50
|
|
|
|
62
|
die "extensions method for $class returned no extensions" unless @extensions; |
|
176
|
|
|
|
|
|
|
|
|
177
|
16
|
|
|
16
|
|
138
|
my $cb = sub ($self, $content) { |
|
|
16
|
|
|
|
|
31
|
|
|
|
16
|
|
|
|
|
63
|
|
|
|
16
|
|
|
|
|
41
|
|
|
178
|
16
|
|
|
|
|
107
|
return $plugin->process_content($self, $content); |
|
179
|
13
|
|
|
|
|
74
|
}; |
|
180
|
|
|
|
|
|
|
|
|
181
|
13
|
|
|
|
|
83
|
$self->add_format($_, $cb) for @extensions; |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
}; |
|
184
|
|
|
|
|
|
|
|
|
185
|
13
|
|
|
|
|
115
|
return $self; |
|
186
|
|
|
|
|
|
|
} |
|
187
|
|
|
|
|
|
|
|
|
188
|
26
|
|
|
26
|
|
63
|
sub _valid_plugin ($self, $plugin) { |
|
|
26
|
|
|
|
|
50
|
|
|
|
26
|
|
|
|
|
47
|
|
|
|
26
|
|
|
|
|
41
|
|
|
189
|
26
|
100
|
|
|
|
4457
|
$plugin->can('does') && $plugin->does('Data::Section::Pluggable::Role::ContentProcessorPlugin'); |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
|
|
193
|
1
|
|
|
1
|
1
|
12
|
sub extract ($self, $dir=undef) { |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
1
|
|
|
194
|
1
|
|
50
|
|
|
6
|
$dir = Path::Tiny->new($dir // '.'); |
|
195
|
|
|
|
|
|
|
|
|
196
|
1
|
|
|
|
|
50
|
my $all = $self->_get_all_data_sections; |
|
197
|
|
|
|
|
|
|
|
|
198
|
1
|
|
|
|
|
8
|
foreach my $key (keys %$all) { |
|
199
|
3
|
|
|
|
|
5639
|
my $path = $dir->child($key); |
|
200
|
3
|
|
|
|
|
121
|
$path->parent->mkdir; |
|
201
|
3
|
|
|
|
|
663
|
my($content,$encoding) = $all->{$key}->@*; |
|
202
|
|
|
|
|
|
|
|
|
203
|
3
|
100
|
|
|
|
9
|
if(defined $encoding) { |
|
204
|
1
|
50
|
|
|
|
5
|
if($encoding eq 'base64') { |
|
205
|
1
|
|
|
|
|
10
|
$path->spew_raw(decode_base64($content)); |
|
206
|
|
|
|
|
|
|
} else { |
|
207
|
0
|
|
|
|
|
0
|
Carp::croak("unknown encoding: $encoding"); |
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
} else { |
|
210
|
2
|
|
|
|
|
8
|
$path->spew_utf8($content); |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
} |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
__END__ |