File Coverage

blib/lib/DBIx/Class/Schema/PopulateMore/Visitor.pm
Criterion Covered Total %
statement 42 43 97.6
branch 11 14 78.5
condition n/a
subroutine 9 9 100.0
pod 4 4 100.0
total 66 70 94.2


line stmt bran cond sub pod time code
1             package DBIx::Class::Schema::PopulateMore::Visitor;
2              
3 3     3   11 use Moo;
  3         3  
  3         19  
4 3     3   769 use Scalar::Util qw/refaddr/;
  3         3  
  3         287  
5 3     3   1686 use Type::Library -base;
  3         55002  
  3         30  
6 3     3   3111 use Types::Standard -types;
  3         112625  
  3         42  
7 3     3   11802 use namespace::clean;
  3         9983  
  3         24  
8              
9             =head1 NAME
10              
11             DBIx::Class::Schema::PopulateMore::Visitor - Visitor for the Populate Data
12              
13             =head1 SYNOPSIS
14              
15             ##Example Usage
16              
17             See Tests for more example usage.
18              
19             =head1 DESCRIPTION
20              
21             When populating a table, sometimes we need to inflate values that we won't
22             know of in advance. For example we might have a column that is FK to another
23             column in another table. We want to make it easy to 'tag' a value as something
24             other than a real value to be inserted into the table.
25              
26             Right now we only have one substitution to do, which is the FK one mentioned
27             above, but we might eventually create other substitution types so we've broken
28             this out to make it neat and easy to do so.
29              
30             =head1 ATTRIBUTES
31              
32             This class defines the following attributes.
33              
34             =head2 update_callback
35              
36             The coderef to be execute should the match condition succeed
37              
38             =cut
39              
40             has 'update_callback' => (
41             is=>'rw',
42             required=>1,
43             lazy=>1,
44             isa=>CodeRef,
45             default=> sub {
46             return sub {
47             return shift;
48             };
49             },
50             );
51              
52             =head2 match_condition
53              
54             How we know the value is really something to inflate or perform a substitution
55             on. This get's the namespace of the substitution plugin and it's other data.
56              
57             The default behavior (where there is no substitution namespace, is to do the
58             inflate to resultset. This is the most common usecase.
59              
60             =cut
61              
62             has 'match_condition' => (
63             is=>'ro',
64             required=>1,
65             isa=>RegexpRef,
66             );
67              
68             =head2 seen
69              
70             Used to collect up ref addresses of arrays/hashes we have already seen
71              
72             =cut
73              
74             has seen => (
75             is => 'rw',
76             isa => HashRef,
77             default => sub { {} },
78             );
79              
80             =head1 METHODS
81              
82             This module defines the following methods.
83              
84             =head2 callback
85              
86             Given a coderef, sets the current callback and returns self so that we can chain
87              
88             =cut
89              
90             sub callback
91             {
92 16     16 1 756 my $self = shift @_;
93 16         468 $self->update_callback(shift @_);
94 16         2215 return $self;
95             }
96              
97             =head2 visit
98              
99             A simple visitor that only expects to perform replacements on values
100              
101             =cut
102              
103             sub visit
104             {
105 119     119 1 157 my ( $self, $target ) = @_;
106 119 100       308 if ( ref $target eq 'ARRAY' ) {
    100          
107 25         71 my $addr = refaddr $target;
108 25 50       467 return $self->seen->{$addr} if defined $self->seen->{$addr};
109 25         513 my $new_array = $self->seen->{$addr} = [];
110 25         173 @$new_array = map { $self->visit($_) } @$target;
  63         908  
111 25         1751 return $new_array;
112             }
113             elsif ( ref $target eq 'HASH' ) {
114 30         79 my $addr = refaddr $target;
115 30 50       560 return $self->seen->{$addr} if defined $self->seen->{$addr};
116 30         1728 my $new_hash = $self->seen->{$addr} = {};
117 30         216 %$new_hash = map { $_ => $self->visit( $target->{$_} ) } keys %$target;
  40         207  
118 30         845 return $new_hash;
119             }
120             else {
121 64         116 $self->visit_value($target);
122             }
123             }
124              
125             =head2 visit_value
126              
127             Here is where we make the choice as to if this value needs to be inflated via a plugin
128              
129             =cut
130              
131             sub visit_value
132             {
133 64     64 1 74 my ($self, $data) = @_;
134            
135 64 100       116 if(my $item = $self->match_or_not($data))
136             {
137 33         589 return $self->update_callback->($item);
138             }
139              
140 31         69 return $data;
141             }
142              
143              
144             =head2 match_or_not
145              
146             We break this out to handle the ugliness surrounding dealing with undef values
147             and also to make it easier on subclassers.
148              
149             =cut
150            
151             sub match_or_not
152             {
153 64     64 1 75 my ($self, $data) = @_;
154 64         137 my $match_condition = $self->match_condition;
155            
156 64 50       445 if( !defined $data )
    100          
157             {
158 0         0 return;
159             }
160             elsif(my ($item) = ($data=~m/$match_condition/))
161             {
162 33         94 return $item;
163             }
164            
165 31         77 return;
166             }
167              
168              
169             =head1 AUTHOR
170              
171             Please see L<DBIx::Class::Schema::PopulateMore> For authorship information
172              
173             visit method culled from code in L<Data::Visitor::Lite> which is copyright 2011 Daichi Hiroki <hirokidaichi {at} gmail.com>
174              
175             =head1 LICENSE
176              
177             Please see L<DBIx::Class::Schema::PopulateMore> For licensing terms.
178              
179             =cut
180              
181              
182             1;