File Coverage

blib/lib/Data/Visitor.pm
Criterion Covered Total %
statement 204 236 86.4
branch 52 72 72.2
condition 19 43 44.1
subroutine 38 41 92.6
pod 20 28 71.4
total 333 420 79.2


line stmt bran cond sub pod time code
1             package Data::Visitor; # git description: Data-Visitor-0.27-23-g8b10b8a
2 9     9   221160 use Moose;
  9         1447902  
  9         61  
3             # ABSTRACT: Visitor style traversal of Perl data structures
4              
5             our $VERSION = '0.31';
6 9     9   62330 use Scalar::Util qw/blessed refaddr reftype weaken isweak/;
  9         19  
  9         743  
7 9     9   58 use overload ();
  9         30  
  9         213  
8 9     9   5450 use Symbol ();
  9         8105  
  9         261  
9              
10 9     9   4084 use Tie::ToObject;
  9         3233  
  9         313  
11              
12 9     9   62 no warnings 'recursion';
  9         18  
  9         507  
13              
14 9     9   4463 use namespace::clean -except => 'meta';
  9         57240  
  9         66  
15              
16             # the double not makes this no longer undef, so exempt from useless constant warnings in older perls
17 9   33 9   3789 use constant DEBUG => not not our $DEBUG || $ENV{DATA_VISITOR_DEBUG};
  9         21  
  9         844  
18              
19 9     9   65 use constant HAS_DATA_ALIAS => eval { +require Data::Alias; 1 };
  9         21  
  9         16  
  9         27528  
  0         0  
