File Coverage

blib/lib/DBIx/Class/Schema/PopulateMore/Command.pm
Criterion Covered Total %
statement 64 67 95.5
branch 4 6 66.6
condition 1 3 33.3
subroutine 19 19 100.0
pod 6 6 100.0
total 94 101 93.0


line stmt bran cond sub pod time code
1             package DBIx::Class::Schema::PopulateMore::Command;
2              
3 3     3   1764 use Moo;
  3         20396  
  3         13  
4 3     3   5494 use MooX::HandlesVia;
  3         1932  
  3         18  
5 3     3   1822 use List::MoreUtils qw(pairwise);
  3         27420  
  3         47  
6 3     3   2663 use DBIx::Class::Schema::PopulateMore::Visitor;
  3         13  
  3         52  
7 3     3   6007 use Module::Pluggable::Object;
  3         21197  
  3         153  
8 3     3   23 use Type::Library -base;
  3         5  
  3         33  
9 3     3   749 use Types::Standard -types;
  3         4  
  3         26  
10 3     3   10744 use namespace::clean;
  3         5  
  3         28  
11              
12             =head1 NAME
13              
14             DBIx::Class::Schema::PopulateMore::Command - Command Class to Populate a Schema
15              
16             =head1 DESCRIPTION
17              
18             This is a command pattern class to manage the job of populating a
19             L<DBIx::Class::Schema> with information. We break this out because the
20             actual job is a bit complex, is likely to grow more complex, and so that
21             we can more easily identify refactorable and reusable parts.
22              
23             =head1 ATTRIBUTES
24              
25             This class defines the following attributes.
26              
27             =head2 schema
28              
29             This is the Schema we are populating
30              
31             =cut
32              
33             has schema => (
34             is=>'ro',
35             required=>1,
36             isa=>Object,
37             );
38              
39             =head2 exception_cb
40              
41             contains a callback to the exception method supplied by DBIC
42              
43             =cut
44              
45             has exception_cb => (
46             is=>'ro',
47             required=>1,
48             isa=>CodeRef,
49             );
50              
51             =head2 definitions
52              
53             This is an arrayref of information used to populate tables in the database
54              
55             =cut
56              
57             has definitions => (
58             is=>'ro',
59             required=>1,
60             isa=>ArrayRef[HashRef],
61             );
62              
63              
64             =head2 match_condition
65              
66             How we know the value is really something to inflate or perform a substitution
67             on. This get's the namespace of the substitution plugin and it's other data.
68              
69             =cut
70              
71             has match_condition => (
72             is=>'ro',
73             required=>1,
74             isa=>RegexpRef,
75             default=>sub {qr/^!(\w+:.+)$/ },
76             );
77              
78              
79             =head2 visitor
80              
81             We define a visitor so that we can perform the value inflations and or
82             substitutions. This is still a little work in progress, but it's getting
83             neater
84              
85             =cut
86              
87             has visitor => (
88             is=>'lazy',
89             isa=>InstanceOf['DBIx::Class::Schema::PopulateMore::Visitor'],
90             handles => [
91             'callback',
92             'visit',
93             ],
94             );
95              
96              
97             =head2 rs_index
98              
99             The index of previously inflated resultsets. Basically when we create a new
100             row in the table, we cache the result object so that it can be used as a
101             dependency in creating another.
102              
103             Eventually will be moved into the constructor for a plugin
104              
105             =head2 set_rs_index
106              
107             Set an index value to an inflated result
108              
109             =head2 get_rs_index
110              
111             given an index, returns the related inflated resultset
112              
113             =cut
114              
115             has rs_index => (
116             is=>'rw',
117             handles_via=>'Hash',
118             isa=>HashRef[Object],
119             default=>sub { +{} },
120             handles=> {
121             set_rs_index => 'set',
122             get_rs_index => 'get',
123             },
124             );
125              
126              
127             =head2 inflator_loader
128              
129             Loads each of the available inflators, provider access to the objects
130              
131             =cut
132              
133             has inflator_loader => (
134             is=>'lazy',
135             isa=>InstanceOf['Module::Pluggable::Object'],
136             handles=>{
137             'inflators' => 'plugins',
138             },
139             );
140              
141              
142             =head2 inflator_dispatcher
143              
144             Holds an object that can perform dispatching to the inflators.
145              
146             =cut
147              
148             has inflator_dispatcher => (
149             is=>'lazy',
150             handles_via=>'Hash',
151             isa=>HashRef[Object],
152             handles=>{
153             inflator_list => 'keys',
154             get_inflator => 'get',
155             },
156             );
157              
158              
159             =head1 METHODS
160              
161             This module defines the following methods.
162              
163             =head2 _build_visitor
164              
165             lazy build for the L</visitor> attribute.
166              
167             =cut
168              
169             sub _build_visitor
170             {
171 5     5   2087 my $self = shift @_;
172            
173 5         121 DBIx::Class::Schema::PopulateMore::Visitor->new({
174             match_condition=>$self->match_condition
175             });
176             }
177              
178              
179             =head2 _build_inflator_loader
180              
181             lazy build for the L</inflator_loader> attribute
182              
183             =cut
184              
185             sub _build_inflator_loader
186             {
187 5     5   1920 my $self = shift @_;
188            
189 5         56 return Module::Pluggable::Object->new(
190             search_path => 'DBIx::Class::Schema::PopulateMore::Inflator',
191             require => 1,
192             except => 'DBIx::Class::Schema::PopulateMore::Inflator',
193             );
194             }
195              
196              
197             =head2 _build_inflator_dispatcher
198              
199             lazy build for the L</inflator_dispatcher> attribute
200              
201             =cut
202              
203             sub _build_inflator_dispatcher
204             {
205 5     5   2180 my $self = shift @_;
206            
207 5         10 my %inflators;
208 5         90 for my $inflator ($self->inflators)
209             {
210 20         24207 my $inflator_obj = $inflator->new;
211 20         340 my $name = $inflator_obj->name;
212            
213 20         52 $inflators{$name} = $inflator_obj;
214            
215             }
216            
217 5         181 return \%inflators;
218             }
219              
220              
221             =head2 execute
222              
223             The command classes main method. Returns a Hash of the created result
224             rows, where each key is the named index and the value is the row object.
225              
226             =cut
227              
228             sub execute
229             {
230 5     5 1 10 my ($self) = @_;
231              
232 5         7 foreach my $definition (@{$self->definitions})
  5         28  
233             {
234 16         55 my ($source => $info) = %$definition;
235 16         67 my @fields = $self->coerce_to_array($info->{fields});
236            
237             my $data = $self
238             ->callback(sub {
239 33     33   249 $self->dispatch_inflator(shift);
240             })
241 16         504 ->visit($info->{data});
242            
243 16         36 while( my ($rs_key, $values) = each %{$data} )
  40         238  
244             {
245 24         84 my @values = $self->coerce_to_array($values);
246            
247 24         134 my $new = $self->create_fixture(
248             $rs_key => $source,
249             $self->merge_fields_values([@fields], [@values])
250             );
251             }
252             }
253            
254 5         12 return %{$self->rs_index};
  5         84  
255             }
256              
257              
258             =head2 dispatch_inflator
259              
260             Dispatch to the correct inflator
261              
262             =cut
263              
264             sub dispatch_inflator
265             {
266 33     33 1 58 my ($self, $arg) = @_;
267 33         149 my ($name, $command) = ($arg =~m/^(\w+):(\w.+)$/);
268            
269 33 50       508 if( my $inflator = $self->get_inflator($name) )
270             {
271 33         3431 $inflator->inflate($self, $command);
272             }
273             else
274             {
275 0         0 my $available = join(', ', $self->inflator_list);
276 0         0 $self->exception_cb->("Can't Handle $name, available are: $available");
277             }
278             }
279              
280              
281             =head2 create_fixture({})
282              
283             Given a hash suitable for a L<DBIx::Class::Resultset> create method, attempt to
284             update or create a row in the named source.
285              
286             returns the newly created row or throws an exception if there is a failure
287              
288             =cut
289              
290             sub create_fixture
291             {
292 24     24 1 70 my ($self, $rs_key => $source, @create) = @_;
293            
294 24         183 my $new = $self
295             ->schema
296             ->resultset($source)
297             ->update_or_create({@create});
298            
299 24         240380 $self->set_rs_index("$source.$rs_key" => $new);
300            
301 24         5206 return $new;
302             }
303              
304              
305             =head2 merge_fields_values
306              
307             Given a fields and values, combine to a hash suitable for using in a create_fixture
308             row statement.
309              
310             =cut
311              
312             sub merge_fields_values
313             {
314 24     24 1 48 my ($self, $fields, $values) = @_;
315            
316             return pairwise {
317 58     58   138 $self->field_value($a,$b)
318 24         268 } (@$fields, @$values);
319             }
320              
321              
322             =head2 field_value
323              
324             Correctly create an array from the fields, values variables, skipping those
325             where the value is undefined.
326              
327             =cut
328              
329             sub field_value
330             {
331 58     58 1 80 my ($self, $a, $b) = @_;
332            
333 58 50 33     234 if(defined $a && defined $b)
334             {
335 58         201 return $a => $b;
336             }
337             else
338             {
339 0         0 return;
340             }
341             }
342              
343              
344             =head2 coerce_to_array
345              
346             given a value that is either an arrayref or a scalar, put it into array context
347             and return that array.
348              
349             =cut
350              
351             sub coerce_to_array
352             {
353 40     40 1 81 my ($self, $value) = @_;
354            
355 40 100       188 return ref $value eq 'ARRAY' ? @$value:($value);
356             }
357              
358              
359             =head1 AUTHOR
360              
361             Please see L<DBIx::Class::Schema::PopulateMore> For authorship information
362              
363             =head1 LICENSE
364              
365             Please see L<DBIx::Class::Schema::PopulateMore> For licensing terms.
366              
367             =cut
368              
369              
370             1;