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: v0.31-4-g7498abb
2 9     9   203630 use Moose;
  9         1407792  
  9         59  
3             # ABSTRACT: Visitor style traversal of Perl data structures
4              
5             our $VERSION = '0.32';
6 9     9   61903 use Scalar::Util qw/blessed refaddr reftype weaken isweak/;
  9         22  
  9         692  
7 9     9   67 use overload ();
  9         21  
  9         239  
8 9     9   5273 use Symbol ();
  9         8337  
  9         245  
9              
10 9     9   4038 use Tie::ToObject;
  9         3304  
  9         352  
11              
12 9     9   64 no warnings 'recursion';
  9         18  
  9         506  
13              
14 9     9   4314 use namespace::clean -except => 'meta';
  9         56522  
  9         59  
15              
16             # the double not makes this no longer undef, so exempt from useless constant warnings in older perls
17 9   33 9   3606 use constant DEBUG => not not our $DEBUG || $ENV{DATA_VISITOR_DEBUG};
  9         25  
  9         773  
18              
19 9     9   61 use constant HAS_DATA_ALIAS => eval { +require Data::Alias; 1 };
  9         18  
  9         18  
  9         26497  
  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 21109 my $self = shift;
53              
54 367         467 local $self->{depth} = (($self->{depth}||0) + 1) if DEBUG;
55 367   100     1125 my $seen_hash = local $self->{_seen} = ($self->{_seen} || {}); # delete it after we're done with the whole visit
56              
57 367         507 my @ret;
58              
59 367         636 foreach my $data ( @_ ) {
60 367         434 $self->trace( flow => visit => $data ) if DEBUG;
61              
62 367 100 66     1271 if ( my $refaddr = ref($data) && refaddr($data) ) { # only references need recursion checks
63 175 50 0     4517 $seen_hash->{weak} ||= isweak($data) if $self->weaken;
64              
65 175 100       407 if ( exists $seen_hash->{$refaddr} ) {
66 12         18 $self->trace( mapping => found_mapping => from => $data, to => $seen_hash->{$refaddr} ) if DEBUG;
67 12         55 push @ret, $self->visit_seen( $data, $seen_hash->{$refaddr} );
68 12         33 next;
69             } else {
70 163         227 $self->trace( mapping => no_mapping => $data ) if DEBUG;
71             }
72             }
73              
74 355 100       620 if ( defined wantarray ) {
75 300         654 push @ret, scalar($self->visit_no_rec_check($data));
76             } else {
77 55         143 $self->visit_no_rec_check($data);
78             }
79             }
80              
81 367 50       1543 return ( @_ == 1 ? $ret[0] : @ret );
82             }
83              
84             sub visit_seen {
85 3     3 1 6 my ( $self, $data, $result ) = @_;
86 3         6 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   357 my ( $self, $data, $new_data ) = @_;
96 193 50       398 return $new_data unless ref $data;
97 193         239 $self->trace( mapping => register_mapping => from => $data, to => $new_data, in => (caller(1))[3] ) if DEBUG;
98 193         841 $self->{_seen}{ refaddr($data) } = $new_data;
99             }
100              
101             sub visit_no_rec_check {
102 355     355 1 665 my ( $self, $data ) = @_;
103              
104 355 100       1024 if ( blessed($data) ) {
    100          
105 35         104 return $self->visit_object($_[1]);
106             } elsif ( ref $data ) {
107 128         378 return $self->visit_ref($_[1]);
108             }
109              
110 192         444 return $self->visit_value($_[1]);
111             }
112              
113             sub visit_object {
114 10     10 1 30 my ( $self, $object ) = @_;
115 10         23 $self->trace( flow => visit_object => $object ) if DEBUG;
116              
117 10 100       40 if ( not defined wantarray ) {
118 5         16 $self->_register_mapping( $object, $object );
119 5         16 $self->visit_value($_[1]);
120 5         16 return;
121             } else {
122 5         12 return $self->_register_mapping( $object, $self->visit_value($_[1]) );
123             }
124             }
125              
126             sub visit_ref {
127 137     137 1 261 my ( $self, $data ) = @_;
128              
129 137         171 local $self->{depth} = (($self->{depth}||0) + 1) if DEBUG;
130              
131 137         161 $self->trace( flow => visit_ref => $data ) if DEBUG;
132              
133 137         345 my $reftype = reftype $data;
134              
135 137 50       412 $reftype = "SCALAR" if $reftype =~ /^(?:REF|LVALUE|VSTRING)$/;
136              
137 137   50     721 my $method = $self->can(lc "visit_$reftype") || "visit_value";
138              
139 137         2190 return $self->$method($_[1]);
140             }
141              
142             sub visit_value {
143 38     38 1 77 my ( $self, $value ) = @_;
144 38         48 $self->trace( flow => visit_value => $value ) if DEBUG;
145 38         94 return $value;
146             }
147              
148             sub visit_hash {
149 61     61 1 151 my ( $self, $hash ) = @_;
150              
151 61         96 local $self->{depth} = (($self->{depth}||0) + 1) if DEBUG;
152              
153 61 100 100     497 if ( defined(tied(%$hash)) and $self->tied_as_objects ) {
154 5         25 return $self->visit_tied_hash(tied(%$hash), $_[1]);
155             } else {
156 56         157 return $self->visit_normal_hash($_[1]);
157             }
158             }
159              
160             sub visit_normal_hash {
161 56     56 0 115 my ( $self, $hash ) = @_;
162              
163 56 100       105 if ( defined wantarray ) {
164 51         87 my $new_hash = {};
165 51         152 $self->_register_mapping( $hash, $new_hash );
166              
167 51         150 %$new_hash = $self->visit_hash_entries($_[1]);
168              
169 51         171 return $self->retain_magic( $_[1], $new_hash );
170             } else {
171 5         22 $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 14 my ( $self, $tied, $hash ) = @_;
179              
180 5 100       10 if ( defined wantarray ) {
181 4         6 my $new_hash = {};
182 4         12 $self->_register_mapping( $hash, $new_hash );
183              
184 4 50       17 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         31 tie %$new_hash, 'Tie::ToObject', $new_tied;
187 4         88 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         6 $self->visit_tied($_[1], $_[2]);
194 1         3 return;
195             }
196             }
197              
198             sub visit_hash_entries {
199 56     56 1 104 my ( $self, $hash ) = @_;
200              
201 56 100       125 if ( not defined wantarray ) {
202 5         32 $self->visit_hash_entry( $_, $hash->{$_}, $hash ) for keys %$hash;
203             } else {
204 51         228 return map { $self->visit_hash_entry( $_, $hash->{$_}, $hash ) } keys %$hash;
  117         461  
205             }
206             }
207              
208             sub visit_hash_entry {
209 120     120 1 274 my ( $self, $key, $value, $hash ) = @_;
210              
211 120         153 $self->trace( flow => visit_hash_entry => key => $key, value => $value ) if DEBUG;
212              
213 120 100       224 if ( not defined wantarray ) {
214 3         9 $self->visit_hash_key($key,$value,$hash);
215 3         18 $self->visit_hash_value($_[2],$key,$hash);
216             } else {
217             return (
218 117         265 $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 235 my ( $self, $key, $value, $hash ) = @_;
226 118         301 $self->visit($key);
227             }
228              
229             sub visit_hash_value {
230 120     120 1 269 my ( $self, $value, $key, $hash ) = @_;
231 120         287 $self->visit($_[1]);
232             }
233              
234             sub visit_array {
235 23     23 1 76 my ( $self, $array ) = @_;
236              
237 23 100 66     169 if ( defined(tied(@$array)) and $self->tied_as_objects ) {
238 4         20 return $self->visit_tied_array(tied(@$array), $_[1]);
239             } else {
240 19         77 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         29 my $new_array = [];
249 16         50 $self->_register_mapping( $array, $new_array );
250              
251 16         59 @$new_array = $self->visit_array_entries($_[1]);
252              
253 16         74 return $self->retain_magic( $_[1], $new_array );
254             } else {
255 3         9 $self->_register_mapping( $array, $array );
256 3         11 $self->visit_array_entries($_[1]);
257              
258 3         7 return;
259             }
260             }
261              
262             sub visit_tied_array {
263 4     4 0 10 my ( $self, $tied, $array ) = @_;
264              
265 4 100       9 if ( defined wantarray ) {
266 3         6 my $new_array = [];
267 3         9 $self->_register_mapping( $array, $new_array );
268              
269 3 50       9 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         23 tie @$new_array, 'Tie::ToObject', $new_tied;
272 3         52 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         5 $self->visit_tied($_[1], $_[2]);
279              
280 1         6 return;
281             }
282             }
283              
284             sub visit_array_entries {
285 19     19 1 40 my ( $self, $array ) = @_;
286              
287 19 100       43 if ( not defined wantarray ) {
288 3         17 $self->visit_array_entry( $array->[$_], $_, $array ) for 0 .. $#$array;
289             } else {
290 16         46 return map { $self->visit_array_entry( $array->[$_], $_, $array ) } 0 .. $#$array;
  24         80  
291             }
292             }
293              
294             sub visit_array_entry {
295 25     25 1 58 my ( $self, $value, $index, $array ) = @_;
296 25         75 $self->visit($_[1]);
297             }
298              
299             sub visit_scalar {
300 11     11 1 34 my ( $self, $scalar ) = @_;
301              
302 11 100 66     141 if ( defined(tied($$scalar)) and $self->tied_as_objects ) {
303 4         15 return $self->visit_tied_scalar(tied($$scalar), $_[1]);
304             } else {
305 7         28 return $self->visit_normal_scalar($_[1]);
306             }
307             }
308              
309             sub visit_normal_scalar {
310 7     7 0 22 my ( $self, $scalar ) = @_;
311              
312 7 100       19 if ( defined wantarray ) {
313 6         10 my $new_scalar;
314 6         20 $self->_register_mapping( $scalar, \$new_scalar );
315              
316 6         21 $new_scalar = $self->visit( $$scalar );
317              
318 6         21 return $self->retain_magic($_[1], \$new_scalar);
319             } else {
320 1         4 $self->_register_mapping( $scalar, $scalar );
321 1         8 $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       12 if ( defined wantarray ) {
331 3         4 my $new_scalar;
332 3         10 $self->_register_mapping( $scalar, \$new_scalar );
333              
334 3 50       12 if ( blessed(my $new_tied = $self->visit_tied($_[1], $_[2])) ) {
335 3         5 $self->trace( data => tying => var => $new_scalar, to => $new_tied ) if DEBUG;
336 3         20 tie $new_scalar, 'Tie::ToObject', $new_tied;
337 3         55 return $self->retain_magic($_[2], \$new_scalar);
338             } else {
339 0         0 return $self->visit_normal_scalar($_[2]);
340             }
341             } else {
342 1         4 $self->_register_mapping( $scalar, $scalar );
343 1         6 $self->visit_tied($_[1], $_[2]);
344 1         3 return;
345             }
346             }
347              
348             sub visit_code {
349 34     34 1 61 my ( $self, $code ) = @_;
350 34         82 $self->visit_value($_[1]);
351             }
352              
353             sub visit_glob {
354 6     6 1 23 my ( $self, $glob ) = @_;
355              
356 6 100 66     127 if ( defined(tied(*$glob)) and $self->tied_as_objects ) {
357 4         22 return $self->visit_tied_glob(tied(*$glob), $_[1]);
358             } else {
359 2         16 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         12 my $new_glob = Symbol::gensym();
368 2         52 $self->_register_mapping( $glob, $new_glob );
369              
370 9     9   64 no warnings 'misc'; # Undefined value assigned to typeglob
  9         41  
  9         6379  
371 2   100     19 *$new_glob = $self->visit( *$glob{$_} || next ) for qw/SCALAR ARRAY HASH/;
372              
373 2         15 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         12 my $new_glob = Symbol::gensym();
386 3         51 $self->_register_mapping( $glob, \$new_glob );
387              
388 3 50       12 if ( blessed(my $new_tied = $self->visit_tied($_[1], $_[2])) ) {
389 3         6 $self->trace( data => tying => var => $new_glob, to => $new_tied ) if DEBUG;
390 3         30 tie *$new_glob, 'Tie::ToObject', $new_tied;
391 3         62 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         3 return;
399             }
400             }
401              
402             sub retain_magic {
403 88     88 1 179 my ( $self, $proto, $new ) = @_;
404              
405 88 100 66     278 if ( blessed($proto) and !blessed($new) ) {
406 9         13 $self->trace( data => blessing => $new, ref $proto ) if DEBUG;
407 9         27 bless $new, ref $proto;
408             }
409              
410 88         153 my $seen_hash = $self->{_seen};
411 88 50       184 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         1497 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.32
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 Karen Etheridge Florian Ragwitz David Steinbrunner Graham Knop Robin Smidsrød
701              
702             =over 4
703              
704             =item *
705              
706             Jesse Luehrs <doy@tozt.net>
707              
708             =item *
709              
710             Karen Etheridge <ether@cpan.org>
711              
712             =item *
713              
714             Florian Ragwitz <rafl@debian.org>
715              
716             =item *
717              
718             David Steinbrunner <dsteinbrunner@pobox.com>
719              
720             =item *
721              
722             Graham Knop <haarg@haarg.org>
723              
724             =item *
725              
726             Robin Smidsrød <robin@smidsrod.no>
727              
728             =back
729              
730             =head1 COPYRIGHT AND LICENCE
731              
732             This software is copyright (c) 2023 by Yuval Kogman.
733              
734             This is free software; you can redistribute it and/or modify it under
735             the same terms as the Perl 5 programming language system itself.
736              
737             =cut