File Coverage

blib/lib/DBIx/Class/ResultSet/RecursiveUpdate.pm
Criterion Covered Total %
statement 370 395 93.6
branch 149 184 80.9
condition 90 130 69.2
subroutine 30 32 93.7
pod 1 1 100.0
total 640 742 86.2


line stmt bran cond sub pod time code
1 10     10   1695540 use strict;
  10         78  
  10         406  
2 10     10   88 use warnings;
  10         40  
  10         604  
3              
4             package DBIx::Class::ResultSet::RecursiveUpdate;
5             $DBIx::Class::ResultSet::RecursiveUpdate::VERSION = '0.41';
6             # ABSTRACT: like update_or_create - but recursive
7              
8 10     10   68 use base qw(DBIx::Class::ResultSet);
  10         29  
  10         7458  
9              
10             sub recursive_update {
11 62     62 1 7545950 my ( $self, $updates, $attrs ) = @_;
12              
13 62         354 my $fixed_fields;
14             my $unknown_params_ok;
15 62         0 my $m2m_force_set_rel;
16              
17             # 0.21+ api
18 62 100 100     493 if ( defined $attrs && ref $attrs eq 'HASH' ) {
    100 66        
19 5         19 $fixed_fields = $attrs->{fixed_fields};
20 5         11 $unknown_params_ok = $attrs->{unknown_params_ok};
21 5         13 $m2m_force_set_rel = $attrs->{m2m_force_set_rel};
22             }
23              
24             # pre 0.21 api
25             elsif ( defined $attrs && ref $attrs eq 'ARRAY' ) {
26 1         3 $fixed_fields = $attrs;
27             }
28              
29 62         276 return DBIx::Class::ResultSet::RecursiveUpdate::Functions::recursive_update(
30             resultset => $self,
31             updates => $updates,
32             fixed_fields => $fixed_fields,
33             unknown_params_ok => $unknown_params_ok,
34             m2m_force_set_rel => $m2m_force_set_rel,
35             );
36             }
37              
38             package DBIx::Class::ResultSet::RecursiveUpdate::Functions;
39             $DBIx::Class::ResultSet::RecursiveUpdate::Functions::VERSION = '0.41';
40 10     10   448941 use Carp::Clan qw/^DBIx::Class|^HTML::FormHandler|^Try::Tiny/;
  10         19698  
  10         93  
41 10     10   1323 use Scalar::Util qw( blessed );
  10         41  
  10         658  
42 10     10   5956 use List::MoreUtils qw/ any all none /;
  10         122284  
  10         83  
43 10     10   12476 use Try::Tiny;
  10         34  
  10         563  
44 10     10   4660 use Data::Dumper::Concise;
  10         72959  
  10         845  
45              
46 10     10   114 use constant DEBUG => 0;
  10         35  
  10         35201  
