File Coverage

blib/lib/Array/To/Moose.pm
Criterion Covered Total %
statement 34 138 24.6
branch 3 100 3.0
condition 0 21 0.0
subroutine 12 20 60.0
pod 5 5 100.0
total 54 284 19.0


line stmt bran cond sub pod time code
1             package Array::To::Moose;
2              
3             # Copyright (c) Stanford University. June 6th, 2010.
4             # All rights reserved.
5             # Author: Sam Brain <samb@stanford.edu>
6             # This library is free software; you can redistribute it and/or modify
7             # it under the same terms as Perl itself, either Perl version 5.8.8 or,
8             # at your option, any later version of Perl 5 you may have available.
9             #
10              
11 22     22   753774 use 5.008008;
  22         90  
  22         1308  
12 22     22   142 use strict;
  22         131  
  22         1039  
13 22     22   152 use warnings;
  22         122  
  22         995  
14              
15             require Exporter;
16 22     22   127 use base qw( Exporter );
  22         42  
  22         5601  
17              
18             our %EXPORT_TAGS = (
19             'ALL' => [ qw( array_to_moose
20             throw_nonunique_keys throw_multiple_rows
21             set_class_ind set_key_ind ) ],
22             'TESTING' => [ qw( _check_descriptor _check_subobj
23             _check_ref_attribs _check_non_ref_attribs ) ],
24             );
25              
26             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'ALL'} }, @{ $EXPORT_TAGS{'TESTING'} } );
27              
28             our @EXPORT = qw( array_to_moose
29              
30             );
31              
32 22     22   23832 use version; our $VERSION = qv('0.0.8');
  22         89214  
  22         151  
33              
34             # BEGIN { $Exporter::Verbose=1 };
35              
36             #BEGIN { print "Got Array::To:Moose Module\n" }
37              
38 22     22   25832 use Params::Validate::Array qw(:all);
  22         398269  
  22         197  
39 22     22   43360 use Array::GroupBy qw(igroup_by str_row_equal);
  22         30540  
  22         7701  
40 22     22   175 use Carp;
  22         51  
  22         1162  
41 22     22   27326 use Data::Dumper;
  22         290045  
  22         3050  
42              
43             $Carp::Verbose = 1;
44              
45             $Data::Dumper::Terse = 1;
46             $Data::Dumper::Indent = 1;
47              
48             # strings for "key => ..." and "class => ..." indicators
49             my ($KEY, $CLASS);
50              
51 22     22   386 BEGIN { $KEY = 'key' ; $CLASS = 'class' }
  22         52579  