20              
21             has tied_as_objects => (
22             isa => "Bool",
23             is => "rw",
24             );
25              
26             # currently broken
27             has weaken => (
28             isa => "Bool",
29             is => "rw",
30             default => 0,
31             );
32              
33             sub trace {
34 0     0 1 0 my ( $self, $category, @msg ) = @_;
35              
36 0         0 our %DEBUG;
37              
38 0 0 0     0 if ( $DEBUG{$category} or !exists($DEBUG{$category}) ) {
39             $self->_print_trace("$self: " . join("",
40             ( " " x ( $self->{depth} - 1 ) ),
41 0         0 ( join(" ", "$category:", map { overload::StrVal($_) } @msg) ),
  0         0  
42             ));
43             }
44             }
45              
46             sub _print_trace {
47 0     0   0 my ( $self, @msg ) = @_;
48 0         0 warn "@msg\n";
49             }
50              
51             sub visit {
52 367     367 1 20852 my $self = shift;
53              
54 367         465 local $self->{depth} = (($self->{depth}||0) + 1) if DEBUG;
55 367   100     1129 my $seen_hash = local $self->{_seen} = ($self->{_seen} || {}); # delete it after we're done with the whole visit
56              
57 367         502 my @ret;
58              
59 367         624 foreach my $data ( @_ ) {
60 367         441 $self->trace( flow => visit => $data ) if DEBUG;
61              
62 367 100 66     1240 if ( my $refaddr = ref($data) && refaddr($data) ) { # only references need recursion checks
63 175 50 0     4599 $seen_hash->{weak} ||= isweak($data) if $self->weaken;
64              
65 175 100       425 if ( exists $seen_hash->{$refaddr} ) {
66 12         20 $self->trace( mapping => found_mapping => from => $data, to => $seen_hash->{$refaddr} ) if DEBUG;
67 12         42 push @ret, $self->visit_seen( $data, $seen_hash->{$refaddr} );
68 12         31 next;
69             } else {
70 163         218 $self->trace( mapping => no_mapping => $data ) if DEBUG;
71             }
72             }
73              
74 355 100       631 if ( defined wantarray ) {
75 300         626 push @ret, scalar($self->visit_no_rec_check($data));
76             } else {
77 55         137 $self->visit_no_rec_check($data);
78             }
79             }
80              
81 367 50       1461 return ( @_ == 1 ? $ret[0] : @ret );
82             }
83              
84             sub visit_seen {
85 3     3 1 7 my ( $self, $data, $result ) = @_;
86 3         7 return $result;
87             }
88              
89             sub _get_mapping {
90 0     0   0 my ( $self, $data ) = @_;
91 0         0 $self->{_seen}{ refaddr($data) };
92             }
93              
94             sub _register_mapping {
95 193     193   358 my ( $self, $data, $new_data ) = @_;
96 193 50       422 return $new_data unless ref $data;
97 193         241 $self->trace( mapping => register_mapping => from => $data, to => $new_data, in => (caller(1))[3] ) if DEBUG;
98 193         890 $self->{_seen}{ refaddr($data) } = $new_data;
99             }
100              
101             sub visit_no_rec_check {
102 355     355 1 673 my ( $self, $data ) = @_;
103              
104 355 100       1072 if ( blessed($data) ) {
    100          
105 35         103 return $self->visit_object($_[1]);
106             } elsif ( ref $data ) {
107 128         385 return $self->visit_ref($_[1]);
108             }
109              
110 192         440 return $self->visit_value($_[1]);
111             }
112              
113             sub visit_object {
114 10     10 1 18 my ( $self, $object ) = @_;
115 10         15 $self->trace( flow => visit_object => $object ) if DEBUG;
116              
117 10 100       25 if ( not defined wantarray ) {
118 5         13 $self->_register_mapping( $object, $object );
119 5         13 $self->visit_value($_[1]);
120 5         13 return;
121             } else {
122 5         11 return $self->_register_mapping( $object, $self->visit_value($_[1]) );
123             }
124             }
125              
126             sub visit_ref {
127 137     137 1 251 my ( $self, $data ) = @_;
128              
129 137         184 local $self->{depth} = (($self->{depth}||0) + 1) if DEBUG;
130              
131 137         174 $self->trace( flow => visit_ref => $data ) if DEBUG;
132              
133 137         331 my $reftype = reftype $data;
134              
135 137 50       421 $reftype = "SCALAR" if $reftype =~ /^(?:REF|LVALUE|VSTRING)$/;
136              
137 137   50     715 my $method = $self->can(lc "visit_$reftype") || "visit_value";
138              
139 137         2492 return $self->$method($_[1]);
140             }
141              
142             sub visit_value {
143 38     38 1 121 my ( $self, $value ) = @_;
144 38         44 $self->trace( flow => visit_value => $value ) if DEBUG;
145 38         91 return $value;
146             }
147              
148             sub visit_hash {
149 61     61 1 142 my ( $self, $hash ) = @_;
150              
151 61         84 local $self->{depth} = (($self->{depth}||0) + 1) if DEBUG;
152              
153 61 100 100     494 if ( defined(tied(%$hash)) and $self->tied_as_objects ) {
154 5         17 return $self->visit_tied_hash(tied(%$hash), $_[1]);
155             } else {
156 56         163 return $self->visit_normal_hash($_[1]);
157             }
158             }
159              
160             sub visit_normal_hash {
161 56     56 0 99 my ( $self, $hash ) = @_;
162              
163 56 100       114 if ( defined wantarray ) {
164 51         97 my $new_hash = {};
165 51         155 $self->_register_mapping( $hash, $new_hash );
166              
167 51         158 %$new_hash = $self->visit_hash_entries($_[1]);
168              
169 51         171 return $self->retain_magic( $_[1], $new_hash );
170             } else {
171 5         14 $self->_register_mapping($hash, $hash);
172 5         19 $self->visit_hash_entries($_[1]);
173 5         14 return;
174             }
175             }
176              
177             sub visit_tied_hash {
178 5     5 0 12 my ( $self, $tied, $hash ) = @_;
179              
180 5 100       10 if ( defined wantarray ) {
181 4         7 my $new_hash = {};
182 4         12 $self->_register_mapping( $hash, $new_hash );
183              
184 4 50       12 if ( blessed(my $new_tied = $self->visit_tied($_[1], $_[2])) ) {
185 4         7 $self->trace( data => tying => var => $new_hash, to => $new_tied ) if DEBUG;
186 4         34 tie %$new_hash, 'Tie::ToObject', $new_tied;
187 4         89 return $self->retain_magic($_[2], $new_hash);
188             } else {
189 0         0 return $self->visit_normal_hash($_[2]);
190             }
191             } else {
192 1         4 $self->_register_mapping($hash, $hash);
193 1         4 $self->visit_tied($_[1], $_[2]);
194 1         2 return;
195             }
196             }
197              
198             sub visit_hash_entries {
199 56     56 1 112 my ( $self, $hash ) = @_;
200              
201 56 100       115 if ( not defined wantarray ) {
202 5         29 $self->visit_hash_entry( $_, $hash->{$_}, $hash ) for keys %$hash;
203             } else {
204 51         194 return map { $self->visit_hash_entry( $_, $hash->{$_}, $hash ) } keys %$hash;
  117         403  
205             }
206             }
207              
208             sub visit_hash_entry {
209 120     120 1 284 my ( $self, $key, $value, $hash ) = @_;
210              
211 120         159 $self->trace( flow => visit_hash_entry => key => $key, value => $value ) if DEBUG;
212              
213 120 100       228 if ( not defined wantarray ) {
214 3         10 $self->visit_hash_key($key,$value,$hash);
215 3         19 $self->visit_hash_value($_[2],$key,$hash);
216             } else {
217             return (
218 117         267 $self->visit_hash_key($key,$value,$hash),
219             $self->visit_hash_value($_[2],$key,$hash),
220             );
221             }
222             }
223              
224             sub visit_hash_key {
225 118     118 1 209 my ( $self, $key, $value, $hash ) = @_;
226 118         294 $self->visit($key);
227             }
228              
229             sub visit_hash_value {
230 120     120 1 287 my ( $self, $value, $key, $hash ) = @_;
231 120         270 $self->visit($_[1]);
232             }
233              
234             sub visit_array {
235 23     23 1 77 my ( $self, $array ) = @_;
236              
237 23 100 66     172 if ( defined(tied(@$array)) and $self->tied_as_objects ) {
238 4         17 return $self->visit_tied_array(tied(@$array), $_[1]);
239             } else {
240 19         65 return $self->visit_normal_array($_[1]);
241             }
242             }
243              
244             sub visit_normal_array {
245 19     19 0 43 my ( $self, $array ) = @_;
246              
247 19 100       40 if ( defined wantarray ) {
248 16         32 my $new_array = [];
249 16         51 $self->_register_mapping( $array, $new_array );
250              
251 16         76 @$new_array = $self->visit_array_entries($_[1]);
252              
253 16         60 return $self->retain_magic( $_[1], $new_array );
254             } else {
255 3         10 $self->_register_mapping( $array, $array );
256 3         12 $self->visit_array_entries($_[1]);
257              
258 3         8 return;
259             }
260             }
261              
262             sub visit_tied_array {
263 4     4 0 9 my ( $self, $tied, $array ) = @_;
264              
265 4 100       10 if ( defined wantarray ) {
266 3         16 my $new_array = [];
267 3         12 $self->_register_mapping( $array, $new_array );
268              
269 3 50       13 if ( blessed(my $new_tied = $self->visit_tied($_[1], $_[2])) ) {
270 3         4 $self->trace( data => tying => var => $new_array, to => $new_tied ) if DEBUG;
271 3         20 tie @$new_array, 'Tie::ToObject', $new_tied;
272 3         51 return $self->retain_magic($_[2], $new_array);
273             } else {
274 0         0 return $self->visit_normal_array($_[2]);
275             }
276             } else {
277 1         4 $self->_register_mapping( $array, $array );
278 1         4 $self->visit_tied($_[1], $_[2]);
279              
280 1         3 return;
281             }
282             }
283              
284             sub visit_array_entries {
285 19     19 1 41 my ( $self, $array ) = @_;
286              
287 19 100       46 if ( not defined wantarray ) {
288 3         26 $self->visit_array_entry( $array->[$_], $_, $array ) for 0 .. $#$array;
289             } else {
290 16         49 return map { $self->visit_array_entry( $array->[$_], $_, $array ) } 0 .. $#$array;
  24         72  
291             }
292             }
293              
294             sub visit_array_entry {
295 25     25 1 59 my ( $self, $value, $index, $array ) = @_;
296 25         71 $self->visit($_[1]);
297             }
298              
299             sub visit_scalar {
300 11     11 1 28 my ( $self, $scalar ) = @_;
301              
302 11 100 66     135 if ( defined(tied($$scalar)) and $self->tied_as_objects ) {
303 4         32 return $self->visit_tied_scalar(tied($$scalar), $_[1]);
304             } else {
305 7         30 return $self->visit_normal_scalar($_[1]);
306             }
307             }
308              
309             sub visit_normal_scalar {
310 7     7 0 16 my ( $self, $scalar ) = @_;
311              
312 7 100       21 if ( defined wantarray ) {
313 6         10 my $new_scalar;
314 6         22 $self->_register_mapping( $scalar, \$new_scalar );
315              
316 6         23 $new_scalar = $self->visit( $$scalar );
317              
318 6         25 return $self->retain_magic($_[1], \$new_scalar);
319             } else {
320 1         4 $self->_register_mapping( $scalar, $scalar );
321 1         6 $self->visit( $$scalar );
322 1         2 return;
323             }
324              
325             }
326              
327             sub visit_tied_scalar {
328 4     4 0 10 my ( $self, $tied, $scalar ) = @_;
329              
330 4 100       10 if ( defined wantarray ) {
331 3         4 my $new_scalar;
332 3         9 $self->_register_mapping( $scalar, \$new_scalar );
333              
334 3 50       13 if ( blessed(my $new_tied = $self->visit_tied($_[1], $_[2])) ) {
335 3         4 $self->trace( data => tying => var => $new_scalar, to => $new_tied ) if DEBUG;
336 3         31 tie $new_scalar, 'Tie::ToObject', $new_tied;
337 3         60 return $self->retain_magic($_[2], \$new_scalar);
338             } else {
339 0         0 return $self->visit_normal_scalar($_[2]);
340             }
341             } else {
342 1         5 $self->_register_mapping( $scalar, $scalar );
343 1         4 $self->visit_tied($_[1], $_[2]);
344 1         3 return;
345             }
346             }
347              
348             sub visit_code {
349 34     34 1 65 my ( $self, $code ) = @_;
350 34         83 $self->visit_value($_[1]);
351             }
352              
353             sub visit_glob {
354 6     6 1 22 my ( $self, $glob ) = @_;
355              
356 6 100 66     123 if ( defined(tied(*$glob)) and $self->tied_as_objects ) {
357 4         15 return $self->visit_tied_glob(tied(*$glob), $_[1]);
358             } else {
359 2         14 return $self->visit_normal_glob($_[1]);
360             }
361             }
362              
363             sub visit_normal_glob {
364 2     2 0 6 my ( $self, $glob ) = @_;
365              
366 2 50       7 if ( defined wantarray ) {
367 2         13 my $new_glob = Symbol::gensym();
368 2         42 $self->_register_mapping( $glob, $new_glob );
369              
370 9     9   124 no warnings 'misc'; # Undefined value assigned to typeglob
  9         20  
  9         6270  
371 2   100     15 *$new_glob = $self->visit( *$glob{$_} || next ) for qw/SCALAR ARRAY HASH/;
372              
373 2         9 return $self->retain_magic($_[1], $new_glob);
374             } else {
375 0         0 $self->_register_mapping( $glob, $glob );
376 0   0     0 $self->visit( *$glob{$_} || next ) for qw/SCALAR ARRAY HASH/;
377 0         0 return;
378             }
379             }
380              
381             sub visit_tied_glob {
382 4     4 0 9 my ( $self, $tied, $glob ) = @_;
383              
384 4 100       10 if ( defined wantarray ) {
385 3         9 my $new_glob = Symbol::gensym();
386 3         47 $self->_register_mapping( $glob, \$new_glob );
387              
388 3 50       9 if ( blessed(my $new_tied = $self->visit_tied($_[1], $_[2])) ) {
389 3         7 $self->trace( data => tying => var => $new_glob, to => $new_tied ) if DEBUG;
390 3         28 tie *$new_glob, 'Tie::ToObject', $new_tied;
391 3         56 return $self->retain_magic($_[2], $new_glob);
392             } else {
393 0         0 return $self->visit_normal_glob($_[2]);
394             }
395             } else {
396 1         4 $self->_register_mapping( $glob, $glob );
397 1         4 $self->visit_tied($_[1], $_[2]);
398 1         46 return;
399             }
400             }
401              
402             sub retain_magic {
403 88     88 1 177 my ( $self, $proto, $new ) = @_;
404              
405 88 100 66     286 if ( blessed($proto) and !blessed($new) ) {
406 9         15 $self->trace( data => blessing => $new, ref $proto ) if DEBUG;
407 9         47 bless $new, ref $proto;
408             }
409              
410 88         150 my $seen_hash = $self->{_seen};
411 88 50       188 if ( $seen_hash->{weak} ) {
412             #if ("$]" >= '5.022') {
413             # TODO: Data::Alias does not work on recent perls, but there is built-in aliasing support now.
414             # e.g. see what Var::Pairs 0.003004 did.
415             #}
416 0 0       0 if (HAS_DATA_ALIAS) {
417 0         0 my @weak_refs;
418 0         0 foreach my $value ( Data::Alias::deref($proto) ) {
419 0 0 0     0 if ( ref $value and isweak($value) ) {
420 0         0 push @weak_refs, refaddr $value;
421             }
422             }
423              
424 0 0       0 if ( @weak_refs ) {
425 0         0 my %targets = map { refaddr($_) => 1 } @{ $self->{_seen} }{@weak_refs};
  0         0  
  0         0  
426 0         0 foreach my $value ( Data::Alias::deref($new) ) {
427 0 0 0     0 if ( ref $value and $targets{refaddr($value)}) {
428 0   0     0 push @{ $seen_hash->{weakened} ||= [] }, $value; # keep a ref around
  0         0  
429 0         0 weaken($value);
430             }
431             }
432             }
433             }
434             else {
435 0         0 die "Found a weak reference, but Data::Alias is not installed. You must install Data::Alias in order for this to work.";
436             }
437             }
438              
439             # FIXME real magic, too
440              
441 88         1410 return $new;
442             }
443              
444             sub visit_tied {
445 17     17 1 32 my ( $self, $tied, $var ) = @_;
446 17         22 $self->trace( flow => visit_tied => $tied ) if DEBUG;
447 17         42 $self->visit($_[1]); # as an object eventually
448             }
449              
450             __PACKAGE__->meta->make_immutable if __PACKAGE__->meta->can("make_immutable");
451              
452             __PACKAGE__;
453              
454             __END__
455              
456             =pod
457              
458             =encoding UTF-8
459              
460             =head1 NAME
461              
462             Data::Visitor - Visitor style traversal of Perl data structures
463              
464             =head1 VERSION
465              
466             version 0.31
467              
468             =head1 SYNOPSIS
469              
470             # NOTE
471             # You probably want to use Data::Visitor::Callback for trivial things
472              
473             package FooCounter;
474             use Moose;
475              
476             extends qw(Data::Visitor);
477              
478             has number_of_foos => (
479             isa => "Int",
480             is => "rw",
481             default => 0,
482             );
483              
484             sub visit_value {
485             my ( $self, $data ) = @_;
486              
487             if ( defined $data and $data eq "foo" ) {
488             $self->number_of_foos( $self->number_of_foos + 1 );
489             }
490              
491             return $data;
492             }
493              
494             my $counter = FooCounter->new;
495              
496             $counter->visit( {
497             this => "that",
498             some_foos => [ qw/foo foo bar foo/ ],
499             the_other => "foo",
500             });
501              
502             $counter->number_of_foos; # this is now 4
503              
504             =head1 DESCRIPTION
505              
506             This module is a simple visitor implementation for Perl values.
507              
508             It has a main dispatcher method, C<visit>, which takes a single perl value and
509             then calls the methods appropriate for that value.
510              
511             It can recursively map (cloning as necessary) or just traverse most structures,
512             with support for per object behavior, circular structures, visiting tied
513             structures, and all ref types (hashes, arrays, scalars, code, globs).
514              
515             L<Data::Visitor> is meant to be subclassed, but also ships with a callback
516             driven subclass, L<Data::Visitor::Callback>.
517              
518             =head1 METHODS
519              
520             =over 4
521              
522             =item visit $data
523              
524             This method takes any Perl value as its only argument, and dispatches to the
525             various other visiting methods using C<visit_no_rec_check>, based on the data's
526             type.
527              
528             If the value is a reference and has already been seen then C<visit_seen> is
529             called.
530              
531             =item visit_seen $data, $first_result
532              
533             When an already seen value is encountered again, it is typically replaced with
534             the result of the first visitation of that value. The value and the result of
535             the first visitation are passed as arguments.
536              
537             Returns C<$first_result>.
538              
539             =item visit_no_rec_check $data
540              
541             Called for any value that has not yet been seen. Does the actual type based
542             dispatch for C<visit>.
543              
544             Should not be called directly unless forcing a circular structure to be
545             unfolded. Use with caution as this may cause infinite recursion.
546              
547             =item visit_object $object
548              
549             If the value is a blessed object, C<visit> calls this method. The base
550             implementation will just forward to C<visit_value>.
551              
552             =item visit_ref $value
553              
554             Generic recursive visitor. All non blessed values are given to this.
555              
556             C<visit_object> can delegate to this method in order to visit the object
557             anyway.
558              
559             This will check if the visitor can handle C<visit_$reftype> (lowercase), and if
560             not delegate to C<visit_value> instead.
561              
562             =item visit_array $array_ref
563              
564             =item visit_hash $hash_ref
565              
566             =item visit_glob $glob_ref
567              
568             =item visit_code $code_ref
569              
570             =item visit_scalar $scalar_ref
571              
572             These methods are called for the corresponding container type.
573              
574             =item visit_value $value
575              
576             If the value is anything else, this method is called. The base implementation
577             will return $value.
578              
579             =item visit_hash_entries $hash
580              
581             =item visit_hash_entry $key, $value, $hash
582              
583             Delegates to C<visit_hash_key> and C<visit_hash_value>. The value is passed as
584             C<$_[2]> so that it is aliased.
585              
586             =item visit_hash_key $key, $value, $hash
587              
588             Calls C<visit> on the key and returns it.
589              
590             =item visit_hash_value $value, $key, $hash
591              
592             The value will be aliased (passed as C<$_[1]>).
593              
594             =item visit_array_entries $array
595              
596             =item visit_array_entry $value, $index, $array
597              
598             Delegates to C<visit> on value. The value is passed as C<$_[1]> to retain
599             aliasing.
600              
601             =item visit_tied $object, $var
602              
603             When C<tied_as_objects> is enabled and a tied variable (hash, array, glob or
604             scalar) is encountered this method will be called on the tied object. If a
605             valid mapped value is returned, the newly constructed result container will be
606             tied to the return value and no iteration of the contents of the data will be
607             made (since all storage is delegated to the tied object).
608              
609             If a non blessed value is returned from C<visit_tied> then the structure will
610             be iterated normally, and the result container will not be tied at all.
611              
612             This is because tying to the same class and performing the tie operations will
613             not yield the same results in many cases.
614              
615             =item retain_magic $orig, $copy
616              
617             Copies over magic from C<$orig> to C<$copy>.
618              
619             Currently only handles C<bless>. In the future this might be expanded using
620             L<Variable::Magic> but it isn't clear what the correct semantics for magic
621             copying should be.
622              
623             =item trace
624              
625             Called if the C<DEBUG> constant is set with a trace message.
626              
627             =back
628              
629             =head1 RETURN VALUE
630              
631             This object can be used as an C<fmap> of sorts - providing an ad-hoc functor
632             interface for Perl data structures.
633              
634             In void context this functionality is ignored, but in any other context the
635             default methods will all try to return a value of similar structure, with its
636             children also fmapped.
637              
638             =head1 SUBCLASSING
639              
640             Data::Visitor is a L<Moose> class, so it should be subclassed using Moose.
641              
642             Then override the callback methods in any way you like. To retain visitor
643             behavior, make sure to retain the functionality of C<visit_array> and
644             C<visit_hash>.
645              
646             =head1 TODO
647              
648             =over 4
649              
650             =item *
651              
652             Add support for "natural" visiting of trees.
653              
654             =item *
655              
656             Expand C<retain_magic> to support tying at the very least, or even more with
657             L<Variable::Magic> if possible.
658              
659             =back
660              
661             =head1 SEE ALSO
662              
663             L<Data::Rmap>, L<Tree::Simple::VisitorFactory>, L<Data::Traverse>
664              
665             L<http://en.wikipedia.org/wiki/Visitor_pattern>,
666             L<http://www.ninebynine.org/Software/Learning-Haskell-Notes.html#functors>,
667             L<http://en.wikipedia.org/wiki/Functor>
668              
669             =for Pod::Coverage HAS_DATA_ALIAS
670             visit_normal_array
671             visit_normal_glob
672             visit_normal_hash
673             visit_normal_scalar
674             visit_tied_array
675             visit_tied_glob
676             visit_tied_hash
677             visit_tied_scalar
678              
679             =head1 SUPPORT
680              
681             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Data-Visitor>
682             (or L<bug-Data-Visitor@rt.cpan.org|mailto:bug-Data-Visitor@rt.cpan.org>).
683              
684             =head1 AUTHORS
685              
686             =over 4
687              
688             =item *
689              
690             Yuval Kogman <nothingmuch@woobling.org>
691              
692             =item *
693              
694             Marcel Grünauer <marcel@cpan.org>
695              
696             =back
697              
698             =head1 CONTRIBUTORS
699              
700             =for stopwords Jesse Luehrs Florian Ragwitz Karen Etheridge David Steinbrunner Robin Smidsrød
701              
702             =over 4
703              
704             =item *
705              
706             Jesse Luehrs <doy@tozt.net>
707              
708             =item *
709              
710             Florian Ragwitz <rafl@debian.org>
711              
712             =item *
713              
714             Karen Etheridge <ether@cpan.org>
715              
716             =item *
717              
718             David Steinbrunner <dsteinbrunner@pobox.com>
719              
720             =item *
721              
722             Robin Smidsrød <robin@smidsrod.no>
723              
724             =back
725              
726             =head1 COPYRIGHT AND LICENCE
727              
728             This software is copyright (c) 2020 by Yuval Kogman.
729              
730             This is free software; you can redistribute it and/or modify it under
731             the same terms as the Perl 5 programming language system itself.
732              
733             =cut