line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# file: Dist/Zilla/Role/ErrorLogger.pm |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Copyright © 2015 Van de Bugger |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
# This file is part of perl-Dist-Zilla-Role-ErrorLogger. |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# perl-Dist-Zilla-Role-ErrorLogger is free software: you can redistribute it and/or modify it |
10
|
|
|
|
|
|
|
# under the terms of the GNU General Public License as published by the Free Software Foundation, |
11
|
|
|
|
|
|
|
# either version 3 of the License, or (at your option) any later version. |
12
|
|
|
|
|
|
|
# |
13
|
|
|
|
|
|
|
# perl-Dist-Zilla-Role-ErrorLogger is distributed in the hope that it will be useful, but WITHOUT |
14
|
|
|
|
|
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
15
|
|
|
|
|
|
|
# PURPOSE. See the GNU General Public License for more details. |
16
|
|
|
|
|
|
|
# |
17
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License along with |
18
|
|
|
|
|
|
|
# perl-Dist-Zilla-Role-ErrorLogger. If not, see <http://www.gnu.org/licenses/>. |
19
|
|
|
|
|
|
|
# |
20
|
|
|
|
|
|
|
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
#pod =for :this This is C<Dist::Zilla::Role::ErrorLogger> role documentation. Read this if you want to |
23
|
|
|
|
|
|
|
#pod have error logging capabilities in your Dist::Zilla plugin. |
24
|
|
|
|
|
|
|
#pod |
25
|
|
|
|
|
|
|
#pod =for test_synopsis |
26
|
|
|
|
|
|
|
#pod my ( $cond, $file ); |
27
|
|
|
|
|
|
|
#pod sub Dist::Zilla::Plugin::YourPlugin::do_something; |
28
|
|
|
|
|
|
|
#pod sub Dist::Zilla::Plugin::YourPlugin::do_something_else; |
29
|
|
|
|
|
|
|
#pod |
30
|
|
|
|
|
|
|
#pod =head1 SYNOPSIS |
31
|
|
|
|
|
|
|
#pod |
32
|
|
|
|
|
|
|
#pod package Dist::Zilla::Plugin::YourPlugin; |
33
|
|
|
|
|
|
|
#pod use Moose; |
34
|
|
|
|
|
|
|
#pod use namespace::autoclean; |
35
|
|
|
|
|
|
|
#pod with 'Dist::Zilla::Role::Plugin'; |
36
|
|
|
|
|
|
|
#pod with 'Dist::Zilla::Role::ErrorLogger'; |
37
|
|
|
|
|
|
|
#pod |
38
|
|
|
|
|
|
|
#pod sub method { |
39
|
|
|
|
|
|
|
#pod my $self = shift( @_ ); |
40
|
|
|
|
|
|
|
#pod |
41
|
|
|
|
|
|
|
#pod if ( $cond ) { $self->log_error( 'error message' ); }; |
42
|
|
|
|
|
|
|
#pod |
43
|
|
|
|
|
|
|
#pod do_something or $self->log_error( 'another error message' ); |
44
|
|
|
|
|
|
|
#pod |
45
|
|
|
|
|
|
|
#pod while ( $cond ) { |
46
|
|
|
|
|
|
|
#pod do_something_else or $self->log_error( 'error message' ) and next; |
47
|
|
|
|
|
|
|
#pod }; |
48
|
|
|
|
|
|
|
#pod |
49
|
|
|
|
|
|
|
#pod $self->log_errors_in_file( |
50
|
|
|
|
|
|
|
#pod $file, |
51
|
|
|
|
|
|
|
#pod 1 => 'error message', # Error at file line 1. |
52
|
|
|
|
|
|
|
#pod 5 => 'another error message', # Error at file line 5. |
53
|
|
|
|
|
|
|
#pod ); |
54
|
|
|
|
|
|
|
#pod |
55
|
|
|
|
|
|
|
#pod $self->abort_if_error( 'errors found' ); |
56
|
|
|
|
|
|
|
#pod }; |
57
|
|
|
|
|
|
|
#pod |
58
|
|
|
|
|
|
|
#pod __PACKAGE__->meta->make_immutable; |
59
|
|
|
|
|
|
|
#pod 1; |
60
|
|
|
|
|
|
|
#pod |
61
|
|
|
|
|
|
|
#pod =cut |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------- |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
#pod =head1 DESCRIPTION |
66
|
|
|
|
|
|
|
#pod |
67
|
|
|
|
|
|
|
#pod The role extends standard C<Dist::Zilla> logging capabilities with few methods a bit more |
68
|
|
|
|
|
|
|
#pod convenient for reporting (multiple) errors than brutal C<log_fatal>. See L</"WHY?"> for more |
69
|
|
|
|
|
|
|
#pod details. |
70
|
|
|
|
|
|
|
#pod |
71
|
|
|
|
|
|
|
#pod The role requires C<log> method in the consumer. |
72
|
|
|
|
|
|
|
#pod |
73
|
|
|
|
|
|
|
#pod =cut |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------- |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
package Dist::Zilla::Role::ErrorLogger; |
78
|
|
|
|
|
|
|
|
79
|
1
|
|
|
1
|
|
3016886
|
use Moose::Role; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
11
|
|
80
|
1
|
|
|
1
|
|
5265
|
use namespace::autoclean; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
9
|
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# ABSTRACT: Have error logging capabilities in your Dist::Zilla plugin |
83
|
|
|
|
|
|
|
our $VERSION = 'v0.8.0'; # VERSION |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
requires qw{ log }; |
86
|
|
|
|
|
|
|
|
87
|
1
|
|
|
1
|
|
92
|
use List::Util qw{ min max }; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
1060
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------- |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
#pod =attr error_count |
92
|
|
|
|
|
|
|
#pod |
93
|
|
|
|
|
|
|
#pod $int = $self->error_count; |
94
|
|
|
|
|
|
|
#pod |
95
|
|
|
|
|
|
|
#pod C<Int>, read-only. Number of logged errors (i. e. number of made C<log_error> calls). |
96
|
|
|
|
|
|
|
#pod |
97
|
|
|
|
|
|
|
#pod =cut |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
has error_count => ( |
100
|
|
|
|
|
|
|
is => 'ro', |
101
|
|
|
|
|
|
|
isa => 'Int', |
102
|
|
|
|
|
|
|
default => 0, |
103
|
|
|
|
|
|
|
init_arg => undef, |
104
|
|
|
|
|
|
|
); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------- |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
#pod =method log_error |
109
|
|
|
|
|
|
|
#pod |
110
|
|
|
|
|
|
|
#pod $self->log_error( @items ); |
111
|
|
|
|
|
|
|
#pod $self->log_error( \%args, @items ); |
112
|
|
|
|
|
|
|
#pod |
113
|
|
|
|
|
|
|
#pod This method calls C<log> method, passing all the arguments, and increments value of C<error_count> |
114
|
|
|
|
|
|
|
#pod attribute. The method returns true value, so can be used in following constructs: |
115
|
|
|
|
|
|
|
#pod |
116
|
|
|
|
|
|
|
#pod while ( ⦠) { |
117
|
|
|
|
|
|
|
#pod do_something or $self->log_error( 'message' ) and next; |
118
|
|
|
|
|
|
|
#pod }; |
119
|
|
|
|
|
|
|
#pod |
120
|
|
|
|
|
|
|
#pod =cut |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub log_error { ## no critic ( RequireArgUnpacking ) |
123
|
46
|
|
|
46
|
1
|
94023
|
my $self = shift( @_ ); |
124
|
|
|
|
|
|
|
# If the first argument is a hashref, it is treated as extra arguments to logging, not as |
125
|
|
|
|
|
|
|
# message, see <https://metacpan.org/pod/Log::Dispatchouli#log>. These extra arguments |
126
|
|
|
|
|
|
|
# include `level` argument. It seems natural that error messages have `'error'` level. |
127
|
|
|
|
|
|
|
# However, such messages do not appear in `dzil` output. So, do not try to set level, just |
128
|
|
|
|
|
|
|
# pass all the arguments to `log`. |
129
|
46
|
|
|
|
|
141
|
$self->log( @_ ); |
130
|
46
|
|
|
|
|
13938
|
++ $self->{ error_count }; |
131
|
46
|
|
|
|
|
140
|
return $self->{ error_count }; |
132
|
|
|
|
|
|
|
}; |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------- |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
#pod =method abort |
137
|
|
|
|
|
|
|
#pod |
138
|
|
|
|
|
|
|
#pod $self->abort( @items ); |
139
|
|
|
|
|
|
|
#pod $self->abort( \%args, @items ); |
140
|
|
|
|
|
|
|
#pod |
141
|
|
|
|
|
|
|
#pod This is an attempt to workaround L<C<log_fatal> |
142
|
|
|
|
|
|
|
#pod drawback|https://github.com/rjbs/Dist-Zilla/issues/397>: in contrast to C<log_fatal>, C<abort> |
143
|
|
|
|
|
|
|
#pod guarantees the message (which can be quite long) appears on the screen only once. |
144
|
|
|
|
|
|
|
#pod |
145
|
|
|
|
|
|
|
#pod The method log the message (via C<log>), then flush C<STDOUT>, then C<die>s with short message |
146
|
|
|
|
|
|
|
#pod C<"Aborting...\n">. |
147
|
|
|
|
|
|
|
#pod |
148
|
|
|
|
|
|
|
#pod =cut |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub abort { ## no critic ( RequireArgUnpacking ) |
151
|
7
|
|
|
7
|
1
|
136914
|
my $self = shift( @_ ); |
152
|
7
|
100
|
|
|
|
34
|
if ( @_ ) { |
153
|
2
|
|
|
|
|
9
|
$self->log( @_ ); |
154
|
|
|
|
|
|
|
}; |
155
|
7
|
|
|
|
|
626
|
STDOUT->flush(); |
156
|
7
|
|
|
|
|
71
|
die( "Aborting...\n" ); |
157
|
|
|
|
|
|
|
}; |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------- |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
#pod =method abort_if_error |
162
|
|
|
|
|
|
|
#pod |
163
|
|
|
|
|
|
|
#pod $self->abort_if_error( @items ); |
164
|
|
|
|
|
|
|
#pod $self->abort_if_error( \%args, @items ); |
165
|
|
|
|
|
|
|
#pod |
166
|
|
|
|
|
|
|
#pod If there was any errors (i. e. C<error_count> is greater than zero), the logs all the arguments and |
167
|
|
|
|
|
|
|
#pod aborts execution. Both actions (logging and aborting) are implemented by calling C<abort>. |
168
|
|
|
|
|
|
|
#pod |
169
|
|
|
|
|
|
|
#pod =cut |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
sub abort_if_error { ## no critic ( RequireArgUnpacking ) |
172
|
7
|
|
|
7
|
1
|
48011
|
my $self = shift( @_ ); |
173
|
7
|
100
|
|
|
|
308
|
if ( $self->error_count ) { |
174
|
5
|
|
|
|
|
17
|
$self->abort( @_ ); |
175
|
|
|
|
|
|
|
}; |
176
|
2
|
|
|
|
|
5
|
return; |
177
|
|
|
|
|
|
|
}; |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------- |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
#pod =method log_errors_in_file |
182
|
|
|
|
|
|
|
#pod |
183
|
|
|
|
|
|
|
#pod The method intended to report errors against a file. It prints file name (and colon after it), then |
184
|
|
|
|
|
|
|
#pod prints line-numbered file content annotated by error messages. The method does not print entire |
185
|
|
|
|
|
|
|
#pod file content, but only error lines with surrounding context (2 lines above and below each error |
186
|
|
|
|
|
|
|
#pod line). |
187
|
|
|
|
|
|
|
#pod |
188
|
|
|
|
|
|
|
#pod $self->log_errors_in_file( |
189
|
|
|
|
|
|
|
#pod $file, |
190
|
|
|
|
|
|
|
#pod $linenum1 => $message1, |
191
|
|
|
|
|
|
|
#pod $linenum2 => $message2, |
192
|
|
|
|
|
|
|
#pod $linenum3 => [ $message3a, $message3b, ... ], |
193
|
|
|
|
|
|
|
#pod ... |
194
|
|
|
|
|
|
|
#pod ); |
195
|
|
|
|
|
|
|
#pod |
196
|
|
|
|
|
|
|
#pod C<$file> should be a C<Dist::Zilla> file (e. g. C<Dist::Zilla::File::OnDisk>, |
197
|
|
|
|
|
|
|
#pod C<Dist::Zilla::File::InMemory>, or does role C<Dist::Zilla::Role::File>). |
198
|
|
|
|
|
|
|
#pod |
199
|
|
|
|
|
|
|
#pod Errors are specified by pairs C<< $linenum => $message >>, where C<$linenum> is a number of problem |
200
|
|
|
|
|
|
|
#pod line (one-based), and C<$message> is an error message (C<Str>) or array of messages |
201
|
|
|
|
|
|
|
#pod (C<ArrayRef[Str]>). Order of errors does not matter usually. However, if errors are associated with |
202
|
|
|
|
|
|
|
#pod the same line (the same line number may appear multiple times), they will be printed in order of |
203
|
|
|
|
|
|
|
#pod appearance. |
204
|
|
|
|
|
|
|
#pod |
205
|
|
|
|
|
|
|
#pod Zero or negative line numbers, or line numbers beyond the last line are invalid. Messages |
206
|
|
|
|
|
|
|
#pod associated with invalid line numbers are reported in unspecified way. |
207
|
|
|
|
|
|
|
#pod |
208
|
|
|
|
|
|
|
#pod Normally, the method prints all the information by calling C<log_error> method and returns a |
209
|
|
|
|
|
|
|
#pod positive integer. However, If any invalid line numbers are specified, the method returns negative |
210
|
|
|
|
|
|
|
#pod integer. If no errors are specified, the method prints "No errors found at I<file>." by calling |
211
|
|
|
|
|
|
|
#pod C<log> (not C<log_error>!) and returns zero. |
212
|
|
|
|
|
|
|
#pod |
213
|
|
|
|
|
|
|
#pod TODO: Example. |
214
|
|
|
|
|
|
|
#pod |
215
|
|
|
|
|
|
|
#pod =cut |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub log_errors_in_file { |
218
|
|
|
|
|
|
|
|
219
|
4
|
|
|
4
|
1
|
220231
|
my ( $self, $file, @errors ) = @_; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# Corner case: no errors specified. |
222
|
4
|
100
|
|
|
|
24
|
if ( not @errors ) { |
223
|
1
|
|
|
|
|
10
|
$self->log( [ 'No errors at %s.', $file->name ] ); |
224
|
1
|
|
|
|
|
400
|
return 0; |
225
|
|
|
|
|
|
|
}; |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# TODO: Chop too long lines? |
228
|
3
|
|
|
|
|
21
|
my $text = [ split( "\n", $file->content ) ]; |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# Parse `@errors`. |
231
|
3
|
|
|
|
|
315
|
my %errors; # Key is line number, value is arrayref to error messages. |
232
|
|
|
|
|
|
|
my %invalid; # The same but for invalid line numbers. |
233
|
3
|
|
|
|
|
13
|
while ( @errors ) { |
234
|
10
|
|
|
|
|
22
|
my ( $n, $msg ) = splice( @errors, 0, 2 ); |
235
|
10
|
100
|
100
|
|
|
57
|
if ( 1 <= $n and $n <= @$text ) { |
236
|
6
|
|
|
|
|
12
|
my $ctx = 2; # TODO: Parametrize it? |
237
|
6
|
|
|
|
|
37
|
for my $k ( max( $n - $ctx, 1 ) .. min( $n + $ctx, @$text + 0 ) ) { |
238
|
27
|
100
|
|
|
|
68
|
if ( not $errors{ $k } ) { |
239
|
19
|
|
|
|
|
54
|
$errors{ $k } = []; |
240
|
|
|
|
|
|
|
}; |
241
|
|
|
|
|
|
|
}; |
242
|
6
|
100
|
|
|
|
10
|
push( @{ $errors{ $n } }, ref( $msg ) ? @$msg : $msg ); |
|
6
|
|
|
|
|
36
|
|
243
|
|
|
|
|
|
|
} else { |
244
|
4
|
50
|
|
|
|
5
|
push( @{ $invalid{ $n } }, ref( $msg ) ? @$msg : $msg ); |
|
4
|
|
|
|
|
25
|
|
245
|
|
|
|
|
|
|
}; |
246
|
|
|
|
|
|
|
}; |
247
|
|
|
|
|
|
|
|
248
|
3
|
|
|
|
|
7
|
my $t = ' ' x 4; # Indent for text lines. |
249
|
|
|
|
|
|
|
|
250
|
3
|
50
|
|
|
|
10
|
if ( %errors ) { |
251
|
|
|
|
|
|
|
|
252
|
3
|
|
|
|
|
9
|
my $w = length( 0 + @$text ); # Width of linenumber column. |
253
|
3
|
|
|
|
|
10
|
my $e = $t . ( ' ' x ( $w + 2 ) ); # Indent for error messages. |
254
|
3
|
|
|
|
|
6
|
my $last = 0; # Number of the last printed text line. |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
my $log_line = sub { # Log text line number and content. |
257
|
20
|
|
|
20
|
|
25
|
my ( $n ) = @_; |
258
|
20
|
50
|
|
|
|
58
|
my $line = $n <= @$text ? $text->[ $n - 1 ] : ''; |
259
|
20
|
|
|
|
|
42
|
chomp( $line ); |
260
|
20
|
|
|
|
|
64
|
$self->log_error( [ '%s%0*d: %s', $t, $w, $n, $line ] ); |
261
|
3
|
|
|
|
|
17
|
}; |
262
|
|
|
|
|
|
|
my $log_messages = sub { # Log error messahes. |
263
|
19
|
|
|
19
|
|
31
|
my ( $n ) = @_; |
264
|
19
|
|
|
|
|
22
|
$self->log_error( [ '%s^^^ %s ^^^', $e, $_ ] ) for @{ $errors{ $n } }; |
|
19
|
|
|
|
|
64
|
|
265
|
3
|
|
|
|
|
13
|
}; |
266
|
|
|
|
|
|
|
my $log_skipped = sub { # Log number of skipped lines. |
267
|
22
|
|
|
22
|
|
34
|
my ( $n ) = @_; |
268
|
22
|
100
|
|
|
|
67
|
if ( $n > $last + 1 ) { # There are skipped lines. |
269
|
6
|
|
|
|
|
8
|
my $count = $n - $last - 1; # Number of skipped lines. |
270
|
6
|
100
|
|
|
|
14
|
if ( $count == 1 ) { |
271
|
1
|
|
|
|
|
4
|
$log_line->( $n - 1 ); # There is no sense to skip one line. |
272
|
|
|
|
|
|
|
} else { |
273
|
5
|
|
|
|
|
18
|
$self->log_error( [ '%s... skipped %d lines ...', $e, $count ] ); |
274
|
|
|
|
|
|
|
}; |
275
|
|
|
|
|
|
|
}; |
276
|
3
|
|
|
|
|
12
|
}; |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# Do actual logging. |
279
|
3
|
|
|
|
|
19
|
$self->log_error( [ '%s:', $file->name ] ); |
280
|
3
|
|
|
|
|
43
|
for my $n ( sort( { $a <=> $b } keys( %errors ) ) ) { |
|
32
|
|
|
|
|
58
|
|
281
|
19
|
|
|
|
|
35
|
$log_skipped->( $n ); |
282
|
19
|
|
|
|
|
39
|
$log_line->( $n ); |
283
|
19
|
|
|
|
|
50
|
$log_messages->( $n ); |
284
|
19
|
|
|
|
|
37
|
$last = $n; |
285
|
|
|
|
|
|
|
}; |
286
|
3
|
|
|
|
|
10
|
$log_skipped->( @$text + 1 ); |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
}; |
289
|
|
|
|
|
|
|
|
290
|
3
|
100
|
|
|
|
13
|
if ( %invalid ) { |
291
|
1
|
|
|
|
|
3
|
$self->log_error( 'Following errors are reported against non-existing lines of the file:' ); |
292
|
1
|
|
|
|
|
4
|
for my $n ( sort( { $a <=> $b } keys( %invalid ) ) ) { |
|
5
|
|
|
|
|
11
|
|
293
|
|
|
|
|
|
|
$self->log_error( [ '%s%s at %s line %d.', $t, $_, $file->name, $n ] ) |
294
|
4
|
|
|
|
|
6
|
for @{ $invalid{ $n } }; |
|
4
|
|
|
|
|
50
|
|
295
|
|
|
|
|
|
|
}; |
296
|
1
|
|
|
|
|
8
|
return -1; |
297
|
|
|
|
|
|
|
}; |
298
|
|
|
|
|
|
|
|
299
|
2
|
|
|
|
|
22
|
return 1; |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
}; |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------- |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
1; |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# -------------------------------------------------------------------------------------------------- |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
#pod =head1 NOTES |
310
|
|
|
|
|
|
|
#pod |
311
|
|
|
|
|
|
|
#pod All the methods defined in the role log items through the C<log> method. C<Dist::Zilla> takes this |
312
|
|
|
|
|
|
|
#pod method from C<Log::Dispatchouli>, the latter uses C<String::Flogger> to process the messages. It |
313
|
|
|
|
|
|
|
#pod means you can use C<String::Flogger> tricks, e. g.: |
314
|
|
|
|
|
|
|
#pod |
315
|
|
|
|
|
|
|
#pod $self->log_error( [ 'oops at %s line %d', $file, $line ] ); |
316
|
|
|
|
|
|
|
#pod # [] are shorter than sprintf. |
317
|
|
|
|
|
|
|
#pod |
318
|
|
|
|
|
|
|
#pod Also note how C<Log::Dispatchouli> describes the C<log> method: |
319
|
|
|
|
|
|
|
#pod |
320
|
|
|
|
|
|
|
#pod $logger->log( @messages ); |
321
|
|
|
|
|
|
|
#pod |
322
|
|
|
|
|
|
|
#pod and says: |
323
|
|
|
|
|
|
|
#pod |
324
|
|
|
|
|
|
|
#pod Each message is flogged individually, then joined with spaces. |
325
|
|
|
|
|
|
|
#pod |
326
|
|
|
|
|
|
|
#pod So beware. A call |
327
|
|
|
|
|
|
|
#pod |
328
|
|
|
|
|
|
|
#pod $self->log_error( 'error 1', 'error 2' ); |
329
|
|
|
|
|
|
|
#pod |
330
|
|
|
|
|
|
|
#pod logs I<one> message "error 1 error 2", I<not> I<two> messages "error 1" and "error 2", and bumps |
331
|
|
|
|
|
|
|
#pod C<error_count> by 1, not 2. |
332
|
|
|
|
|
|
|
#pod |
333
|
|
|
|
|
|
|
#pod =head1 SEE ALSO |
334
|
|
|
|
|
|
|
#pod |
335
|
|
|
|
|
|
|
#pod =for :list |
336
|
|
|
|
|
|
|
#pod = L<Dist::Zilla> |
337
|
|
|
|
|
|
|
#pod = L<Dist::Zilla::Role> |
338
|
|
|
|
|
|
|
#pod = L<Dist::Zilla::Plugin> |
339
|
|
|
|
|
|
|
#pod = L<Log::Dispatchouli> |
340
|
|
|
|
|
|
|
#pod = L<String::Flogger> |
341
|
|
|
|
|
|
|
#pod |
342
|
|
|
|
|
|
|
#pod =head1 COPYRIGHT AND LICENSE |
343
|
|
|
|
|
|
|
#pod |
344
|
|
|
|
|
|
|
#pod Copyright (C) 2015 Van de Bugger |
345
|
|
|
|
|
|
|
#pod |
346
|
|
|
|
|
|
|
#pod License GPLv3+: The GNU General Public License version 3 or later |
347
|
|
|
|
|
|
|
#pod <http://www.gnu.org/licenses/gpl-3.0.txt>. |
348
|
|
|
|
|
|
|
#pod |
349
|
|
|
|
|
|
|
#pod This is free software: you are free to change and redistribute it. There is |
350
|
|
|
|
|
|
|
#pod NO WARRANTY, to the extent permitted by law. |
351
|
|
|
|
|
|
|
#pod |
352
|
|
|
|
|
|
|
#pod |
353
|
|
|
|
|
|
|
#pod =cut |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
# doc/what.pod # |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
#pod =encoding UTF-8 |
358
|
|
|
|
|
|
|
#pod |
359
|
|
|
|
|
|
|
#pod =head1 WHAT? |
360
|
|
|
|
|
|
|
#pod |
361
|
|
|
|
|
|
|
#pod C<Dist-Zilla-Role-ErrorLogger> is a C<Dist::Zilla> role. It provides C<log_error>, C<abort>, and |
362
|
|
|
|
|
|
|
#pod C<abort_if_error> methods to consuming plugins. |
363
|
|
|
|
|
|
|
#pod |
364
|
|
|
|
|
|
|
#pod =cut |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# end of file # |
367
|
|
|
|
|
|
|
# doc/why.pod # |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
#pod =encoding UTF-8 |
370
|
|
|
|
|
|
|
#pod |
371
|
|
|
|
|
|
|
#pod =head1 WHY? |
372
|
|
|
|
|
|
|
#pod |
373
|
|
|
|
|
|
|
#pod C<Dist::Zilla> limits logging capabilities with 3 logging levels available in plugins through |
374
|
|
|
|
|
|
|
#pod C<log_debug>, C<log>, and C<log_fatal> methods. Debug level messages are turned off by default, the |
375
|
|
|
|
|
|
|
#pod first fatal message terminates C<Dist::Zilla>. This is simple, but sometimes you may want to report |
376
|
|
|
|
|
|
|
#pod all the errors, instead of stopping at the first found one. In such a case C<log_fatal> cannot be |
377
|
|
|
|
|
|
|
#pod used, obviously. There are few alternatives: |
378
|
|
|
|
|
|
|
#pod |
379
|
|
|
|
|
|
|
#pod Collect error messages in an array, then report all the errors with single C<log_fatal> call: |
380
|
|
|
|
|
|
|
#pod |
381
|
|
|
|
|
|
|
#pod my @errors; |
382
|
|
|
|
|
|
|
#pod ⦠|
383
|
|
|
|
|
|
|
#pod push( @errors, ⦠); |
384
|
|
|
|
|
|
|
#pod ⦠|
385
|
|
|
|
|
|
|
#pod if ( @errors ) { |
386
|
|
|
|
|
|
|
#pod $self->log_fatal( join( "\n", @errors ) ); |
387
|
|
|
|
|
|
|
#pod }; |
388
|
|
|
|
|
|
|
#pod |
389
|
|
|
|
|
|
|
#pod This works, but current implementation of C<log_fatal> has a disadvantage: it prints the message |
390
|
|
|
|
|
|
|
#pod twice, so output looks ugly. (See L<message handling in log_fatal is |
391
|
|
|
|
|
|
|
#pod suboptimal|https://github.com/rjbs/Dist-Zilla/issues/397>.) |
392
|
|
|
|
|
|
|
#pod |
393
|
|
|
|
|
|
|
#pod Another approach is reporting each error immediately with C<log>, counting number of reported |
394
|
|
|
|
|
|
|
#pod errors, and calling C<log_fatal> once at the end: |
395
|
|
|
|
|
|
|
#pod |
396
|
|
|
|
|
|
|
#pod my $error_count = 0; |
397
|
|
|
|
|
|
|
#pod ⦠|
398
|
|
|
|
|
|
|
#pod $self->log( 'error' ); |
399
|
|
|
|
|
|
|
#pod ++ $error_count; |
400
|
|
|
|
|
|
|
#pod ⦠|
401
|
|
|
|
|
|
|
#pod if ( $error_count ) { |
402
|
|
|
|
|
|
|
#pod $self->log_fatal( 'Aborting...' ); |
403
|
|
|
|
|
|
|
#pod }; |
404
|
|
|
|
|
|
|
#pod |
405
|
|
|
|
|
|
|
#pod This works, but incrementing the counter after each C<log> call is boring and error-prone. |
406
|
|
|
|
|
|
|
#pod C<Dist-Zilla-Role-ErrorLogger> role automates it, making plugin code shorter and more readable: |
407
|
|
|
|
|
|
|
#pod |
408
|
|
|
|
|
|
|
#pod with 'Dist-Zilla-Role-ErrorLogger'; |
409
|
|
|
|
|
|
|
#pod ⦠|
410
|
|
|
|
|
|
|
#pod $self->log_error( 'error' ); |
411
|
|
|
|
|
|
|
#pod ⦠|
412
|
|
|
|
|
|
|
#pod $self->abort_if_error(); |
413
|
|
|
|
|
|
|
#pod |
414
|
|
|
|
|
|
|
#pod =cut |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# end of file # |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# end of file # |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
__END__ |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=pod |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
=encoding UTF-8 |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
=head1 NAME |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
Dist::Zilla::Role::ErrorLogger - Have error logging capabilities in your Dist::Zilla plugin |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
=head1 VERSION |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
Version v0.8.0, released on 2015-10-24 17:27 UTC. |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=head1 WHAT? |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
C<Dist-Zilla-Role-ErrorLogger> is a C<Dist::Zilla> role. It provides C<log_error>, C<abort>, and |
438
|
|
|
|
|
|
|
C<abort_if_error> methods to consuming plugins. |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
This is C<Dist::Zilla::Role::ErrorLogger> role documentation. Read this if you want to |
441
|
|
|
|
|
|
|
have error logging capabilities in your Dist::Zilla plugin. |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=head1 SYNOPSIS |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
package Dist::Zilla::Plugin::YourPlugin; |
446
|
|
|
|
|
|
|
use Moose; |
447
|
|
|
|
|
|
|
use namespace::autoclean; |
448
|
|
|
|
|
|
|
with 'Dist::Zilla::Role::Plugin'; |
449
|
|
|
|
|
|
|
with 'Dist::Zilla::Role::ErrorLogger'; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
sub method { |
452
|
|
|
|
|
|
|
my $self = shift( @_ ); |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
if ( $cond ) { $self->log_error( 'error message' ); }; |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
do_something or $self->log_error( 'another error message' ); |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
while ( $cond ) { |
459
|
|
|
|
|
|
|
do_something_else or $self->log_error( 'error message' ) and next; |
460
|
|
|
|
|
|
|
}; |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
$self->log_errors_in_file( |
463
|
|
|
|
|
|
|
$file, |
464
|
|
|
|
|
|
|
1 => 'error message', # Error at file line 1. |
465
|
|
|
|
|
|
|
5 => 'another error message', # Error at file line 5. |
466
|
|
|
|
|
|
|
); |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
$self->abort_if_error( 'errors found' ); |
469
|
|
|
|
|
|
|
}; |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
472
|
|
|
|
|
|
|
1; |
473
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=head1 DESCRIPTION |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
The role extends standard C<Dist::Zilla> logging capabilities with few methods a bit more |
477
|
|
|
|
|
|
|
convenient for reporting (multiple) errors than brutal C<log_fatal>. See L</"WHY?"> for more |
478
|
|
|
|
|
|
|
details. |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
The role requires C<log> method in the consumer. |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=head1 OBJECT ATTRIBUTES |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=head2 error_count |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
$int = $self->error_count; |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
C<Int>, read-only. Number of logged errors (i. e. number of made C<log_error> calls). |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=head1 OBJECT METHODS |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=head2 log_error |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
$self->log_error( @items ); |
495
|
|
|
|
|
|
|
$self->log_error( \%args, @items ); |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
This method calls C<log> method, passing all the arguments, and increments value of C<error_count> |
498
|
|
|
|
|
|
|
attribute. The method returns true value, so can be used in following constructs: |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
while ( ⦠) { |
501
|
|
|
|
|
|
|
do_something or $self->log_error( 'message' ) and next; |
502
|
|
|
|
|
|
|
}; |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=head2 abort |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
$self->abort( @items ); |
507
|
|
|
|
|
|
|
$self->abort( \%args, @items ); |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
This is an attempt to workaround L<C<log_fatal> |
510
|
|
|
|
|
|
|
drawback|https://github.com/rjbs/Dist-Zilla/issues/397>: in contrast to C<log_fatal>, C<abort> |
511
|
|
|
|
|
|
|
guarantees the message (which can be quite long) appears on the screen only once. |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
The method log the message (via C<log>), then flush C<STDOUT>, then C<die>s with short message |
514
|
|
|
|
|
|
|
C<"Aborting...\n">. |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=head2 abort_if_error |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
$self->abort_if_error( @items ); |
519
|
|
|
|
|
|
|
$self->abort_if_error( \%args, @items ); |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
If there was any errors (i. e. C<error_count> is greater than zero), the logs all the arguments and |
522
|
|
|
|
|
|
|
aborts execution. Both actions (logging and aborting) are implemented by calling C<abort>. |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=head2 log_errors_in_file |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
The method intended to report errors against a file. It prints file name (and colon after it), then |
527
|
|
|
|
|
|
|
prints line-numbered file content annotated by error messages. The method does not print entire |
528
|
|
|
|
|
|
|
file content, but only error lines with surrounding context (2 lines above and below each error |
529
|
|
|
|
|
|
|
line). |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
$self->log_errors_in_file( |
532
|
|
|
|
|
|
|
$file, |
533
|
|
|
|
|
|
|
$linenum1 => $message1, |
534
|
|
|
|
|
|
|
$linenum2 => $message2, |
535
|
|
|
|
|
|
|
$linenum3 => [ $message3a, $message3b, ... ], |
536
|
|
|
|
|
|
|
... |
537
|
|
|
|
|
|
|
); |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
C<$file> should be a C<Dist::Zilla> file (e. g. C<Dist::Zilla::File::OnDisk>, |
540
|
|
|
|
|
|
|
C<Dist::Zilla::File::InMemory>, or does role C<Dist::Zilla::Role::File>). |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
Errors are specified by pairs C<< $linenum => $message >>, where C<$linenum> is a number of problem |
543
|
|
|
|
|
|
|
line (one-based), and C<$message> is an error message (C<Str>) or array of messages |
544
|
|
|
|
|
|
|
(C<ArrayRef[Str]>). Order of errors does not matter usually. However, if errors are associated with |
545
|
|
|
|
|
|
|
the same line (the same line number may appear multiple times), they will be printed in order of |
546
|
|
|
|
|
|
|
appearance. |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
Zero or negative line numbers, or line numbers beyond the last line are invalid. Messages |
549
|
|
|
|
|
|
|
associated with invalid line numbers are reported in unspecified way. |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
Normally, the method prints all the information by calling C<log_error> method and returns a |
552
|
|
|
|
|
|
|
positive integer. However, If any invalid line numbers are specified, the method returns negative |
553
|
|
|
|
|
|
|
integer. If no errors are specified, the method prints "No errors found at I<file>." by calling |
554
|
|
|
|
|
|
|
C<log> (not C<log_error>!) and returns zero. |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
TODO: Example. |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
=head1 WHY? |
559
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
C<Dist::Zilla> limits logging capabilities with 3 logging levels available in plugins through |
561
|
|
|
|
|
|
|
C<log_debug>, C<log>, and C<log_fatal> methods. Debug level messages are turned off by default, the |
562
|
|
|
|
|
|
|
first fatal message terminates C<Dist::Zilla>. This is simple, but sometimes you may want to report |
563
|
|
|
|
|
|
|
all the errors, instead of stopping at the first found one. In such a case C<log_fatal> cannot be |
564
|
|
|
|
|
|
|
used, obviously. There are few alternatives: |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
Collect error messages in an array, then report all the errors with single C<log_fatal> call: |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
my @errors; |
569
|
|
|
|
|
|
|
⦠|
570
|
|
|
|
|
|
|
push( @errors, ⦠); |
571
|
|
|
|
|
|
|
⦠|
572
|
|
|
|
|
|
|
if ( @errors ) { |
573
|
|
|
|
|
|
|
$self->log_fatal( join( "\n", @errors ) ); |
574
|
|
|
|
|
|
|
}; |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
This works, but current implementation of C<log_fatal> has a disadvantage: it prints the message |
577
|
|
|
|
|
|
|
twice, so output looks ugly. (See L<message handling in log_fatal is |
578
|
|
|
|
|
|
|
suboptimal|https://github.com/rjbs/Dist-Zilla/issues/397>.) |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
Another approach is reporting each error immediately with C<log>, counting number of reported |
581
|
|
|
|
|
|
|
errors, and calling C<log_fatal> once at the end: |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
my $error_count = 0; |
584
|
|
|
|
|
|
|
⦠|
585
|
|
|
|
|
|
|
$self->log( 'error' ); |
586
|
|
|
|
|
|
|
++ $error_count; |
587
|
|
|
|
|
|
|
⦠|
588
|
|
|
|
|
|
|
if ( $error_count ) { |
589
|
|
|
|
|
|
|
$self->log_fatal( 'Aborting...' ); |
590
|
|
|
|
|
|
|
}; |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
This works, but incrementing the counter after each C<log> call is boring and error-prone. |
593
|
|
|
|
|
|
|
C<Dist-Zilla-Role-ErrorLogger> role automates it, making plugin code shorter and more readable: |
594
|
|
|
|
|
|
|
|
595
|
|
|
|
|
|
|
with 'Dist-Zilla-Role-ErrorLogger'; |
596
|
|
|
|
|
|
|
⦠|
597
|
|
|
|
|
|
|
$self->log_error( 'error' ); |
598
|
|
|
|
|
|
|
⦠|
599
|
|
|
|
|
|
|
$self->abort_if_error(); |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=for test_synopsis my ( $cond, $file ); |
602
|
|
|
|
|
|
|
sub Dist::Zilla::Plugin::YourPlugin::do_something; |
603
|
|
|
|
|
|
|
sub Dist::Zilla::Plugin::YourPlugin::do_something_else; |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=head1 NOTES |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
All the methods defined in the role log items through the C<log> method. C<Dist::Zilla> takes this |
608
|
|
|
|
|
|
|
method from C<Log::Dispatchouli>, the latter uses C<String::Flogger> to process the messages. It |
609
|
|
|
|
|
|
|
means you can use C<String::Flogger> tricks, e. g.: |
610
|
|
|
|
|
|
|
|
611
|
|
|
|
|
|
|
$self->log_error( [ 'oops at %s line %d', $file, $line ] ); |
612
|
|
|
|
|
|
|
# [] are shorter than sprintf. |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
Also note how C<Log::Dispatchouli> describes the C<log> method: |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
$logger->log( @messages ); |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
and says: |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
Each message is flogged individually, then joined with spaces. |
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
So beware. A call |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
$self->log_error( 'error 1', 'error 2' ); |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
logs I<one> message "error 1 error 2", I<not> I<two> messages "error 1" and "error 2", and bumps |
627
|
|
|
|
|
|
|
C<error_count> by 1, not 2. |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
=head1 SEE ALSO |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
=over 4 |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
=item L<Dist::Zilla> |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
=item L<Dist::Zilla::Role> |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
=item L<Dist::Zilla::Plugin> |
638
|
|
|
|
|
|
|
|
639
|
|
|
|
|
|
|
=item L<Log::Dispatchouli> |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
=item L<String::Flogger> |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
=back |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=head1 AUTHOR |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
Van de Bugger <van.de.bugger@gmail.com> |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
Copyright (C) 2015 Van de Bugger |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
License GPLv3+: The GNU General Public License version 3 or later |
654
|
|
|
|
|
|
|
<http://www.gnu.org/licenses/gpl-3.0.txt>. |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
This is free software: you are free to change and redistribute it. There is |
657
|
|
|
|
|
|
|
NO WARRANTY, to the extent permitted by law. |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
=cut |