52              
53             # throw error if a HashRef[] key found to be non-unique
54             my $throw_nonunique_keys;
55              
56             # throw error if there are multiple candidate rows for an attribute
57             # which is a single object, "isa => 'MyObject'"
58             my $throw_multiple_rows;
59              
60             ############################################
61             # Set the indicators for "key => ..." and "class => ..."
62             # If there is no arg, reset them back to the default 'key' and 'class'
63             ############################################
64             sub set_key_ind {
65 0 0 0 0 1 0 croak "set_key_ind('$_[0]') not a legal identifier"
66             if defined $_[0] and $_[0] !~ /^\w+$/;
67              
68 0 0       0 $KEY = defined $_[0] ? $_[0] : 'key';
69             }
70              
71             ############################################
72             sub set_class_ind {
73 0 0 0 0 1 0 croak "set_class_ind('$_[0]') not a legal identifier"
74             if defined $_[0] and $_[0] !~ /^\w+$/;
75              
76 0 0       0 $CLASS = defined $_[0] ? $_[0] : 'class';
77             }
78              
79             ########################################
80             # throw error if non-unique keys in a HashRef['] is causing already-constructed
81             # Moose objects to be overwritten
82             # throw_nonunique_keys() to set, throw_nonunique_keys(0) to unset
83             ########################################
84 0 0   0 1 0 sub throw_nonunique_keys { $throw_nonunique_keys = defined $_[0] ? $_[0] : 1 }
85              
86             ########################################
87             # throw error if a single object attribute has multiple data rows
88             # throw_multiple_rows() to set throw_multiple_rows(0) to unset
89             ########################################
90 0 0   0 1 0 sub throw_multiple_rows { $throw_multiple_rows = defined $_[0] ? $_[0] : 1 }
91              
92             ##########
93             # Usage
94             # my $moose_object_ref = array_to_moose( data => $array_ref,
95             # desc => { ... },
96             # );
97             ############################################
98             sub array_to_moose {
99 3     3 1 3798 my ($data, $desc) = validate(@_,
100             [ data => { type => ARRAYREF },
101             desc => { type => HASHREF },
102             ]
103             );
104              
105 3 100       237 croak "'data => ...' isn't a 2D array (AoA)"
106             unless ref($data->[0]);
107              
108 1 50       15 croak 'empty descriptor'
109             unless keys %$desc;
110              
111             #print "data ", Dumper($data), "\ndesc ", Dumper($desc);
112              
113              
114 0           my $result = []; # returned result is either an array or a hash of objects
115              
116             # extract column of possible hash key
117 0           my $keycol;
118              
119 0 0         if (exists $desc->{$KEY}) {
120 0           $keycol = $desc->{$KEY};
121              
122 0           $result = {}; # returning a hashref
123              
124             }
125              
126             # _check_descriptor returns:
127             # $class, the class of the object
128             # $attribs, a hashref (attrib => column_number) of "simple" attributes
129             # (column numbers only)
130             # $ref_attribs, a hashref of attribute/column number values for
131             # non-simple attributes, currently limited to "ArrayRef[`a]",
132             # where `a is e.g 'Str', etc (i.e. `a is not a class)
133             # $sub_desc, a hashref of sub-objects.
134             # the keys are the attrib. names, the values the
135             # descriptors of the next level down
136              
137 0           my ($class, $attribs, $ref_attribs, $sub_obj_desc) =
138             _check_descriptor($data, $desc);
139              
140             #print "data ", Dumper($data), "\nattrib = ", Dumper($attribs),
141             # "\nargs = ", Dumper([ values %$attribs ]);
142              
143             #print "\$ref_attribs ", Dumper($ref_attribs); exit;
144              
145 0           my $iter = igroup_by(
146             data => $data,
147             compare => \&str_row_equal,
148             args => [ values %$attribs ],
149             );
150              
151 0           while (my $subset = $iter->()) {
152              
153             #print "subset: ", Dumper($subset), "\n";
154              
155             #print "before 1: attrib ", Dumper($attribs), "\ndata ", Dumper($subset);
156              
157             # change attribs from col numbers to values:
158             # from: { name => 1, sex => 2, ... }
159             # to { name => 'Smith, J.', sex => 'male', ... }
160 0           my %attribs = map { $_ => $subset->[0]->[$attribs->{$_}] } keys %$attribs;
  0            
161            
162              
163             # print "after 1: attrib ", Dumper(\%attribs), "\n";
164              
165             # add the 'simple ArrayRef' sub-objects
166             # (there should really be only one of these - test for it?)
167 0           while (my($attr_name, $col) = each %$ref_attribs) {
168 0           my @col = map { $_->[$col] } @$subset;
  0            
169 0           $attribs{$attr_name} = \@col;
170              
171             # ... or ...
172             #$attribs{$attr_name} = [ map { $_->[$col] } @$subset ];
173             }
174              
175             # print "after 2: attrib ", Dumper(\%attribs), "\n";
176              
177             # sub-objects - recursive call to array_to_moose()
178 0           while( my($attr_name, $desc) = each %$sub_obj_desc) {
179              
180 0 0         my $type = $class->meta->find_attribute_by_name($attr_name)->type_constraint
181             or croak "Moose attribute '$attr_name' has no type";
182              
183             #print "'$attr_name' has type '$type'";
184              
185 0           my $sub_obj = array_to_moose( data => $subset,
186             desc => $desc,
187             );
188              
189 0           $sub_obj = _check_subobj($class, $attr_name, $type, $sub_obj);
190              
191             #print "type $type\n";
192              
193 0           $attribs{$attr_name} = $sub_obj;
194             }
195              
196             # print "after 2: attrib ", Dumper(\%attribs), "\n";
197              
198 0           my $obj;
199 0           eval { $obj = $class->meta->new_object(%attribs) };
  0            
200 0 0         croak "Can't make a new '$class' object:\n$@\n"
201             if $@;
202              
203 0 0         if (defined $keycol) {
204 0           my $key_name = $subset->[0]->[$keycol];
205              
206             # optionally croak if we are overwriting an existing hash entry
207 0 0 0       croak "Non-unique key '$key_name' in '", $desc->{$CLASS}, "' class"
208             if exists $result->{$key_name} and $throw_nonunique_keys;
209              
210 0           $result->{$key_name} = $obj;
211             } else {
212 0           push @{$result}, $obj;
  0            
213             }
214             }
215 0           return $result;
216             }
217              
218             ############################################
219             # Usage: my ($class, $attribs, $ref_attribs, $sub_desc)
220             # = _check_descriptor($data, $desc)
221             #
222             # Check the correctness of the descriptor hashref, $desc.
223             #
224             # Checks of descriptor $desc include:
225             # 1. "class => 'MyClass'" line exists, and that class "MyClass" has
226             # been defined
227             # 2. for "attrib => N"
228             # or "key => N" lines, N, the column number, is an integer, and that
229             # the column numbers is within limits of the data
230             # 3. For "attrib => [N]", (note square brackets), N, the columnn number,
231             # is within limits of the data
232             #
233             # Returns:
234             # $class, the class name,
235             # $attribs, hashref (name => column_index) of "simple" attributes
236             # $ref_attribs hashref (name => column_index) of attribs which are
237             # ArrayRef[']s of simple types (i.e. not a Class)
238             # (HashRef[']s not implemented)
239             # $sub_desc hashref (name => desc) of sub-object descriptors
240             ############################################
241             sub _check_descriptor {
242 0     0     my ($data, $desc) = @_;
243              
244             # remove from production!
245 0 0         croak "_check_descriptor() needs two arguments"
246             unless @_ == 2;
247              
248 0 0         my $class = $desc->{$CLASS}
249             or croak "No class descriptor '$CLASS => ...' in descriptor:\n",
250             Dumper($desc);
251              
252 0           my $meta;
253              
254             # see other example of getting meta in Moose::Manual::???
255 0           eval{ $meta = $class->meta };
  0            
256 0 0         croak "Class '$class' not defined: $@"
257             if $@;
258              
259 0           my $ncols = @{ $data->[0] };
  0            
260              
261             # separate out simple (i.e. non-reference) attributes, reference
262             # attributes, and sub-objects
263 0           my ($attrib, $ref_attrib, $sub_desc);
264              
265 0           while ( my ($name, $value) = each %$desc) {
266              
267             # check lines which have 'simple' column numbers ( attrib or key => N)
268 0 0 0       unless (ref($value) or $name eq $CLASS) {
269              
270 0           my $msg = "attribute '$name => $value'";
271              
272 0 0         croak "$msg must be a (non-negative) integer"
273             unless $value =~ /^\d+$/;
274              
275 0 0         croak "$msg greater than # cols in the data ($ncols)"
276             if $value > $ncols - 1;
277             }
278              
279             # check to see if there are attributes called 'class' or 'key'
280 0 0 0       if ($name eq $CLASS or $name eq $KEY) {
281 0 0         croak "The '$class' object has an attribute called '$name'"
282             if $meta->find_attribute_by_name($name);
283              
284 0           next;
285             }
286              
287 0 0         croak "Attribute '$name' not in '$class' object"
288             unless $meta->find_attribute_by_name($name);
289              
290 0 0         if ((my $ref = ref($value)) eq 'HASH') {
    0          
    0          
291 0           $sub_desc->{$name} = $value;
292              
293             } elsif ($ref eq 'ARRAY') {
294             # descr entry looks like, e.g.:
295             # attrib => [6],
296             #
297             # ( or attrib => [key => 6, value => 7], in future... ?)
298              
299 0 0         croak "attribute must be of form, e.g.: '$name => [N], "
300             . "where N is a single integer'"
301             unless @$value == 1;
302              
303 0           my $msg = "attribute '$name => [ " . $value->[0] . " ]'. '" .
304             $value->[0] . "'";
305              
306 0 0         croak "$msg must be a (non-negative) integer"
307             unless $value->[0] =~ /^\d+$/;
308              
309 0 0         croak "$msg greater than # cols in the data ($ncols)"
310             if $value->[0] > $ncols - 1;
311              
312 0           $ref_attrib->{$name} = $value->[0];
313              
314             } elsif ($ref) {
315 0           croak "attribute '$name' can't be a '$ref' reference";
316              
317             } else {
318             # "simple" attribute
319 0           $attrib->{$name} = $value;
320             }
321             }
322              
323              
324             # check ref- and ...
325 0 0         _check_ref_attribs($class, $ref_attrib)
326             if $ref_attrib;
327              
328             # ... non-ref attributes from the descriptor against the Moose object
329 0 0         _check_non_ref_attribs($class, $attrib)
330             if $attrib;
331              
332 0 0 0       croak "no attributes with column numbers in descriptor:\n", Dumper($desc)
333             unless $attrib and %$attrib;
334              
335 0           return ($class, $attrib, $ref_attrib, $sub_desc);
336             }
337              
338             ########################################
339             # Usage: $sub_obj = _check_subobj($class, $attr_name, $type, $sub_obj);
340             #
341             # $class is the name of the current class
342             # $attr_name is the name of the attribute in the descriptor, e.g.
343             # MyObjs => { ... } (used only diagnostic messages)
344             # $type is the expected Moose type of the sub-object
345             # i.e. 'HashRef[MyObj]', 'ArrayRef[MyObj]', or 'MyObj'
346             # $sub_obj_ref Reference to the data (just returned from a recursive call to
347             # array_to_moose() ) to be stored in the sub-object,
348             # i.e. isa => 'HashRef[MyObj]', isa => 'ArrayRef[MyObj]',
349             # or isa => 'MyObj'
350             #
351             #
352             # Checks that the data in $sub_obj_ref agrees with the type of the object to
353             # contain it
354             # if $type is a ref to an object (isa => 'MyObj'), _check_subobj() converts
355             # $sub_obj_ref from an arrayref to sub-object to ref to a subobj
356             # (see notes in code below)
357             #
358             # Throws error is it finds a type mis-match
359             ########################################
360             sub _check_subobj {
361 0     0     my ($class, $attr_name, $type, $sub_obj) = @_;
362              
363             # for now...
364 0 0         croak "_check_subobj() should have 4 args" unless @_ == 4;
365              
366             #my $type = $class->meta->find_attribute_by_name($attr_name)->type_constraint
367             # or croak "Moose class '$class' attribute '$attr_name' has no type";
368              
369 0 0         if ( $type =~ /^HashRef\[([^]]*)\]/ ) {
    0          
370              
371             #print "subobj is of type ", ref($sub_obj), "\n";
372             #print "subobj ", Dumper($sub_obj);
373              
374 0 0         croak "Moose attribute '$attr_name' has type '$type' "
375             . "but your descriptor produced an object "
376             . "of type '" . ref($sub_obj) . "'\n"
377             if ref($sub_obj) ne 'HASH';
378              
379             #print "\$1 '$1', value: ", ref( ( values %{$sub_obj} )[0] ), "\n";
380              
381 0           croak("Moose attribute '$attr_name' has type '$type' "
382             . "but your descriptor produced an object "
383 0           . "of type 'HashRef[" . ref( ( values %{$sub_obj} )[0] )
384             . "]'\n")
385 0 0         if ref( ( values %{$sub_obj} )[0] ) ne $1;
386              
387             } elsif ( $type =~ /^ArrayRef\[([^]]*)\]/ ) {
388              
389 0 0         croak "Moose attribute '$attr_name' has type '$type' "
390             . "but your descriptor produced an object "
391             . "of type '" . ref($sub_obj) . "'\n"
392             if ref($sub_obj) ne 'ARRAY';
393              
394 0 0         croak "Moose attribute '$attr_name' has type '$type' "
395             . "but your descriptor produced an object "
396             . "of type 'ArrayRef[" . ref( $sub_obj->[0] ) . "]'\n"
397             if ref( $sub_obj->[0] ) ne $1;
398              
399             } else {
400              
401             # not isa => 'ArrayRef[MyObj]' or 'HashRef[MyObj]' but isa => 'MyObj',
402             # *but* since array_to_moose() can return only a hash- or arrayref of Moose
403             # objects, $sub_obj will be an arrayref of Moose objects, which we convert to a
404             # ref to an object
405              
406 0 0         croak "Moose attribute '$attr_name' has type '$type' "
407             . "but your descriptor generated a '"
408             . ref($sub_obj)
409             . "' object and not the expected ARRAY"
410             unless ref $sub_obj eq 'ARRAY';
411              
412             # optionally give error if we got more than one row
413 0 0 0       croak "Expected a single '$type' object, but got ",
414             scalar @$sub_obj, " of them"
415             if @$sub_obj != 1 and $throw_multiple_rows;
416              
417             # convert from arrayref of objects to ref to object
418 0           $sub_obj = $sub_obj->[0];
419              
420             # print "\$sub_obj type is ", ref($sub_obj), "\n";
421              
422 0 0         croak "Moose attribute '$attr_name' has type '$type' "
423             . "but your descriptor produced an object "
424             . "of type '" . ref( $sub_obj ) . "'"
425             unless ref( $sub_obj ) eq $type;
426             }
427 0           return $sub_obj;
428             }
429              
430             {
431              
432             # The Moose type hierarchy (from Moose::Manual::Types) is:
433             # Any
434             # Item
435             # Bool
436             # Maybe[`a]
437             # Undef
438             # Defined
439             # Value
440             # Str
441             # Num
442             # Int
443             # ClassName
444             # RoleName
445             # Ref
446             # ScalarRef[`a]
447             # ArrayRef[`a]
448             # HashRef[`a]
449             # CodeRef
450             # RegexpRef
451             # GlobRef
452             # FileHandle
453             # Object
454              
455             # So the test for
456              
457             my %simple_types;
458              
459             BEGIN
460             {
461 22     22   74 %simple_types = map { $_ => 1 }
  198         32767  
462             qw ( Any Item Bool Undef Defined Value Str Num Int );
463             }
464              
465             ########################################
466             # Usage:
467             # _check_ref_attribs($class, $ref_attribs);
468             # Checks that "reference" attributes from the descriptor (e.g., attr => [N])
469             # are ArrayRef[]'s of simple attributes in the Moose object
470             # (e.g., isa => ArrayRef['Str'])
471             # Throws an exception if check fails
472             #
473             # where:
474             # $class is the current Moose class
475             # $ref_attribs an hashref of Moose attributes which are "ref
476             # attributes", e.g., " has 'hobbies' (isa => 'ArrayRef[Str]'); "
477             #
478             ########################################
479             sub _check_ref_attribs {
480 0     0     my ($class, $ref_attribs) = @_;
481              
482 0 0         my $meta = $class->meta
483             or croak "No meta for class '$class'?";
484              
485 0           foreach my $attrib ( keys %{ $ref_attribs } ) {
  0            
486 0           my $msg = "Moose class '$class' ref attrib '$attrib'";
487              
488 0 0         my $constraint = $meta->find_attribute_by_name($attrib)->type_constraint
489             or croak "$msg has no type constraint";
490              
491             #print "_check_ref_attribs(): $attrib $constraint\n";
492              
493 0 0         if ($constraint =~ /^ArrayRef\[([^]]*)\]/ ) {
494              
495 0 0         croak "$msg has bad type '$constraint' ('$1' is not a simple type)"
496             unless $simple_types{$1};
497              
498 0           return;
499             }
500 0           croak "$msg must be an ArrayRef[`a] and not a '$constraint'";
501             }
502             }
503              
504              
505             ########################################
506             # Usage:
507             # _check_non_ref_attribs($class, $non_ref_attribs);
508             # Checks that non-ref attributes from the descriptor (e.g., attr => N)
509             # are indeed simple attributes in the Moose object (e.g., isa => 'Str')
510             # Throws an exception if check fails
511             #
512             #
513             # where:
514             # $class is the current Moose class
515             # $non_ref_attribs an hashref of Moose attributes which are
516             # non-reference, or "simple" attributes like 'Str', 'Int', etc.
517             # The key is the attribute name, the value the type
518             #
519             ########################################
520             sub _check_non_ref_attribs {
521 0     0     my ($class, $attribs) = @_;
522              
523 0 0         my $meta = $class->meta
524             or croak "No meta for class '$class'?";
525              
526 0           foreach my $attrib ( keys %{ $attribs } ) {
  0            
527 0           my $msg = "Moose class '$class', attrib '$attrib'";
528              
529 0 0         my $constraint = $meta->find_attribute_by_name($attrib)->type_constraint
530             or croak "$msg has no type (isa => ...)";
531              
532             #print "_check_non_ref_attribs(): $attrib '$constraint'\n";
533              
534             # kludge for Maybe[`]
535 0           $constraint =~ /^Maybe\[([^]]+)\]/;
536 0 0         $constraint = $1 if $1;
537              
538             #print " after: $attrib '$constraint'\n";
539              
540 0 0         next if $simple_types{$constraint};
541              
542 0           $msg = "$msg has type '$constraint', but your descriptor had '$attrib => "
543             . $attribs->{$attrib} . "'.";
544              
545 0 0         $msg .= " (Did you forget the '[]' brackets?)"
546             if $constraint =~ /^ArrayRef/;
547            
548 0           croak $msg;
549             }
550             }
551            
552             } # end of local block
553              
554              
555             1;
556              
557             __END__
558              
559             =head1 NAME
560              
561             Array::To::Moose - Build Moose objects from a data array
562              
563             =head1 VERSION
564              
565             This document describes Array::To::Moose version 0.0.8
566              
567             =head1 SYNOPSIS
568              
569             use Array::To::Moose;
570             # or
571             use Array::To::Moose qw(array_to_moose set_class_ind set_key_ind
572             throw_nonunique_keys throw_multiple_rows );
573              
574             C<Array::To::Moose> exports function C<array_to_moose()> by default, and
575             convenience functions C<set_class_ind()>, C<set_key_ind()>,
576             C<throw_nonunique_keys()> and C<throw_multiple_rows()> if requested.
577              
578             =head2 array_to_moose
579              
580             C<array_to_moose()> builds Moose objects from suitably-sorted
581             2-dimensional arrays of data of the type returned by, e.g.,
582             L<DBI::selectall_arrayref()|DBI/selectall_arrayref>
583             i.e. a reference to an array containing
584             references to an array for each row of data fetched.
585              
586             =head2 Example 1a
587              
588             package Car;
589             use Moose;
590              
591             has 'make' => (is => 'ro', isa => 'Str');
592             has 'model' => (is => 'ro', isa => 'Str');
593             has 'year' => (is => 'ro', isa => 'Int');
594              
595             package CarOwner;
596             use Moose;
597              
598             has 'last' => (is => 'ro', isa => 'Str');
599             has 'first' => (is => 'ro', isa => 'Str');
600             has 'Cars' => (is => 'ro', isa => ArrayRef[Car]');
601              
602             ...
603              
604             # in package main:
605              
606             use Array::To::Moose;
607              
608             # In this dataset Alex owns two cars, Jim one, and Alice three
609             my $data = [
610             [ qw( Green Alex Ford Focus 2011 ) ],
611             [ qw( Green Alex VW Jetta 2009 ) ],
612             [ qw( Green Jim Honda Civic 2007 ) ],
613             [ qw( Smith Alice Buick Regal 2012 ) ],
614             [ qw( Smith Alice Toyota Camry 2008 ) ],
615             [ qw( Smith Alice BMW X5 2010 ) ],
616             ];
617              
618             my $CarOwners = array_to_moose(
619             data => $data,
620             desc => {
621             class => 'CarOwner',
622             last => 0,
623             first => 1,
624             Cars => {
625             class => 'Car',
626             make => 2,
627             model => 3,
628             year => 4,
629             } # Cars
630             } # Car Owners
631             );
632              
633             print $CarOwners->[2]->Cars->[1]->model; # prints "Camry"
634              
635             =head2 Example 1b - Hash(ref) Sub-objects
636              
637             In the above example, C<array_to_moose()> returns a reference to an
638             B<array> of C<CarOwner> objects, C<$CarOwners>.
639              
640             If a B<hash> of C<CarOwner> objects is required, a "C<key =E<gt>>... " entry
641             must be added to the descriptor hash. For example, to construct a hash of
642             C<CarOwner> objects, whose key is the owner's first name, (unique for
643             every person in the example data), the call
644             becomes:
645              
646             my $CarOwnersH = array_to_moose(
647             data => $data,
648             desc => {
649             class => 'CarOwner',
650             key => 1, # note key
651             last => 0,
652             first => 1,
653             Cars => {
654             class => 'Car',
655             make => 2,
656             model => 3,
657             year => 4,
658             } # Cars
659             } # Car Owners
660             );
661              
662             print $CarOwnersH->{Alex}->Cars->[0]->make; # prints "Ford"
663              
664             Similarly, to construct the C<Cars> sub-objects as I<hash> sub-objects
665             (and not an I<array> as above), define C<CarOwner> as:
666              
667             package CarOwner;
668             use Moose;
669              
670             has 'last' => (is => 'ro', isa => 'Str' );
671             has 'first' => (is => 'ro', isa => 'Str' );
672             has 'Cars' => (is => 'ro', isa => 'HashRef[Car]'); # Was 'ArrayRef[Car]'
673              
674             and noting that the car C<make> is unique for each person in the C<$data> dataset, we
675             construct the reference to an array of objects with the call:
676              
677             $CarOwners = array_to_moose(
678             data => $data,
679             desc => {
680             class => 'CarOwner',
681             last => 0,
682             first => 1,
683             Cars => {
684             class => 'Car',
685             key => 2, # note key
686             model => 3,
687             year => 4,
688             } # Cars
689             } # Car Owners
690             );
691              
692             print $CarOwners->[2]->Cars->{BMW}->model; # prints 'X5'
693              
694             =head2 Example 1c - "Simple" Reference Attributes
695              
696             If, instead of the car owner object containing an ArrayRef or HashRef of
697             C<Car> sub-objects, it contains, say, a ArrayRef of strings representing the
698             names of the car makers:
699              
700             package SimpleCarOwner;
701             use Moose;
702              
703             has 'last' => (is => 'ro', isa => 'Str' );
704             has 'first' => (is => 'ro', isa => 'Str' );
705             has 'CarMakers' => (is => 'ro', isa => 'ArrayRef[Str]');
706              
707             Using the same dataset from Example 1a, we construct an arrayref
708             C<SimpleCarOwner> objects as:
709              
710             $SimpleCarOwners = array_to_moose(
711             data => $data,
712             desc => {
713             class => 'SimpleCarOwner',
714             last => 0,
715             first => 1,
716             CarMakers => [2], # Note the '[...]' brackets
717             }
718             );
719              
720             print $SimpleCarOwners->[2]->[1]; # prints 'Toyota'
721              
722             I.e., when the object attribute is an I<ArrayRef> of one of the Moose "simple" types,
723             e.g. C<'Str'>, C<'Num'>, C<'Bool'>,
724             etc (See L<Moose::Manual::Types|THE TYPES>), then the column number should
725             appear in square brackets ('C<CarMakers =E<gt> [2]>' above) to differentiate them from the bare
726             types (C<last =E<gt> 0,> and C<first =E<gt> 1,> above).
727              
728             Note that Array::To::Moose doesn't (yet) handle the case of hashrefs of
729             "simple" types, e.g., C<( isa =E<gt> "HashRef[Str]" )>
730              
731             =head2 Example 2 - Use with DBI
732              
733             The main rationale for writing C<Array::To::Moose> is to make it easy to build
734             Moose objects from data extracted from relational databases,
735             especially when the database query
736             involves multiple tables with one-to-many relationships to each other.
737              
738             As an example, consider a database which models patients making visits
739             to a clinic on multiple occasions, and on each visit, having a doctor
740             run some tests and diagnose the patient's complaint. In this model, the
741             database I<Patient> table would have a one-to-many relationship with the
742             I<Visit> table, which in turn would have a one-to-many relationship with
743             the I<Test> table
744              
745             The corresponding Moose model has nested Moose objects which reflects those
746             one-to-many relationships, i.e.,
747             multiple Visit objects per Patient object and multiple Test objects
748             per Visit object, declared as:
749              
750             package Test;
751             use Moose;
752             has 'name' => (is => 'rw', isa => 'Str');
753             has 'result' => (is => 'rw', isa => 'Str');
754              
755             package Visit;
756             use Moose;
757             has 'date' => (is => 'rw', isa => 'Str' );
758             has 'md' => (is => 'rw', isa => 'Str' );
759             has 'diagnosis' => (is => 'rw', isa => 'Str' );
760             has 'Tests' => (is => 'rw', isa => 'HashRef[Test]' );
761              
762             package Patient;
763             use Moose;
764             has 'last' => (is => 'rw', isa => 'Str' );
765             has 'first' => (is => 'rw', isa => 'Str' );
766             has 'Visits' => (is => 'rw', isa => 'ArrayRef[Visit]' );
767              
768             In the main program:
769              
770             use DBI;
771             use Array::To::Moose;
772              
773             ...
774              
775             my $sql = q{
776             SELECT
777             P.Last, P.First
778             ,V.Date, V.Doctor, V.Diagnosis
779             ,T.Name, T.Result
780             FROM
781             Patient P
782             ,Visit V
783             ,Test T
784             WHERE
785             -- join clauses
786             P.Patient_key = V.Patient_key
787             AND V.Visit_key = T.Visit_key
788             ...
789             ORDER BY
790             P.Last, P.First, V.Date
791             };
792              
793             my $dbh = DBI->connect(...);
794              
795             my $data = $dbh->selectall_arrayref($sql);
796              
797             # rows of @$data contain:
798             # Last, First, Date, Doctor, Diagnosis, Name, Result
799             # at positions: [0] [1] [2] [3] [4] [5] [6]
800              
801             my $patients = array_to_moose(
802             data => $data,
803             desc => {
804             class => 'Patient',
805             last => 0,
806             first => 1,
807             Visits => {
808             class => 'Visit',
809             date => 2,
810             md => 3,
811             diagnosis => 4,
812             Tests => {
813             class => 'Test',
814             key => 5,
815             name => 5,
816             result => 6,
817             } # tests
818             } # visits
819             } # patients
820             );
821              
822             print $patients->[2]->Visits->[0]->Tests->{BP}->result; # prints '120/80'
823              
824             Note: We used the Test C<name> as the key for the Visit 'C<Tests>', as the
825             tests have unique names within any one Visit.
826             (See t/5.t)
827              
828             =head1 DESCRIPTION
829              
830             As shown in the above examples, the general usage is:
831              
832             package MyClass;
833             use Moose;
834             (define Moose object(s))
835             ...
836             use Array::To::Moose;
837             ...
838             my $data_ref = selectall_arrayref($sql); # for example
839              
840             my $object_ref = array_to_moose(
841             data => $data_ref
842             desc => {
843             class => 'MyClass',
844             key => K, # only for HashRefs
845             attrib_1 => N1,
846             attrib_2 => N2,
847             ...
848             attrib_m => [ M ],
849             ...
850             SubObject => {
851             class => 'MySubClass',
852             ...
853             }
854             }
855             );
856              
857             Where:
858              
859             C<array_to_moose()> returns an array- or hash reference of C<MyClass>
860             Moose objects.
861             All Moose classes (C<MyClass>, C<MySubClass>, etc) must
862             already have been defined by the user.
863              
864             C<$data_ref> is a reference to an array containing references to arrays of
865             scalars of the kind returned by, e.g.,
866             L<DBI::selectall_arrayref()|DBI/selectall_arrayref>
867              
868             C<desc> (descriptor) is a reference to a hash which contains several types
869             of data:
870              
871             C<class =E<gt>> 'MyObj' is I<required> and defines the Moose class or
872             package which will contain the data. The user should have defined this class
873             already.
874              
875             C<key =E<gt> N > is required
876             if the Moose object being constructed is to be a hashref, either at
877             the top-level Moose object returned from C<array_to_moose()> or as a
878             "C<isa =E<gt> 'HashRef[...]'>" sub-object.
879              
880             C<attrib =E<gt> N > where C<attrib> is the name of a Moose attribute
881             ("C<has 'attrib' =E<gt>> ...")
882              
883             C<attrib =E<gt> [ N ] > where C<attrib> is the name of a Moose "simple" sub-attribute
884             ("C<has =E<gt> 'attrib' ( isa =E<gt> 'ArrayRef[Type]' ...)> "), where C<Type>
885             is a "simple" Moose type, e.g., C<'Str', 'Int'>, etc.
886              
887             In the above cases, C<N> is a positive integer containing the
888             the corresponding zero-indexed
889             column number in the data array where that attribute's data is to be found.
890              
891             =head2 Sub-Objects
892              
893             C<array_to_moose()> can handle three types of Moose sub-objects, i.e.:
894              
895             an array of sub-objects:
896              
897             has => 'Sub_Obj' ( isa => 'ArrayRef[MyObj]' );
898              
899             a hash of sub-objects:
900              
901             has => 'Sub_Obj' ( isa => 'HashRef[MyObj]' );
902              
903             or a single sub-object:
904              
905             has => 'Sub_Obj' ( isa => 'MyObj' );
906              
907             the descriptor entry for C<Sub_Obj> in each of these cases is (almost) the same:
908              
909             desc => {
910             class => ...
911             ...
912             Sub_Obj => {
913             class => 'MyObj',
914             key => <keycol> # HashRef['] only
915             attrib_a => <N>,
916             ...
917             } # end SubObj
918             ...
919             } # end desc
920              
921             (A C<HashRef[']> sub-object will also I<require> a
922             C<key =E<gt> N> entry in the descriptor).
923              
924             In addition, C<array_to_moose()> can also handle C<ArrayRef>s of "simple"
925             types:
926              
927             has => 'Sub_Obj' ( isa => 'ArrayRef[Type]' );
928              
929             where C<Type> is a "simple" Moose type, e.g., C<'Str', 'Int, 'Bool'>, etc.
930              
931             =head2 Ordering the data
932              
933             C<array_to_moose()> does not sort the input data array, and does all
934             processing in a single pass through the data. This means that the data in the
935             array must be sorted properly for the algorithm to work.
936              
937             For example, in the previous Patient/Visit/Test example, in which there are
938             many I<Test>s per I<Visit> and many I<Visit>s per I<Patient>, the data in the
939             I<Test> column(s) must change the fastest, the I<Visit> data slower, and the
940             I<Patient> data the slowest:
941              
942             Patient Visit Test
943             ------ ----- ----
944             P1 V1 T1
945             P1 V1 T2
946             P1 V1 T3
947             P1 V2 T4
948             P1 V2 T5
949             P2 V3 T6
950             P2 V3 T7
951             P2 V4 T8
952              
953             In SQL this would be accomplished by a C<SORT BY> clause, e.g.:
954              
955             SORT BY Patient.Key, Visit.Key, Test.Key
956              
957             =head2 throw_nonunique_keys ()
958              
959             By default, C<array_to_moose()> does not check the uniqueness of hash key
960             values within the data. If the key values in the data are not unique,
961             existing hash entries will get overwritten, and
962             the sub-object will contain the value from the last data row which
963             contained that key value. For example:
964              
965             package Employer;
966             use Moose;
967             has 'year' => (is => 'rw', isa => 'Str');
968             has 'name' => (is => 'rw', isa => 'Str');
969              
970             package Person;
971             use Moose;
972             has 'name' => (is => 'rw', isa => 'Str' );
973             has 'Employers' => (is => 'rw', isa => 'HashRef[Employer]');
974              
975             ...
976              
977             my $data = [
978             [ 'Anne Miller', '2005', 'Acme Corp' ],
979             [ 'Anne Miller', '2006', 'Acme Corp' ],
980             [ 'Anne Miller', '2007', 'Widgets, Inc' ],
981             ...
982             ];
983              
984             The call:
985              
986             my $obj = array_to_moose(
987             data => $data,
988             desc => {
989             class => 'Person',
990             name => 0,
991             Employers => {
992             class => 'Employer',
993             key => 2, # using employer name as key
994             year => 1,
995             } # Employer
996             } # Person
997             );
998              
999             Because the employer was C<'Acme Corp'> in years 2005 & 2006,
1000             C<array_to_moose>
1001             will silently overwrite the 2005 Employer object with the data for the
1002             2006 Employer object:
1003              
1004             print $obj->[0]->Employers->{'Acme Corp'}->year, "\n"; # prints '2006'
1005              
1006             Calling C<throw_uniq_keys()> (either with no argument, or with a non-zero
1007             argument) enables reporting of non-unique keys. In the above example,
1008             C<array_to_moose()> would exit with warning:
1009              
1010             Non-unique key 'Acme Corp' in 'Employer' class ...
1011              
1012             Calling C<throw_uniq_keys(0)>, i.e. with an argument of zero will disable
1013             subsequent reporting of non-unique keys.
1014             (See t/8c.t)
1015              
1016             =head2 throw_multiple_rows ()
1017              
1018             For single-occurence sub-objects (i.e. C<( isa =E<gt> 'MyObj' )>),
1019             if the data contains more than one row of data for the sub-object,
1020             only the first row will be used to construct the single sub-object and
1021             C<array_to_moose()> will not report the fact. E.g.:
1022              
1023             package Salary;
1024             use Moose;
1025             has 'year' => (is => 'rw', isa => 'Str');
1026             has 'amount' => (is => 'rw', isa => 'Int');
1027              
1028             package Person;
1029             use Moose;
1030             has 'name' => (is => 'rw', isa => 'Str' );
1031             has 'Salary' => (is => 'rw', isa => 'Salary'); # a single object
1032              
1033             ...
1034              
1035             my $data = [
1036             [ 'John Smith', '2005', 23_350 ],
1037             [ 'John Smith', '2006', 24_000 ],
1038             [ 'John Smith', '2007', 26_830 ],
1039             ...
1040             ];
1041              
1042             The call:
1043              
1044             my $obj = array_to_moose(
1045             data => $data,
1046             desc => {
1047             class => 'Person'
1048             name => 0,
1049             Salary => {
1050             class => 'Salary',
1051             year => 1,
1052             amount => 2
1053             } # Salary
1054             } # Person
1055             );
1056              
1057             would silently assign to C<Salary>, the first row of the three Salary
1058             data rows, i.e. for year 2005:
1059              
1060             print $object->[0]->Salary->year, "\n"; # prints '2005'
1061              
1062             Calling C<throw_multiple_rows()>
1063             (either with no argument, or with a non-zero argument)
1064             enables reporting of this situation. In the
1065             above example, C<array_to_moose()> will exit with error:
1066              
1067             Expected a single 'Salary' object, but got 3 of them ...
1068              
1069             Calling C<throw_multiple_rows(0)>, i.e. with an argument of zero will disable
1070             subsequent reporting of this error.
1071             (See t/8d.t)
1072              
1073             =head2 set_class_ind (), set_key_ind ()
1074              
1075             Problems arise if the Moose objects being constructed contain attributes
1076             called I<class> or I<key>, causing ambiguities in the descriptor. (Does
1077             C<key =E<gt> 5> mean the I<attribute> C<key> or the I<hash key> C<key> is in
1078             the 5th column?)
1079              
1080             In these cases, C<set_class_ind()> and
1081             C<set_key_ind()> can be used to change the keywords for C<class
1082             =E<gt> ...> and C<key =E<gt> ...> descriptor entries.
1083              
1084             For example:
1085              
1086             package Letter;
1087             use Moose;
1088              
1089             has 'address' => ( is => 'ro', isa => 'Str' );
1090             has 'class' => ( is => 'ro', isa => 'PostalClass' );
1091             ...
1092              
1093             set_key_ind('package'); # use "package =>" in place of "class =>"
1094              
1095             my $letters = array_to_moose(
1096             data => $data,
1097             desc => {
1098             package => 'Letter', # the Moose class
1099             address => 0,
1100             class => 1, # the attribute 'class'
1101             ...
1102             }
1103             );
1104              
1105              
1106             =head2 Read-only Attributes
1107              
1108             One of the recommendations of L<Moose::Manual::BestPractices>
1109             is to make attributes read-only (C<isa =E<gt> 'ro'>) wherever
1110             possible. C<Array::To::Moose> supports this by evaluating all the
1111             attributes for a given object given in the descriptor, then including
1112             them all in the call to C<new(...)> when constructing the object.
1113              
1114             For Moose objects with attributes which are
1115             sub-objects, i.e. references to a Moose object, or references to an array or hash of
1116             Moose objects, it means that the sub-objects must be evaluated before the
1117             C<new()> call. The effect of this for multi-leveled Moose objects is that
1118             object evaluations are carried out depth-first.
1119              
1120             =head2 Treatment of C<NULL>s
1121              
1122             C<array_to_moose()> uses
1123             L<Array::GroupBy::igroup_by|Array::GroupBy.pm/DESCRIPTION>
1124             to compare the rows in
1125             the data given in C<data =E<gt> ...>, using function
1126             L<Array::GroupBy::str_row_equal()|Array::GroupBy.pm/Routines_str_row_equal()_and_num_row_equal()>
1127             which compares the data as I<strings>.
1128              
1129             If the data contains C<undef> values, typically returned from
1130             database SQL queries in which L<DBI> maps NULL values to C<undef>, when
1131             C<str_row_equal()> encounters C<undef> elements in I<corresponding> column
1132             positions, it will consider the elements C<equal>. When I<corresponding>
1133             column elements are defined and C<undef> respectively, the elements are
1134             considered C<unequal>.
1135              
1136             This truth table demonstrates the various combinations:
1137              
1138             -------+------------+--------------+--------------+--------------
1139             row 1 | ('a', 'b') | ('a', undef) | ('a', undef) | ('a', 'b' )
1140             row 2 | ('a', 'b') | ('a', undef) | ('a', 'b' ) | ('a', undef)
1141             -------+------------+--------------+--------------+--------------
1142             equal? | yes | yes | no | no
1143              
1144             =head1 EXPORT
1145              
1146             C<array_to_moose> by default; C<throw_nonunique_keys>, C<throw_multiple_rows>,
1147             C<set_class_ind> and C<set_key_ind> if requested.
1148              
1149             =head1 DIAGNOSTICS
1150              
1151             Errors in the call of C<array-to-moose()> will be caught by
1152             L<Params::Validate::Array>, q.v.
1153              
1154             <array-to-moose> does a lot of error checking, and is probably annoyingly
1155             chatty. Most of the errors generated are, of course, self-explanatory :-)
1156              
1157             =head1 DEPENDENCIES
1158              
1159             Carp
1160             Params::Validate::Array
1161             Array::GroupBy
1162              
1163             =head1 SEE ALSO
1164              
1165             L<DBI>, L<Moose>, L<Array::GroupBy>
1166              
1167             =head1 BUGS
1168              
1169             The handling of Moose type constraints is primitive.
1170              
1171             =head1 AUTHOR
1172              
1173             Sam Brain <samb@stanford.edu>
1174              
1175             =head1 COPYRIGHT AND LICENSE
1176              
1177             Copyright (c) Stanford University. June 6th, 2010.
1178             All rights reserved.
1179             Author: Sam Brain <samb@stanford.edu>
1180              
1181             This library is free software; you can redistribute it and/or modify
1182             it under the same terms as Perl itself, either Perl version 5.8.8 or,
1183             at your option, any later version of Perl 5 you may have available.
1184              
1185             =cut
1186              
1187             # TODO
1188             #
1189             # test for non-square data array?
1190             #
1191             # - allow argument "compare => sub {...}" in array_to_moose() call to
1192             # allow a user-defined row-comparison routine to be passed to
1193             # Array::GroupBy::igroup_by()
1194             #
1195             # - make it Mouse-compatible? (All meta->... stuff would break?)
1196              
1197             ##### SUBROUTINE INDEX #####
1198             # #
1199             # gen by index_subs.pl #
1200             # on 24 Apr 2014 21:11 #
1201             # #
1202             ############################
1203              
1204              
1205             ####### Packages ###########
1206              
1207             # Array::To::Moose ......................... 1
1208             # array_to_moose ......................... 2
1209             # set_class_ind .......................... 2
1210             # set_key_ind ............................ 2
1211             # throw_multiple_rows .................... 2
1212             # throw_nonunique_keys ................... 2
1213             # _check_descriptor ...................... 4
1214             # _check_non_ref_attribs ................. 9
1215             # _check_ref_attribs ..................... 8
1216             # _check_subobj .......................... 6
1217