File Coverage

lib/BoutrosLab/TSVStream/IO/Role/Dyn.pm
Criterion Covered Total %
statement 26 40 65.0
branch 1 6 16.6
condition 0 3 0.0
subroutine 9 11 81.8
pod 0 1 0.0
total 36 61 59.0


line stmt bran cond sub pod time code
1             package BoutrosLab::TSVStream::IO::Role::Dyn;
2              
3             # safe Perl
4 1     1   10558 use warnings;
  1         2  
  1         28  
5 1     1   3 use strict;
  1         1  
  1         14  
6 1     1   3 use Carp;
  1         1  
  1         52  
7              
8 1     1   3 use Moose::Role;
  1         1  
  1         6  
9 1     1   3440 use namespace::autoclean;
  1         1  
  1         6  
10              
11 1     1   431 use BoutrosLab::TSVStream::IO::Reader::Dyn;
  1         2  
  1         39  
12 1     1   573 use BoutrosLab::TSVStream::IO::Writer::Dyn;
  1         3  
  1         376  
13              
14             with 'BoutrosLab::TSVStream::IO::Role::Fixed';
15              
16             =head1 NAME
17              
18             BoutrosLab::TSVStreamDyn::IO::Role::Dyn
19              
20             =head1 SYNOPSIS
21              
22             # in a Moose class definition...
23              
24             my $_fields = [ qw(foo bar) ];
25             sub _fields { return $_fields }
26              
27             with 'BoutrosLab::TSVStream::IO::Role::Dyn';
28              
29             has 'foo' => ( ... );
30             has 'bar' => ( ... );
31             ...
32              
33             =head1 DESCRIPTION
34              
35             This role provides methods to create a file/iostream reader/writer
36             for a class, using a stream of lines with tab separated fields for
37             each record, converting to/from an object of the class. Usually,
38             the stream will start with an initial line that has the field names
39             as a tab separated record.
40              
41             This is essentially the same as a TSVStream::IO::Role::Fixed, but for
42             a TSVStream::IO::Role::Dyn the list of fixed name fields (which may be
43             empty), is followed by a dynamically determined list of extra fields.
44             The entire stream must consistently contain the same number of fields
45             in each record.
46              
47             This role provides attributes C<dyn_names> and C<dyn_values>,
48             which are both arrays of strings. C<dyn_names> contains the names
49             of the extra fields that follow the ones specified in _fields.
50             This attribute will have the same list of names for every record
51             returned from a single reader object, and should contain the same
52             list of names for every record passed to a single writer object.
53             C<dyn_values> is the list of values read or to be written for
54             each record in a stream (and these can be different, of course).
55              
56             This role also provided a BUILDARGS wrapper that alows the
57             constructor to be given field_values=>[fld1,fld2,...] instead of
58             providing each field explicitly by name. You can either provide
59             all of the values (both for the _fields and the dyn_fields) in this
60             one array, or else you can provide field_values=[fid1,fld2,...] and
61             dyn_values=>[dyn_1,dyn_2,...] as two separate arguments.
62              
63             =cut
64              
65             sub _reader_class {
66 111     111   402 return 'BoutrosLab::TSVStream::IO::Reader::Dyn';
67             }
68              
69             sub _writer_class {
70 0     0   0 return 'BoutrosLab::TSVStream::IO::Writer::Dyn';
71             }
72              
73             has dyn_fields => (
74             is => 'ro',
75             required => 1,
76             isa => 'ArrayRef[Str]'
77             );
78              
79             has dyn_values => (
80             is => 'rw',
81             required => 1,
82             isa => 'ArrayRef[Str]'
83             );
84              
85             around BUILDARGS => sub {
86             my $orig = shift;
87             my $class = shift;
88             my $arg = ref($_[0]) ? $_[0] : { @_ };
89             if (my $field_values = delete $arg->{field_values}) {
90             my $fldnames = $class->_fields;
91             my @v = @$field_values;
92             if (defined $fldnames && ref($fldnames)) {
93             $arg->{ $_ } = shift @v for @$fldnames;
94             }
95             $arg->{dyn_values} = \@v;
96             }
97             $class->$orig( $arg );
98             };
99              
100             # add a reader/writer access method for each dynamic fieldname
101             # (these are not attributes - they modify the values array)
102              
103             has install_methods => (
104             is => 'ro',
105             isa => 'Bool',
106             default => 0
107             );
108              
109             sub BUILD {
110 84     84 0 55427 my $self = shift;
111 84 50       2564 unless ($self->install_methods) {
112 84         744 $self->_check_dups( $self->_fields );
113 84         166 return;
114             }
115              
116 0           $self->_check_dups( @{ $self->_fields}, @{ $self->dyn_fields } );
  0            
  0            
117              
118 0           my $meta = $self->meta;
119 0           my $df = $self->dyn_fields;
120 0           while(my($ind, $fld) = each @$df) {
121             $meta->add_method( $fld => sub {
122 0     0     my $self = shift;
123             my $seen = $self->install_methods
124 0   0       && grep { $_ eq $fld } @{ $self->dyn_fields };
125             # print(
126             # " Validating attribute $fld against: (",
127             # join( ', ', @{ $self->dyn_fields } ),
128             # ") ",
129             # ( $seen ? "good\n" : "BAD\n" )
130             # );
131 0 0         croak( "calling dynamic attribute ($fld) not in this stream" )
132             unless $seen;
133 0           my $values = $self->dyn_values;
134 0 0         $values->[$ind] = shift if @_;
135 0           $values->[$ind];
136 0           } );
137             }
138             }
139              
140              
141             =head1 AUTHOR
142              
143             John Macdonald - Boutros Lab
144              
145             =head1 ACKNOWLEDGEMENTS
146              
147             Paul Boutros, Phd, PI - Boutros Lab
148              
149             The Ontario Institute for Cancer Research
150              
151             =cut
152              
153             1;
154