line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Data::Tubes::Util::Output; |
2
|
3
|
|
|
3
|
|
95621
|
use strict; |
|
3
|
|
|
|
|
18
|
|
|
3
|
|
|
|
|
95
|
|
3
|
3
|
|
|
3
|
|
15
|
use warnings; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
86
|
|
4
|
3
|
|
|
3
|
|
582
|
use English qw< -no_match_vars >; |
|
3
|
|
|
|
|
3684
|
|
|
3
|
|
|
|
|
15
|
|
5
|
3
|
|
|
3
|
|
1163
|
use 5.010; |
|
3
|
|
|
|
|
11
|
|
6
|
3
|
|
|
3
|
|
29
|
use File::Path qw< make_path >; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
209
|
|
7
|
3
|
|
|
3
|
|
23
|
use File::Basename qw< dirname >; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
340
|
|
8
|
|
|
|
|
|
|
our $VERSION = '0.740'; |
9
|
|
|
|
|
|
|
|
10
|
3
|
|
|
3
|
|
561
|
use Log::Log4perl::Tiny qw< :easy :dead_if_first >; |
|
3
|
|
|
|
|
15740
|
|
|
3
|
|
|
|
|
20
|
|
11
|
3
|
|
|
3
|
|
2561
|
use Mo qw< default >; |
|
3
|
|
|
|
|
1655
|
|
|
3
|
|
|
|
|
16
|
|
12
|
|
|
|
|
|
|
has binmode => (default => ':raw'); |
13
|
|
|
|
|
|
|
has footer => (); |
14
|
|
|
|
|
|
|
has header => (); |
15
|
|
|
|
|
|
|
has interlude => (); |
16
|
|
|
|
|
|
|
has output => (default => \*STDOUT); |
17
|
|
|
|
|
|
|
has policy => (default => undef); |
18
|
|
|
|
|
|
|
has track => ( |
19
|
|
|
|
|
|
|
default => sub { |
20
|
|
|
|
|
|
|
return { |
21
|
|
|
|
|
|
|
files => 0, |
22
|
|
|
|
|
|
|
records => 0, |
23
|
|
|
|
|
|
|
chars_file => 0, |
24
|
|
|
|
|
|
|
chars_total => 0, |
25
|
|
|
|
|
|
|
}; |
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
); |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub open { |
30
|
31
|
|
|
31
|
1
|
61
|
my ($self, $hint) = @_; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# reset some tracking parameters |
33
|
31
|
|
|
|
|
50
|
my $track = $self->track(); |
34
|
31
|
|
|
|
|
167
|
$track->{files}++; |
35
|
31
|
|
|
|
|
43
|
$track->{records} = 0; |
36
|
31
|
|
|
|
|
42
|
$track->{chars_file} = 0; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# get new filehandle |
39
|
|
|
|
|
|
|
my ($fh, $fh_releaser) = |
40
|
31
|
|
|
|
|
58
|
@{$track}{qw< current_fh current_fh_releaser>} = $self->get_fh($hint); |
|
31
|
|
|
|
|
96
|
|
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# do header handling |
43
|
31
|
|
|
|
|
107
|
$self->_print($fh, $self->header(), $track); |
44
|
|
|
|
|
|
|
|
45
|
31
|
|
|
|
|
117
|
return $fh; |
46
|
|
|
|
|
|
|
} ## end sub open |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
sub __open_file { |
49
|
30
|
|
|
30
|
|
218
|
my ($filename, $binmode) = @_; |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# ensure its directory exists |
52
|
30
|
|
|
|
|
2260
|
make_path(dirname($filename), {error => \my $errors}); |
53
|
30
|
50
|
|
|
|
193
|
if (@$errors) { |
54
|
0
|
|
|
|
|
0
|
my ($error) = values %{$errors->[0]}; |
|
0
|
|
|
|
|
0
|
|
55
|
0
|
|
|
|
|
0
|
LOGCONFESS "make_path() for '$filename': $error"; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# can open the file, at last |
59
|
30
|
50
|
|
1
|
|
1616
|
CORE::open my $fh, '>', $filename |
|
1
|
|
|
|
|
7
|
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
6
|
|
60
|
|
|
|
|
|
|
or LOGCONFESS "open('$filename'): $OS_ERROR"; |
61
|
30
|
|
|
|
|
1115
|
binmode $fh, $binmode; |
62
|
|
|
|
|
|
|
|
63
|
30
|
|
|
|
|
1530
|
return $fh; |
64
|
|
|
|
|
|
|
} ## end sub __open_file |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub get_fh { |
67
|
31
|
|
|
31
|
1
|
49
|
my ($self, $handle) = @_; |
68
|
31
|
|
33
|
|
|
97
|
$handle //= $self->output(); |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# define a default releaser, but not for GLOBs as they have their own |
71
|
|
|
|
|
|
|
# life outside of here |
72
|
|
|
|
|
|
|
my $releaser = ref($handle) eq 'GLOB' ? undef : sub { |
73
|
30
|
50
|
|
30
|
|
1258
|
CORE::close $_[0] or LOGCONFESS "close(): $OS_ERROR"; |
74
|
30
|
|
|
|
|
99
|
return undef; |
75
|
31
|
100
|
|
|
|
307
|
}; |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# if $handle is a factory, treat it as such |
78
|
31
|
100
|
|
|
|
79
|
if (ref($handle) eq 'CODE') { |
79
|
29
|
|
|
|
|
68
|
my @items = $handle->($self); |
80
|
29
|
|
|
|
|
62
|
$handle = shift @items; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# override the $releaser if and only if the factory instructed to |
83
|
|
|
|
|
|
|
# do so. Otherwise, the default one will be kept. |
84
|
29
|
50
|
|
|
|
96
|
$releaser = shift @items if @items; |
85
|
|
|
|
|
|
|
} ## end if (ref($handle) eq 'CODE') |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# now, we either have a filehandle, or a filename |
88
|
31
|
100
|
|
|
|
67
|
return ($handle, $releaser) if ref($handle) eq 'GLOB'; |
89
|
30
|
|
|
|
|
101
|
return (__open_file($handle, $self->binmode()), $releaser); |
90
|
|
|
|
|
|
|
} ## end sub get_fh |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub release_fh { |
93
|
31
|
|
|
31
|
1
|
56
|
my ($self, $fh) = @_; |
94
|
31
|
|
|
|
|
49
|
my $track = $self->track(); |
95
|
31
|
100
|
|
|
|
193
|
if (my $releaser = delete $track->{current_fh_releaser}) { |
96
|
30
|
|
|
|
|
53
|
$releaser->($fh); |
97
|
|
|
|
|
|
|
} |
98
|
31
|
|
|
|
|
61
|
delete $track->{current_fh}; |
99
|
31
|
|
|
|
|
173
|
return undef; |
100
|
|
|
|
|
|
|
} ## end sub release_fh |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub close { |
103
|
31
|
|
|
31
|
1
|
57
|
my ($self, $fh, $track) = @_; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# do footer handling |
106
|
31
|
|
|
|
|
61
|
$self->_print($fh, $self->footer(), $track); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# call close, prepare $fh for other possible records |
109
|
31
|
|
|
|
|
61
|
return $self->release_fh($fh); |
110
|
|
|
|
|
|
|
} ## end sub close |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
sub just_close { |
113
|
18
|
|
|
18
|
1
|
23
|
my $self = shift; |
114
|
18
|
|
|
|
|
52
|
my $track = $self->track(); |
115
|
18
|
100
|
|
|
|
247
|
my $fh = $track->{current_fh} or return; |
116
|
7
|
|
|
|
|
19
|
$self->close($fh, $track); |
117
|
7
|
|
|
|
|
69
|
return; |
118
|
|
|
|
|
|
|
} ## end sub just_close |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub print { |
121
|
37
|
|
|
37
|
1
|
1549
|
my $self = shift; |
122
|
|
|
|
|
|
|
|
123
|
37
|
|
33
|
|
|
81
|
my $iterator = ref($_[0]) && $_[0]; |
124
|
37
|
|
|
|
|
71
|
my $checker = $self->checker(); |
125
|
37
|
|
|
|
|
146
|
my $track = $self->track(); |
126
|
37
|
|
|
|
|
124
|
my $fh = $track->{current_fh}; |
127
|
37
|
|
|
|
|
73
|
my $interlude = $self->interlude(); |
128
|
|
|
|
|
|
|
|
129
|
37
|
|
|
|
|
118
|
while ('necessary') { |
130
|
76
|
50
|
|
|
|
166
|
my $record = $iterator ? $iterator->() : shift(@_); |
131
|
76
|
100
|
|
|
|
157
|
last unless defined $record; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# get filehandle if needed |
134
|
39
|
|
66
|
|
|
122
|
$fh ||= $self->open(); |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# print interlude if we have previous records, increase count |
137
|
|
|
|
|
|
|
$self->_print($fh, $interlude, $track) |
138
|
39
|
100
|
|
|
|
79
|
if $track->{records}; |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# print record |
141
|
39
|
|
|
|
|
81
|
$self->_print($fh, $record, $track); |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# increment number of records, for next print |
144
|
39
|
|
|
|
|
49
|
$track->{records}++; |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# do checks if activated |
147
|
39
|
100
|
100
|
|
|
96
|
$fh = $self->close($fh, $track) |
148
|
|
|
|
|
|
|
if $checker && (!$checker->($self)); |
149
|
|
|
|
|
|
|
} ## end while ('necessary') |
150
|
|
|
|
|
|
|
|
151
|
37
|
|
|
|
|
89
|
return; |
152
|
|
|
|
|
|
|
} ## end sub print |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub _print { |
155
|
109
|
|
|
109
|
|
360
|
my ($self, $fh, $data, $track) = @_; |
156
|
109
|
100
|
|
|
|
187
|
return unless defined $data; |
157
|
91
|
50
|
|
|
|
141
|
$data = $data->($self) if ref $data; |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# do print data |
160
|
91
|
50
|
|
|
|
182
|
ref($fh) or LOGCONFESS("$fh is not a reference"); |
161
|
91
|
50
|
|
|
|
106
|
print {$fh} $data or LOGCONFESS "print(): $OS_ERROR"; |
|
91
|
|
|
|
|
266
|
|
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# update trackers |
164
|
91
|
|
|
|
|
147
|
my $new_chars = length($data); |
165
|
91
|
|
|
|
|
116
|
$track->{chars_file} += $new_chars; |
166
|
91
|
|
|
|
|
104
|
$track->{chars_total} += $new_chars; |
167
|
|
|
|
|
|
|
|
168
|
91
|
|
|
|
|
133
|
return $new_chars; |
169
|
|
|
|
|
|
|
} ## end sub _print |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
sub default_check { |
172
|
28
|
|
|
28
|
1
|
44
|
my $self = shift; |
173
|
|
|
|
|
|
|
|
174
|
28
|
50
|
|
|
|
59
|
my $policy = $self->policy() |
175
|
|
|
|
|
|
|
or return 1; # no policy, always fine |
176
|
28
|
|
|
|
|
225
|
my $track = $self->track(); |
177
|
28
|
50
|
|
|
|
163
|
if (my $mr = $policy->{records_threshold}) { |
178
|
28
|
100
|
|
|
|
120
|
return 0 if $track->{records} >= $mr; |
179
|
|
|
|
|
|
|
} |
180
|
4
|
50
|
|
|
|
9
|
if (my $cpf = $policy->{characters_threshold}) { |
181
|
0
|
0
|
|
|
|
0
|
return 0 if $track->{chars_file} >= $cpf; |
182
|
|
|
|
|
|
|
} |
183
|
4
|
|
|
|
|
15
|
return 1; |
184
|
|
|
|
|
|
|
} ## end sub default_check |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub checker { |
187
|
37
|
|
|
37
|
1
|
46
|
my $self = shift; |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# allow for overriding tout-court |
190
|
37
|
50
|
|
|
|
144
|
if (my $method = $self->can('check')) { |
191
|
0
|
|
|
|
|
0
|
return $method; # will eventually be called in the right way |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# if no policy is set, there's no reason to do checks |
195
|
37
|
100
|
|
|
|
86
|
my $policy = $self->policy() or return; |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
# at this point, let's use the default_check, whatever it is |
198
|
27
|
|
|
|
|
229
|
return $self->can('default_check'); |
199
|
|
|
|
|
|
|
} ## end sub checker |
200
|
|
|
|
|
|
|
|
201
|
18
|
|
|
18
|
|
1143
|
sub DESTROY { shift->just_close() } |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub writer { |
204
|
0
|
|
|
0
|
1
|
0
|
my $package = shift; |
205
|
0
|
|
|
|
|
0
|
my $self = $package->new(@_); |
206
|
0
|
|
|
0
|
|
0
|
return sub { return $self->print(@_) }; |
|
0
|
|
|
|
|
0
|
|
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
1; |