47              
48             sub recursive_update {
49 215     215   1623958 my %params = @_;
50             my ( $self, $updates, $fixed_fields, $object, $resolved, $if_not_submitted,
51             $unknown_params_ok, $m2m_force_set_rel )
52             = @params{
53 215         1104 qw/resultset updates fixed_fields object resolved if_not_submitted unknown_params_ok m2m_force_set_rel/
54             };
55 215   100     1059 $resolved ||= {};
56 215         1594 $ENV{DBIC_NULLABLE_KEY_NOWARN} = 1;
57              
58 215         767 my $source = $self->result_source;
59              
60 215 50       707 croak "first parameter needs to be defined"
61             unless defined $updates;
62              
63 215 50       749 croak "first parameter needs to be a hashref"
64             unless ref($updates) eq 'HASH';
65              
66 215 50 66     789 croak 'fixed fields needs to be an arrayref'
67             if defined $fixed_fields && ref $fixed_fields ne 'ARRAY';
68              
69 215         390 DEBUG and warn "recursive_update: " . $source->name . "\n";
70 215 100       610 DEBUG and warn "object passed, skipping find" .
71             (defined $object->id
72             ? " (id " . $object->id . ")\n"
73             : "\n")
74             if defined $object;
75              
76             # always warn about additional parameters if storage debugging is enabled
77 215 100       882 $unknown_params_ok = 0
78             if $source->storage->debug;
79              
80 215 50 33     11730 if ( blessed($updates) && $updates->isa('DBIx::Class::Row') ) {
81 0         0 return $updates;
82             }
83              
84 215         787 my @pks = $source->primary_columns;
85 215         1844 my %pk_kvs;
86 215         576 for my $colname (@pks) {
87 276 100 66     1188 if (exists $updates->{$colname} && defined $updates->{$colname}) {
88 138         466 $pk_kvs{$colname} = $updates->{$colname};
89 138         362 next;
90             }
91             $pk_kvs{$colname} = $resolved->{$colname}
92 138 100 66     736 if exists $resolved->{$colname} && defined $resolved->{$colname};
93             }
94             # support the special case where a method on the related row
95             # populates one or more primary key columns and we don't have
96             # all primary key values already
97             # see DBSchema::Result::DVD relationship keysbymethod
98 215         429 DEBUG and warn "pk columns so far: " . join (', ',
99             sort keys %pk_kvs) . "\n";
100             my @non_pk_columns = grep {
101 215         1081 my $colname = $_;
  405         869  
102 405     266   2542 none { $colname eq $_ } keys %pk_kvs
  266         1337  
103             }
104             sort keys %$updates;
105 215         491 DEBUG and warn "non-pk columns: " . join (', ',
106             @non_pk_columns) . "\n";
107 215 100 66     1035 if ( scalar keys %pk_kvs != scalar @pks && @non_pk_columns) {
108 78         208 DEBUG and warn "not all primary keys available, trying " .
109             "object creation\n";
110             # new_result throws exception if non column values are passed
111             # because we want to also support e.g. a BUILDARGS method that
112             # populates primary key columns from an additional value
113             # filter out all relationships
114             my @non_rel_columns = grep {
115 78   100     267 !is_m2m( $self, $_ )
  183         2890  
116             && !$source->has_relationship($_)
117             }
118             sort keys %$updates;
119             my %non_rel_updates = map {
120 78         2011 $_ => $updates->{$_}
  119         425  
121             } @non_rel_columns;
122             # transform columns specified by their accessor name
123 78         265 my %columns_by_accessor = _get_columns_by_accessor($self);
124 78         455 for my $accessor_name (sort keys %columns_by_accessor) {
125 353         679 my $colname = $columns_by_accessor{$accessor_name}->{name};
126 353 100 100     910 if ($accessor_name ne $colname
127             && exists $non_rel_updates{$accessor_name}) {
128 12         20 DEBUG and warn "renaming column accessor " .
129             "'$accessor_name' to column name '$colname'\n";
130             $non_rel_updates{$colname} = delete
131 12         40 $non_rel_updates{$accessor_name};
132             }
133             }
134 78         157 DEBUG and warn "using all non-rel updates for object " .
135             "construction: " . Dumper(\%non_rel_updates);
136             # the object creation might fail because of non-column and
137             # non-constructor handled parameters which shouldn't break RU
138             try {
139 78     78   5925 my $row = $self->new_result(\%non_rel_updates);
140 75         10157 for my $colname (@pks) {
141             next
142 81 100       347 if exists $pk_kvs{$colname};
143              
144 77 100 66     2444 if ($row->can($colname)
145             && defined $row->$colname) {
146 6         125 DEBUG and warn "missing pk column $colname exists " .
147             "and defined on object\n";
148 6         131 $pk_kvs{$colname} = $row->$colname;
149             }
150             else {
151 71         2346 DEBUG and warn "missing pk column $colname doesn't "
152             . "exist or isn't defined on object, aborting\n";
153 71         344 last;
154             }
155             }
156             }
157             catch {
158 3     3   5714 DEBUG and warn "object construction failed, ignoring:
159             $_\n";
160 78         837 };
161             }
162              
163             # check if row can be found in resultset cache
164 215 100 100     3854 if ( !defined $object && scalar keys %pk_kvs == scalar @pks ) {
165 80         362 my $cached_rows = $self->get_cache;
166 80 100       491 if (defined $cached_rows) {
167 4         8 DEBUG and warn "find in cache\n";
168 4         24 $object = _get_matching_row(\%pk_kvs, $cached_rows)
169             }
170             }
171              
172 215         944 $updates = { %$updates, %$resolved };
173              
174 215         750 my %fixed_fields = map { $_ => 1 } @$fixed_fields;
  2         8  
175              
176             # add the resolved columns to the updates hashref
177 215         729 my %all_pks = ( %pk_kvs, %fixed_fields );
178              
179 215 100 100     953 if ( !defined $object && scalar keys %all_pks == scalar @pks) {
180 76         162 DEBUG and warn "find by pk\n";
181 76         471 $object = $self->find( \%all_pks, { key => 'primary' } );
182             }
183              
184 215 100       246301 unless (defined $object) {
185 62         116 DEBUG and warn "create new row\n";
186 62         267 $object = $self->new_result( {} );
187             }
188              
189             # direct column accessors
190 215         6588 my %columns;
191              
192             # relations that that should be done before the row is inserted into the
193             # database like belongs_to
194             my %pre_updates;
195              
196             # relations that that should be done after the row is inserted into the
197             # database like has_many, might_have and has_one
198 215         0 my %post_updates;
199 215         0 my %other_methods;
200 215         0 my %m2m_accessors;
201 215         652 my %columns_by_accessor = _get_columns_by_accessor($self);
202              
203             # this section determines to what each key/value pair maps to,
204             # column or relationship
205 215         1162 for my $name ( sort keys %$updates ) {
206             DEBUG and warn "updating $name to "
207 496         1451 . ($updates->{$name} // '[undef]') . "\n";
208             # columns
209 496 100 100     1897 if ( exists $columns_by_accessor{$name} &&
      100        
210             !( $source->has_relationship($name) && ref( $updates->{$name} ) ) ) {
211 355         2895 $columns{$name} = $updates->{$name};
212 355         844 next;
213             }
214              
215             # relationships
216 141 100       781 if ( $source->has_relationship($name) ) {
217 112 100       699 if ( _master_relation_cond( $self, $name ) ) {
218 63         188 $pre_updates{$name} = $updates->{$name};
219 63         194 next;
220             }
221             else {
222 49         147 $post_updates{$name} = $updates->{$name};
223 49         140 next;
224             }
225             }
226              
227             # many-to-many helper accessors
228 29 100       292 if ( is_m2m( $self, $name ) ) {
229 24         648 DEBUG and warn "is m2m\n";
230             # Transform m2m data into recursive has_many data
231             # if IntrospectableM2M is in use.
232             #
233             # This removes the overhead related to deleting and
234             # re-adding all relationships.
235 24 100 100     594 if ( !$m2m_force_set_rel && $source->result_class->can('_m2m_metadata') ) {
236 17         872 my $meta = $source->result_class->_m2m_metadata->{$name};
237 17         1022 my $bridge_rel = $meta->{relation};
238 17         58 my $foreign_rel = $meta->{foreign_relation};
239              
240             $post_updates{$bridge_rel} = [
241             map {
242 39         168 { $foreign_rel => $_ }
243 17         47 } @{ $updates->{$name} }
  17         63  
244             ];
245             DEBUG and warn "m2m '$name' transformed to:\n$bridge_rel => " .
246 17         46 Dumper($post_updates{$bridge_rel}) . "\n";
247             }
248             # Fall back to set_$rel if IntrospectableM2M
249             # is not available. (removing and re-adding all relationships)
250             else {
251 7         161 $m2m_accessors{$name} = $updates->{$name};
252             }
253              
254 24         79 next;
255             }
256              
257             # accessors
258 5 100 66     112 if ( $object->can($name) && not $source->has_relationship($name) ) {
259 2         21 $other_methods{$name} = $updates->{$name};
260 2         7 next;
261             }
262              
263             # unknown
264              
265             # don't throw a warning instead of an exception to give users
266             # time to adapt to the new API
267             carp(
268 3 100       24 "No such column, relationship, many-to-many helper accessor or " .
269             "generic accessor '$name' on '" . $source->name . "'"
270             ) unless $unknown_params_ok;
271              
272             }
273              
274             # first update columns and other accessors
275             # so that later related records can be found
276 215         832 for my $name ( sort keys %columns ) {
277 343         26247 $object->$name( $columns{$name} );
278             }
279 209         23395 for my $name ( sort keys %other_methods ) {
280 2         8 $object->$name( $other_methods{$name} );
281             }
282 209         998 for my $name ( sort keys %pre_updates ) {
283 61         477 _update_relation( $self, $name, $pre_updates{$name}, $object, $if_not_submitted, 0 );
284             }
285              
286             # $self->_delete_empty_auto_increment($object);
287             # don't allow insert to recurse to related objects
288             # do the recursion ourselves
289             # $object->{_rel_in_storage} = 1;
290             # Update if %other_methods because of possible custom update method
291 205         1833 my $in_storage = $object->in_storage;
292              
293             # preserve related resultsets as DBIx::Class::Row->update clears them
294             # yes, this directly accesses a row attribute, but no API exists and in
295             # the hope to get the recursive_update feature into core DBIx::Class this
296             # is the easiest solution
297 205         541 my $related_resultsets = $object->{related_resultsets};
298              
299 205         351 DEBUG and warn "before update_or_insert\n";
300 205 100 66     1359 $object->update_or_insert if ( $object->is_changed || keys %other_methods );
301 205         1471348 DEBUG and warn "after update_or_insert\n";
302              
303             # restore related resultsets
304 205         669 $object->{related_resultsets} = $related_resultsets;
305              
306             # updating many_to_many
307 205         800 for my $name ( sort keys %m2m_accessors ) {
308 7         18 DEBUG and warn "updating m2m $name\n";
309 7         26 my $value = $m2m_accessors{$name};
310              
311             # TODO: only first pk col is used
312 7         33 my ($pk) = _get_pk_for_related( $self, $name );
313 7         57 my @rows;
314 7         32 my $rel_source = $object->$name->result_source;
315 7         22408 my @updates;
316 7 50 33     172 if ( defined $value && ref $value eq 'ARRAY' ) {
    0 0        
    0          
317 7         17 @updates = @{$value};
  7         25  
318             }
319             elsif ( defined $value && !ref $value ) {
320 0         0 @updates = ($value);
321             }
322             elsif ( defined $value ) {
323 0         0 carp "value of many-to-many rel '$name' must be an arrayref or scalar: $value";
324             }
325 7         20 for my $elem (@updates) {
326 16 50 33     19787 if ( blessed($elem) && $elem->isa('DBIx::Class::Row') ) {
    100          
327 0         0 push @rows, $elem;
328             }
329             elsif ( ref $elem eq 'HASH' ) {
330 5         25 push @rows,
331             recursive_update(
332             resultset => $rel_source->resultset,
333             updates => $elem
334             );
335             }
336             else {
337 11         44 push @rows, $rel_source->resultset->find( { $pk => $elem } );
338             }
339             }
340 7         15764 my $set_meth = 'set_' . $name;
341 7         61 $object->$set_meth( \@rows );
342             }
343 205         461802 for my $name ( sort keys %post_updates ) {
344 57         435 _update_relation( $self, $name, $post_updates{$name}, $object, $if_not_submitted, $in_storage );
345             }
346 200         2974 delete $ENV{DBIC_NULLABLE_KEY_NOWARN};
347 200         2342 return $object;
348             }
349              
350             # returns DBIx::Class::ResultSource::column_info as a hash indexed by column accessor || name
351             sub _get_columns_by_accessor {
352 316     316   770 my $self = shift;
353 316         860 my $source = $self->result_source;
354 316         648 my %columns;
355 316         1139 for my $name ( $source->columns ) {
356 1295         5880 my $info = $source->column_info($name);
357 1295         12634 $info->{name} = $name;
358 1295   66     4948 $columns{ $info->{accessor} || $name } = $info;
359             }
360 316         1826 return %columns;
361             }
362              
363             sub _get_matching_row {
364 67     67   212 my ($kvs, $rows) = @_;
365              
366             return
367 67 50       217 unless defined $rows;
368              
369 67 50       283 croak 'key/value need to be a hashref'
370             unless ref $kvs eq 'HASH';
371              
372 67 50       221 croak 'key/value needs to have at least one pair'
373             if keys %$kvs == 0;
374              
375 67 50       252 croak 'rows need to be an arrayref'
376             unless ref $rows eq 'ARRAY';
377              
378 67 50       230 unless ($rows) {
379 0         0 DEBUG and warn "skipping because no rows passed\n";
380 0         0 return;
381             }
382              
383 67         168 my $matching_row;
384              
385             my @matching_rows;
386 67         193 for my $row (@$rows) {
387             push @matching_rows, $row
388 234     234   1597 if all { $kvs->{$_} eq $row->get_column($_) }
389 134 100       2030 grep { !ref $kvs->{$_} }
  237         840  
390             sort keys %$kvs;
391             }
392 67 50       778 DEBUG and warn "multiple matching rows: " . scalar @matching_rows . "\n"
393             if @matching_rows > 1;
394 67 100       211 $matching_row = $matching_rows[0]
395             if scalar @matching_rows == 1;
396             DEBUG and warn "matching row found for: " . Dumper($kvs) . " in " .
397 67 100       193 Dumper([map { { $_->get_columns } } @$rows]) . "\n"
398             if defined $matching_row;
399             DEBUG and warn "matching row not found for: " . Dumper($kvs) . " in " .
400 67 100       188 Dumper([map { { $_->get_columns } } @$rows]) . "\n"
401             unless defined $matching_row;
402              
403 67         513 return $matching_row;
404             }
405              
406             # Arguments: $rs, $name, $updates, $row, $if_not_submitted, $row_existed
407             sub _update_relation {
408 118     118   408 my ( $self, $name, $updates, $object, $if_not_submitted, $row_existed ) = @_;
409              
410             # this should never happen because we're checking the paramters passed to
411             # recursive_update, but just to be sure...
412 118 50       3585 $object->throw_exception("No such relationship '$name'")
413             unless $object->has_relationship($name);
414              
415 118         14489 DEBUG and warn "_update_relation: $name\n";
416              
417 118         519 my $info = $object->result_source->relationship_info($name);
418 118         1265 my $attrs = $info->{attrs};
419              
420             # get a related resultset without a condition
421 118         607 my $related_source = $self->related_resultset($name)->result_source;
422 118         136383 my $related_resultset = $related_source->resultset;
423 118 50       39973 $self->throw_exception("result_source must support _resolve_condition")
424             unless $self->result_source->can('_resolve_condition');
425 118         608 my $resolved = $self->result_source->_resolve_condition( $info->{cond}, $name, $object, $name );
426              
427 118 100 66     37997 $resolved = {}
428             if defined $DBIx::Class::ResultSource::UNRESOLVABLE_CONDITION &&
429             $DBIx::Class::ResultSource::UNRESOLVABLE_CONDITION == $resolved;
430              
431             # This is a hack. I'm not sure that this will handle most
432             # custom code conditions yet. This needs tests.
433 118         315 my @rel_cols;
434 118 50       507 if ( ref $info->{cond} eq 'CODE' ) {
435 0         0 my $new_resolved;
436             # remove 'me.' from keys in returned hashref
437 0         0 while ( my ( $key, $value ) = each %$resolved ) {
438 0         0 $key =~ s/^me\.//;
439 0         0 $new_resolved->{$key} = $value;
440 0         0 push @rel_cols, $key;
441             }
442 0         0 $resolved = $new_resolved;
443             }
444             else {
445 118         293 @rel_cols = sort keys %{ $info->{cond} };
  118         510  
446 118         378 map { s/^foreign\.// } @rel_cols;
  118         655  
447             }
448              
449             # find out if all related columns are nullable
450 118         287 my $all_fks_nullable = 1;
451 118         316 for my $rel_col (@rel_cols) {
452             $all_fks_nullable = 0
453 118 100       645 unless $related_resultset->result_source->column_info($rel_col)->{is_nullable};
454             }
455              
456 118 100       2005 $if_not_submitted = $all_fks_nullable ? 'set_to_null' : 'delete'
    100          
457             unless defined $if_not_submitted;
458              
459             # the only valid datatype for a has_many rels is an arrayref
460 118 100 66     731 if ( $attrs->{accessor} eq 'multi' ) {
    50          
461 49         109 DEBUG and warn "has_many: $name\n";
462              
463             # handle undef like empty arrayref
464 49 100       187 $updates = []
465             unless defined $updates;
466 49 50       188 $self->throw_exception("data for has_many relationship '$name' must be an arrayref")
467             unless ref $updates eq 'ARRAY';
468              
469 49         221 my @updated_objs;
470             my @related_rows;
471             # newly created rows can't have related rows
472 49 100       179 if ($row_existed) {
473 28         680 @related_rows = $object->$name;
474 28         100028 DEBUG and warn "got related rows: " . scalar @related_rows . "\n";
475             }
476 49         197 my $related_result_source = $related_resultset->result_source;
477 49         202 my @pks = $related_result_source->primary_columns;
478              
479 49         500 for my $sub_updates ( @{$updates} ) {
  49         153  
480 85         157 DEBUG and warn "updating related row: " . Dumper($sub_updates)
481             . "\n";
482 85         242 my %pk_kvs;
483             # detect the special case where the primary key of a currently not
484             # related row is passed in the updates hash
485             # let the resolved column values fill any missing primary key
486             # columns but not overwrite them
487 85         229 for my $colname (@pks) {
488 142 100 66     675 if (exists $sub_updates->{$colname}
489             && defined $sub_updates->{$colname}) {
490             # $sub_updates->{$colname} might be a hashref if a
491             # relationship is named the same as a foreign key column
492 66 100       306 if (ref $sub_updates->{$colname} eq 'HASH') {
493 29 50       140 if ($related_source->has_relationship($colname)) {
494 29         274 my $rel_info = $related_source
495             ->relationship_info($colname);
496 29         153 my @rel_cols = sort keys %{ $rel_info->{cond} };
  29         160  
497 29         107 map { s/^foreign\.// } @rel_cols;
  29         217  
498 29 50       124 $self->throw_exception("passing a hashref for " .
499             "a multi-column relationship named the " .
500             "same as a column ('$colname') is not " .
501             "implemented")
502             if scalar @rel_cols != 1;
503 29         74 DEBUG and warn "using '$rel_cols[0]' in hashref " .
504             "for primary key column '$colname'\n";
505             $pk_kvs{$colname} = $sub_updates->{$colname}
506 29         132 ->{$rel_cols[0]};
507             }
508             else {
509 0         0 $self->throw_exception(
510             "data for $colname is a hashref but no " .
511             "relationship with that name exists");
512             }
513             }
514             else {
515 37         139 $pk_kvs{$colname} = $sub_updates->{$colname};
516             }
517 66         200 next;
518             }
519             $pk_kvs{$colname} = $resolved->{$colname}
520             if exists $resolved->{$colname}
521 76 100 66     498 && defined $resolved->{$colname};
522             }
523 85         178 my $related_object;
524              
525             # support the special case where a method on the related row
526             # populates one or more primary key columns and we don't have
527             # all primary key values already
528             # see DBSchema::Result::DVD relationship keysbymethod
529 85         163 DEBUG and warn "pk columns so far: " . join (', ',
530             sort keys %pk_kvs) . "\n";
531             my @non_pk_columns = grep {
532 85         422 my $colname = $_;
  112         254  
533 112     100   866 none { $colname eq $_ } keys %pk_kvs
  100         561  
534             }
535             sort keys %$sub_updates;
536 85         202 DEBUG and warn "non-pk columns: " . join (', ',
537             @non_pk_columns) . "\n";
538 85 100 66     452 if ( scalar keys %pk_kvs != scalar @pks && @non_pk_columns) {
539 23         47 DEBUG and warn "not all primary keys available, trying " .
540             "object creation\n";
541             # new_result throws exception if non column values are passed
542             # because we want to also support e.g. a BUILDARGS method that
543             # populates primary key columns from an additional value
544             # filter out all relationships
545             my @non_rel_columns = grep {
546 23   100     77 !is_m2m( $related_resultset, $_ )
  43         633  
547             && !$related_result_source->has_relationship($_)
548             }
549             sort keys %$sub_updates;
550             my %non_rel_updates = map {
551 23         602 $_ => $sub_updates->{$_}
  35         132  
552             } @non_rel_columns;
553             # transform columns specified by their accessor name
554 23         83 my %columns_by_accessor = _get_columns_by_accessor($related_resultset);
555 23         162 for my $accessor_name (sort keys %columns_by_accessor) {
556 111         253 my $colname = $columns_by_accessor{$accessor_name}->{name};
557 111 100 100     336 if ($accessor_name ne $colname
558             && exists $non_rel_updates{$accessor_name}) {
559 10         18 DEBUG and warn "renaming column accessor " .
560             "'$accessor_name' to column name '$colname'\n";
561             $non_rel_updates{$colname} = delete
562 10         29 $non_rel_updates{$accessor_name};
563             }
564             }
565 23         51 DEBUG and warn "using all non-rel updates for object " .
566             "construction: " . Dumper(\%non_rel_updates);
567             # the object creation might fail because of non-column and
568             # non-constructor handled parameters which shouldn't break RU
569             try {
570 23     23   1881 my $related_row = $related_resultset
571             ->new_result(\%non_rel_updates);
572 23         3170 for my $colname (@pks) {
573             next
574 27 100       118 if exists $pk_kvs{$colname};
575              
576 24 100 66     848 if ($related_row->can($colname)
577             && defined $related_row->$colname) {
578 2         34 DEBUG and warn "missing pk column $colname exists " .
579             "and defined on object\n";
580 2         39 $pk_kvs{$colname} = $related_row->$colname;
581             }
582             else {
583 22         784 DEBUG and warn "missing pk column $colname doesn't "
584             . "exist or isn't defined on object, aborting\n";
585 22         132 last;
586             }
587             }
588             }
589             catch {
590 0     0   0 DEBUG and warn "object construction failed, ignoring:
591             $_\n";
592 23         243 };
593             }
594              
595             # The only reasons to let recursive_update search for an existing
596             # row (= not passing a new result to it) is relinking of existing
597             # rows.
598             # Relinking is only possible if all primary key column values are
599             # known and only required if at least one of the foreign row
600             # columns, which are part of the relationship, differ between
601             # current and target ones.
602             # There are two different cases:
603             # The foreign row columns are part of the foreign primary key.
604             # An example is the dvdtags relationship of Dvd.
605             # Or one or more non primary key form the relationship.
606             # An example is the owned_dvds relationship of User.
607 85         1150 my $relink = 0;
608              
609 85 100       392 if ( scalar keys %pk_kvs == scalar @pks ) {
610 63         173 DEBUG and warn "all primary keys available, " .
611             "searching for row in currently related rows\n";
612             # the lookup can fail if the primary key of a currently not
613             # related row is passed in the updates hash
614 63         310 $related_object = _get_matching_row(\%pk_kvs, \@related_rows);
615             # %pk_kvs contains the scalar value instead of a hashref
616             # when a column and relationship are named the same so
617             # overwrite the hashref in $sub_updates with that
618             # don't include %$resolved as well as that contains target data
619 63         396 my %current_data = (%$sub_updates, %pk_kvs);
620 63         146 DEBUG and warn "current data: " . Dumper(\%current_data);
621 63         131 DEBUG and warn "target data: " . Dumper($resolved);
622              
623 10     10   120 no warnings 'uninitialized';
  10         28  
  10         17745  
624              
625             # If the row can't be found by _get_matching_row it is
626             # currently not linked or doesn't even exist. In this case we
627             # must execute a sql select to find it.
628             $relink = 1
629             if (not defined $related_object)
630 30     30   211 && (any { $resolved->{$_} ne $current_data{$_} }
631 63 100 100     504 keys %$resolved);
632             }
633 85         308 DEBUG and warn "relink: $relink\n";
634              
635             # pass an empty object if no related row found and it's not the
636             # special case where the primary key of a currently not related
637             # row is passed in the updates hash to prevent the find by pk in
638             # recursive_update to happen
639 85 100 100     422 if ((not defined $related_object) && (not $relink)) {
640 50         96 DEBUG and warn "passing empty row to prevent find by pk\n";
641 50         253 $related_object = $related_resultset->new_result({});
642             }
643              
644 85         5241 my $sub_object = recursive_update(
645             resultset => $related_resultset,
646             updates => $sub_updates,
647             resolved => $resolved,
648             # pass prefetched object if found
649             object => $related_object,
650             );
651              
652 80         432 push @updated_objs, $sub_object;
653             }
654              
655             # determine if a removal query is required
656             my @remove_rows = grep {
657 44         178 my $existing_row = $_;
  69         9776  
658 94     94   7750 none { $existing_row->ID eq $_->ID } @updated_objs
659 69         486 } @related_rows;
660 44         4781 DEBUG and warn "rows for removal: " . join(', ', map { $_->ID }
661             @remove_rows) . "\n";
662              
663 44 100       407 if (scalar @remove_rows) {
664 17         515 my $rs_rel_delist = $object->$name;
665              
666             # foreign table has a single pk column
667 17 100       4756 if (scalar @pks == 1) {
668 6         21 DEBUG and warn "delete in not_in\n";
669 6         50 $rs_rel_delist = $rs_rel_delist->search_rs(
670             {
671             $self->current_source_alias . "." .
672             $pks[0] => { -not_in => [ map ( $_->id, @updated_objs ) ] }
673             }
674             );
675             }
676              
677             # foreign table has multiple pk columns
678             else {
679 11         39 my @cond;
680 11         44 for my $obj (@updated_objs) {
681 14         33 my %cond_for_obj;
682 14         39 for my $col (@pks) {
683 28         277 $cond_for_obj{ $self->current_source_alias . ".$col" } =
684             $obj->get_column($col);
685              
686             }
687 14         183 push @cond, \%cond_for_obj;
688             }
689              
690             # only limit resultset if there are related rows left
691 11 100       46 if (scalar @cond) {
692 8         44 $rs_rel_delist = $rs_rel_delist->search_rs({ -not => [ @cond ] });
693             }
694             }
695              
696 17 100       8442 if ($if_not_submitted eq 'delete') {
    50          
697 15         145 $rs_rel_delist->delete;
698             }
699             elsif ($if_not_submitted eq 'set_to_null') {
700 2         6 my %update = map {$_ => undef} @rel_cols;
  2         12  
701 2         8 $rs_rel_delist->update(\%update);
702             }
703             }
704             }
705             elsif ( $attrs->{accessor} eq 'single' ||
706             $attrs->{accessor} eq 'filter' ) {
707             DEBUG and warn "has_one, might_have, belongs_to (" .
708 69         147 $attrs->{accessor} . "): $name\n";
709              
710 69         159 my $sub_object;
711 69 100       199 if ( ref $updates ) {
712 68         117 my $existing_row = 0;
713 68         257 my @pks = $related_resultset->result_source->primary_columns;
714 68 100   68   887 if ( all { exists $updates->{$_} && defined $updates->{$_} } @pks ) {
  68 100       408  
715 24         149 $existing_row = 1;
716             }
717 68         261 DEBUG and warn $existing_row ? "existing row\n" : "new row\n";
718             # newly created rows can't have related rows
719 68         149 my $related_row;
720 68 100       192 if ($row_existed) {
721 2         39 $related_row = $object->$name;
722 2         9612 DEBUG and warn "got related row\n";
723             }
724 68 100 66     645 if ( blessed($updates) && $updates->isa('DBIx::Class::Row') ) {
    100 100        
725 10         42 $sub_object = $updates;
726             }
727             elsif ( $attrs->{accessor} eq 'single' && defined $related_row )
728             {
729 1 50       33 $sub_object = recursive_update(
730             resultset => $related_resultset,
731             updates => $updates,
732             $existing_row ? () : (object => $object->$name),
733             );
734             }
735             else {
736 57 100       346 $sub_object = recursive_update(
737             resultset => $related_resultset,
738             updates => $updates,
739             $existing_row ? () : (resolved => $resolved),
740             );
741             }
742             }
743             else {
744             $sub_object = $related_resultset->find($updates)
745             unless (
746             !$updates &&
747             ( exists $attrs->{join_type} &&
748 1 50 33     11 $attrs->{join_type} eq 'LEFT' )
      33        
749             );
750             }
751 65   100     397 my $join_type = $attrs->{join_type} || '';
752             # unmarked 'LEFT' join for belongs_to
753             my $might_belong_to =
754             ( $attrs->{accessor} eq 'single' || $attrs->{accessor} eq 'filter' ) &&
755 65   66     545 $attrs->{is_foreign_key_constraint};
756             # adding check for custom condition that's a coderef
757             # this 'set_from_related' should probably not be called in lots of other
758             # situations too, but until that's worked out, kludge it
759 65 50 33     937 if ( ( $sub_object || $updates || $might_belong_to || $join_type eq 'LEFT' ) &&
      33        
760             ref $info->{cond} ne 'CODE' ) {
761 65         5846 $object->$name($sub_object);
762             }
763             }
764             else {
765             $self->throw_exception(
766             "recursive_update doesn't now how to handle relationship '$name' with accessor " .
767 0         0 $info->{attrs}{accessor} );
768             }
769              
770 109         321292 DEBUG and warn "_update_relation end\n";
771             }
772              
773             sub is_m2m {
774 335     335   902 my ( $self, $relation ) = @_;
775 335         1126 my $rclass = $self->result_class;
776              
777             # DBIx::Class::IntrospectableM2M
778 335 100       5096 if ( $rclass->can('_m2m_metadata') ) {
779 56         1680 return $rclass->_m2m_metadata->{$relation};
780             }
781 279         915 my $object = $self->new_result( {} );
782 279 100 100     23079 if ( $object->can($relation) and
      100        
783             !$self->result_source->has_relationship($relation) and
784             $object->can( 'set_' . $relation ) ) {
785 10         182 return 1;
786             }
787 269         3790 return;
788             }
789              
790             sub get_m2m_source {
791 7     7   32 my ( $self, $relation ) = @_;
792 7         32 my $rclass = $self->result_class;
793              
794             # DBIx::Class::IntrospectableM2M
795 7 100       82 if ( $rclass->can('_m2m_metadata') ) {
796             return $self->result_source->related_source(
797             $rclass->_m2m_metadata->{$relation}{relation} )
798 2         45 ->related_source( $rclass->_m2m_metadata->{$relation}{foreign_relation} );
799             }
800 5         21 my $object = $self->new_result( {} );
801 5         338 my $r = $object->$relation;
802 5         18413 return $r->result_source;
803             }
804              
805             sub _delete_empty_auto_increment {
806 0     0   0 my ( $self, $object ) = @_;
807 0         0 for my $col ( sort keys %{ $object->{_column_data} } ) {
  0         0  
808 0 0 0     0 if (
      0        
809             $object->result_source->column_info($col)->{is_auto_increment} and
810             ( !defined $object->{_column_data}{$col} or
811             $object->{_column_data}{$col} eq '' )
812             ) {
813 0         0 delete $object->{_column_data}{$col};
814             }
815             }
816             }
817              
818             sub _get_pk_for_related {
819 80     80   204 my ( $self, $relation ) = @_;
820 80         154 my $source;
821 80 100       285 if ( $self->result_source->has_relationship($relation) ) {
822 73         477 $source = $self->result_source->related_source($relation);
823             }
824              
825             # many to many case
826 80 100       12073 if ( is_m2m( $self, $relation ) ) {
827 7         152 $source = get_m2m_source( $self, $relation );
828             }
829 80         2361 return $source->primary_columns;
830             }
831              
832             # This function determines whether a relationship should be done before or
833             # after the row is inserted into the database
834             # relationships before: belongs_to
835             # relationships after: has_many, might_have and has_one
836             # true means before, false after
837             sub _master_relation_cond {
838 112     112   310 my ( $self, $name ) = @_;
839              
840 112         340 my $source = $self->result_source;
841 112         327 my $info = $source->relationship_info($name);
842              
843             # has_many rels are always after
844             return 0
845 112 100       855 if $info->{attrs}->{accessor} eq 'multi';
846              
847 73         246 my @foreign_ids = _get_pk_for_related( $self, $name );
848              
849 73         665 my $cond = $info->{cond};
850              
851             sub _inner {
852 73     73   206 my ( $source, $cond, @foreign_ids ) = @_;
853              
854 73         153 while ( my ( $f_key, $col ) = each %{$cond} ) {
  73         361  
855              
856             # might_have is not master
857 72         390 $col =~ s/^self\.//;
858 72         279 $f_key =~ s/^foreign\.//;
859 72 100       256 if ( $source->column_info($col)->{is_auto_increment} ) {
860 9         131 return 0;
861             }
862 63 50   63   1170 if ( any { $_ eq $f_key } @foreign_ids ) {
  63         246  
863 63         424 return 1;
864             }
865             }
866 1         4 return 0;
867             }
868              
869 73 50       251 if ( ref $cond eq 'HASH' ) {
    0          
    0          
870 73         216 return _inner( $source, $cond, @foreign_ids );
871             }
872              
873             # arrayref of hashrefs
874             elsif ( ref $cond eq 'ARRAY' ) {
875 0         0 for my $new_cond (@$cond) {
876 0         0 return _inner( $source, $new_cond, @foreign_ids );
877             }
878             }
879              
880             # we have a custom join condition, so update afterward
881             elsif ( ref $cond eq 'CODE' ) {
882 0         0 return 0;
883             }
884              
885             else {
886 0         0 $source->throw_exception( "unhandled relation condition " . ref($cond) );
887             }
888 0         0 return;
889             }
890              
891             1;
892              
893             __END__
894              
895             =pod
896              
897             =encoding UTF-8
898              
899             =head1 NAME
900              
901             DBIx::Class::ResultSet::RecursiveUpdate - like update_or_create - but recursive
902              
903             =head1 VERSION
904              
905             version 0.41
906              
907             =head1 SYNOPSIS
908              
909             # The functional interface:
910              
911             my $schema = MyDB::Schema->connect();
912             my $new_item = DBIx::Class::ResultSet::RecursiveUpdate::Functions::recursive_update(
913             resultset => $schema->resultset('User'),
914             updates => {
915             id => 1,
916             owned_dvds => [
917             {
918             title => "One Flew Over the Cuckoo's Nest"
919             }
920             ]
921             },
922             unknown_params_ok => 1,
923             );
924              
925              
926             # As ResultSet subclass:
927              
928             __PACKAGE__->load_namespaces( default_resultset_class => '+DBIx::Class::ResultSet::RecursiveUpdate' );
929              
930             # in the Schema file (see t/lib/DBSchema.pm). Or appropriate 'use base' in the ResultSet classes.
931              
932             my $user = $schema->resultset('User')->recursive_update({
933             id => 1,
934             owned_dvds => [
935             {
936             title => "One Flew Over the Cuckoo's Nest"
937             }
938             ]
939             }, {
940             unknown_params_ok => 1,
941             });
942              
943             # You'll get a warning if you pass non-result specific data to
944             # recursive_update. See L</"Additional data in the updates hashref">
945             # for more information how to prevent this.
946              
947             =head1 DESCRIPTION
948              
949             You can feed the ->create method of DBIx::Class with a recursive datastructure
950             and have the related records created. Unfortunately you cannot do a similar
951             thing with update_or_create. This module tries to fill that void until
952             L<DBIx::Class> has an api itself.
953              
954             The functional interface can be used without modifications of the model,
955             for example by form processors like L<HTML::FormHandler::Model::DBIC>.
956              
957             It is a base class for L<DBIx::Class::ResultSet>s providing the method
958             recursive_update which works just like update_or_create but can recursively
959             update or create result objects composed of multiple rows. All rows need to be
960             identified by primary keys so you need to provide them in the update structure
961             (unless they can be deduced from the parent row. For example a related row of
962             a belongs_to relationship). If any of the primary key columns are missing,
963             a new row will be created, with the expectation that the missing columns will
964             be filled by it (as in the case of auto_increment primary keys).
965              
966             If the resultset itself stores an assignment for the primary key,
967             like in the case of:
968              
969             my $restricted_rs = $user_rs->search( { id => 1 } );
970              
971             you need to inform recursive_update about the additional predicate with the fixed_fields attribute:
972              
973             my $user = $restricted_rs->recursive_update( {
974             owned_dvds => [
975             {
976             title => 'One Flew Over the Cuckoo's Nest'
977             }
978             ]
979             },
980             {
981             fixed_fields => [ 'id' ],
982             }
983             );
984              
985             For a many_to_many (pseudo) relation you can supply a list of primary keys
986             from the other table and it will link the record at hand to those and
987             only those records identified by them. This is convenient for handling web
988             forms with check boxes (or a select field with multiple choice) that lets you
989             update such (pseudo) relations.
990              
991             For a description how to set up base classes for ResultSets see
992             L<DBIx::Class::Schema/load_namespaces>.
993              
994             =head2 Additional data in the updates hashref
995              
996             If you pass additional data to recursive_update which doesn't match a column
997             name, column accessor, relationship or many-to-many helper accessor, it will
998             throw a warning by default. To disable this behaviour you can set the
999             unknown_params_ok attribute to a true value.
1000              
1001             The warning thrown is:
1002             "No such column, relationship, many-to-many helper accessor or generic accessor '$key'"
1003              
1004             When used by L<HTML::FormHandler::Model::DBIC> this can happen if you have
1005             additional form fields that aren't relevant to the database but don't have the
1006             noupdate attribute set to a true value.
1007              
1008             NOTE: in a future version this behaviour will change and throw an exception
1009             instead of a warning!
1010              
1011             =head1 DESIGN CHOICES
1012              
1013             Columns and relationships which are excluded from the updates hashref aren't
1014             touched at all.
1015              
1016             =head2 Treatment of belongs_to relations
1017              
1018             In case the relationship is included but undefined in the updates hashref,
1019             all columns forming the relationship will be set to null.
1020             If not all of them are nullable, DBIx::Class will throw an error.
1021              
1022             Updating the relationship:
1023              
1024             my $dvd = $dvd_rs->recursive_update( {
1025             id => 1,
1026             owner => $user->id,
1027             });
1028              
1029             Clearing the relationship (only works if cols are nullable!):
1030              
1031             my $dvd = $dvd_rs->recursive_update( {
1032             id => 1,
1033             owner => undef,
1034             });
1035              
1036             Updating a relationship including its (full) primary key:
1037              
1038             my $dvd = $dvd_rs->recursive_update( {
1039             id => 1,
1040             owner => {
1041             id => 2,
1042             name => "George",
1043             },
1044             });
1045              
1046             =head2 Treatment of might_have relationships
1047              
1048             In case the relationship is included but undefined in the updates hashref,
1049             all columns forming the relationship will be set to null.
1050              
1051             Updating the relationship:
1052              
1053             my $user = $user_rs->recursive_update( {
1054             id => 1,
1055             address => {
1056             street => "101 Main Street",
1057             city => "Podunk",
1058             state => "New York",
1059             }
1060             });
1061              
1062             Clearing the relationship:
1063              
1064             my $user = $user_rs->recursive_update( {
1065             id => 1,
1066             address => undef,
1067             });
1068              
1069             =head2 Treatment of has_many relations
1070              
1071             If a relationship key is included in the data structure with a value of undef
1072             or an empty array, all existing related rows will be deleted, or their foreign
1073             key columns will be set to null.
1074              
1075             The exact behaviour depends on the nullability of the foreign key columns and
1076             the value of the "if_not_submitted" parameter. The parameter defaults to
1077             undefined which neither nullifies nor deletes.
1078              
1079             When the array contains elements they are updated if they exist, created when
1080             not and deleted if not included.
1081              
1082             =head3 All foreign table columns are nullable
1083              
1084             In this case recursive_update defaults to nullifying the foreign columns.
1085              
1086             =head3 Not all foreign table columns are nullable
1087              
1088             In this case recursive_update deletes the foreign rows.
1089              
1090             Updating the relationship:
1091              
1092             Passing ids:
1093              
1094             my $user = $user_rs->recursive_update( {
1095             id => 1,
1096             owned_dvds => [1, 2],
1097             });
1098              
1099             Passing hashrefs:
1100              
1101             my $user = $user_rs->recursive_update( {
1102             id => 1,
1103             owned_dvds => [
1104             {
1105             name => 'temp name 1',
1106             },
1107             {
1108             name => 'temp name 2',
1109             },
1110             ],
1111             });
1112              
1113             Passing objects:
1114              
1115             my $user = $user_rs->recursive_update( {
1116             id => 1,
1117             owned_dvds => [ $dvd1, $dvd2 ],
1118             });
1119              
1120             You can even mix them:
1121              
1122             my $user = $user_rs->recursive_update( {
1123             id => 1,
1124             owned_dvds => [ 1, { id => 2 } ],
1125             });
1126              
1127             Clearing the relationship:
1128              
1129             my $user = $user_rs->recursive_update( {
1130             id => 1,
1131             owned_dvds => undef,
1132             });
1133              
1134             This is the same as passing an empty array:
1135              
1136             my $user = $user_rs->recursive_update( {
1137             id => 1,
1138             owned_dvds => [],
1139             });
1140              
1141             =head2 Treatment of many-to-many pseudo relations
1142              
1143             If a many-to-many accessor key is included in the data structure with a value
1144             of undef or an empty array, all existing related rows are unlinked.
1145              
1146             When the array contains elements they are updated if they exist, created when
1147             not and deleted if not included.
1148              
1149             RecursiveUpdate defaults to
1150             calling 'set_$rel' to update many-to-many relationships.
1151             See L<DBIx::Class::Relationship/many_to_many> for details.
1152             set_$rel effectively removes and re-adds all relationship data,
1153             even if the set of related items did not change at all.
1154              
1155             If L<DBIx::Class::IntrospectableM2M> is in use, RecursiveUpdate will
1156             look up the corresponding has_many relationship and use this to recursively
1157             update the many-to-many relationship.
1158              
1159             While both mechanisms have the same final result, deleting and re-adding
1160             all relationship data can have unwanted consequences if triggers or
1161             method modifiers are defined or logging modules like L<DBIx::Class::AuditLog>
1162             are in use.
1163              
1164             The traditional "set_$rel" behaviour can be forced by passing
1165             "m2m_force_set_rel => 1" to recursive_update.
1166              
1167             See L</is_m2m> for many-to-many pseudo relationship detection.
1168              
1169             Updating the relationship:
1170              
1171             Passing ids:
1172              
1173             my $dvd = $dvd_rs->recursive_update( {
1174             id => 1,
1175             tags => [1, 2],
1176             });
1177              
1178             Passing hashrefs:
1179              
1180             my $dvd = $dvd_rs->recursive_update( {
1181             id => 1,
1182             tags => [
1183             {
1184             id => 1,
1185             file => 'file0'
1186             },
1187             {
1188             id => 2,
1189             file => 'file1',
1190             },
1191             ],
1192             });
1193              
1194             Passing objects:
1195              
1196             my $dvd = $dvd_rs->recursive_update( {
1197             id => 1,
1198             tags => [ $tag1, $tag2 ],
1199             });
1200              
1201             You can even mix them:
1202              
1203             my $dvd = $dvd_rs->recursive_update( {
1204             id => 1,
1205             tags => [ 2, { id => 3 } ],
1206             });
1207              
1208             Clearing the relationship:
1209              
1210             my $dvd = $dvd_rs->recursive_update( {
1211             id => 1,
1212             tags => undef,
1213             });
1214              
1215             This is the same as passing an empty array:
1216              
1217             my $dvd = $dvd_rs->recursive_update( {
1218             id => 1,
1219             tags => [],
1220             });
1221              
1222             Make sure that set_$rel used to update many-to-many relationships
1223             even if IntrospectableM2M is loaded:
1224              
1225             my $dvd = $dvd_rs->recursive_update( {
1226             id => 1,
1227             tags => [1, 2],
1228             },
1229             { m2m_force_set_rel => 1 },
1230             );
1231              
1232             =head1 INTERFACE
1233              
1234             =head1 METHODS
1235              
1236             =head2 recursive_update
1237              
1238             The method that does the work here.
1239              
1240             =head2 is_m2m
1241              
1242             =over 4
1243              
1244             =item Arguments: $name
1245              
1246             =item Return Value: true, if $name is a many to many pseudo-relationship
1247              
1248             =back
1249              
1250             The function gets the information about m2m relations from
1251             L<DBIx::Class::IntrospectableM2M>. If it isn't loaded in the ResultSource
1252             class, the code relies on the fact:
1253              
1254             if($object->can($name) and
1255             !$object->result_source->has_relationship($name) and
1256             $object->can( 'set_' . $name )
1257             )
1258              
1259             to identify a many to many pseudo relationship. In a similar ugly way the
1260             ResultSource of that many to many pseudo relationship is detected.
1261              
1262             So if you need many to many pseudo relationship support, it's strongly
1263             recommended to load L<DBIx::Class::IntrospectableM2M> in your ResultSource
1264             class!
1265              
1266             =head2 get_m2m_source
1267              
1268             =over 4
1269              
1270             =item Arguments: $name
1271              
1272             =item Return Value: $result_source
1273              
1274             =back
1275              
1276             =head1 CONFIGURATION AND ENVIRONMENT
1277              
1278             DBIx::Class::RecursiveUpdate requires no configuration files or environment variables.
1279              
1280             =head1 DEPENDENCIES
1281              
1282             DBIx::Class
1283              
1284             optional but recommended:
1285             DBIx::Class::IntrospectableM2M
1286              
1287             =head1 INCOMPATIBILITIES
1288              
1289             None reported.
1290              
1291             =head1 BUGS AND LIMITATIONS
1292              
1293             The list of reported bugs can be viewed at L<http://rt.cpan.org/Public/Dist/Display.html?Name=DBIx-Class-ResultSet-RecursiveUpdate>.
1294              
1295             Please report any bugs or feature requests to
1296             C<bug-DBIx-Class-ResultSet-RecursiveUpdate@rt.cpan.org>, or through the web interface at
1297             L<http://rt.cpan.org>.
1298              
1299             =head1 AUTHORS
1300              
1301             =over 4
1302              
1303             =item *
1304              
1305             Zbigniew Lukasiak <zby@cpan.org>
1306              
1307             =item *
1308              
1309             John Napiorkowski <jjnapiork@cpan.org>
1310              
1311             =item *
1312              
1313             Alexander Hartmaier <abraxxa@cpan.org>
1314              
1315             =item *
1316              
1317             Gerda Shank <gshank@cpan.org>
1318              
1319             =back
1320              
1321             =head1 COPYRIGHT AND LICENSE
1322              
1323             This software is copyright (c) 2020 by Zbigniew Lukasiak, John Napiorkowski, Alexander Hartmaier.
1324              
1325             This is free software; you can redistribute it and/or modify it under
1326             the same terms as the Perl 5 programming language system itself.
1327              
1328             =cut