File Coverage

blib/lib/Text/vFile/Base.pm
Criterion Covered Total %
statement 48 97 49.4
branch 4 40 10.0
condition 2 39 5.1
subroutine 13 18 72.2
pod 11 12 91.6
total 78 206 37.8


line stmt bran cond sub pod time code
1             package Text::vFile::Base;
2              
3 5     5   151331 use strict;
  5         15  
  5         279  
4              
5             =head1 NAME
6              
7             Text::vFile::Base - Base class for most of the functional classes based on the vCard/vCalendar etc spec.
8             Most of the hard work in breaking apart lines of data happens using methods in here.
9              
10             =head1 SYNOPSIS
11              
12             package Text::vFoo;
13              
14             use Text::vFile::Base;
15             use vars qw(@ISA);
16             push @ISA, qw(Text::vCard::Base);
17              
18             # Tell vFile that BEGIN:VFOO line creates one of these objects
19             $Text::vFile::classMap{'VCARD'}=__PACKAGE__;
20            
21             # Then you will need to create a varHandler - although there are defaults to try
22             # and get you going.
23             #
24             # As well you will need to define more exotic, type specific loaders
25             #
26             # And finally accessors are your responsibility
27              
28             =head1 END USAGE
29              
30             To tell your users how to use this module:
31              
32             use Text::vFoo;
33             my $loader = Text::vFoo->loader( source => "youppi.vfoo" );
34              
35             while (my $vfoo = $loader->next) {
36             $vfoo->all_night;
37             }
38              
39             # or even sexier
40            
41             while (my $vfoo = <$loader> ) {
42             $vfoo->all_night;
43             }
44              
45             It may sound silly, but it should be mentioned. Just becase the user
46             says:
47              
48             my $loader = Text::vFoo->loader( );
49              
50             Does not mean there will be any complaints if they try to load non-vfoo data.
51             If the source has vcards in it - that's what they're going to get.
52              
53             =cut
54              
55              
56 5     5   28 use Carp; $SIG{__DIE__} = \&Carp::confess;
  5         9  
  5         540  
57 5     5   5302 use Data::Dumper; $Data::Dumper::Indent=1; $Data::Dumper::Sortkeys=1;
  5         54457  
  5         427  
58              
59 5     5   4334 use lib qw(lib);
  5         3593  
  5         25  
60 5     5   2723 use Text::vFile;
  5         17  
  5         7675  
61              
62             my $typeSequence=1;
63              
64             sub _nextSequence {
65 60     60   113 return $typeSequence++;
66             }
67              
68             =head1 CONVENIENCE METHODS
69              
70             =over 4
71              
72             =item @objects = $class->load( key => value )
73              
74             Calls the Text::vFile load routine. Should slurp all the objects
75             and return as an array/array ref.
76              
77             =cut
78              
79             sub load {
80 1     1 1 10 shift;
81 1         7 Text::vFile->load(@_);
82             }
83              
84             =item $loader = $class->loader( key => value )
85              
86             Returns an object which can return back objects one at a time. Nice
87             if you want to iterate through a stack of vcards at your leisure.
88              
89             =cut
90              
91             sub loader {
92 4     4 1 190 shift;
93 4         32 return Text::vFile->new(@_);
94             }
95              
96             =item $object = class->new
97              
98             Make a new object object that represents this vFile data being parsed.
99              
100             =cut
101              
102             sub new {
103              
104 15 50   15 1 46 my $class = ref($_[0]) ? ref(shift) : shift;
105 15 50       39 my $opts = ref($_[0]) ? $_[0] : {@_};
106              
107 15         44 my $self = bless {}, $class;
108              
109 15         32 map { $self->$_( $opts->{$_} ) } keys %$opts;
  0         0  
110              
111 15         45 return $self;
112              
113             }
114              
115             =back
116              
117             =head1 DATA HANDLERS
118              
119             =over 4
120              
121             =item varHandler
122              
123             Returns a hash ref mapping the item label to a handler name. Ie:
124              
125             {
126             'FN' => 'singleText',
127             'NICKNAME' => 'multipleText',
128             'PHOTO' => 'singleBinary',
129             'BDAY' => 'singleText',
130             'ADR' => 'ADR', # Not really necessary
131             };
132              
133             By default if there is no explicit handler then the vFile loader will
134              
135             - is there a method called "load_NAME"?
136             - test does the item have type attributes or not
137             - yes? singleTextTyped
138             - no? singleText
139            
140              
141             =cut
142            
143             sub varHandler {
144 15     15 1 37 return {};
145             }
146              
147             =item typeDefault
148              
149             Additional information where handlers require type info. Such as ADR - is this
150             a home, postal, or whatever? If not supplied the RFC specifies what types they should
151             default to.
152              
153             from vCard:
154              
155             {
156             'ADR' => [ qw(intl postal parcel work) ],
157             'LABEL' => [ qw(intl postal parcel work) ],
158             'TEL' => [ qw(voice) ],
159             'EMAIL' => [ qw(internet) ],
160             };
161              
162             =cut
163              
164             sub typeDefault {
165 60     60 1 106 return {};
166             }
167              
168             =item load_singleText
169              
170             Loads a single text item with no processing other than unescape text
171              
172             =cut
173              
174             sub load_singleText {
175              
176 15     15 1 26 my $val=$_[3];
177 15         35 $val=~s/\\([\n,])/$1/gs;
178             # $val=~s/\\n/\n/gs;
179 15         51 $_[0]->{$_[1]}{'value'}=$val;
180 15 0 33     46 $_[0]->{$_[1]}{'attr'}=$_[2] if $_[2] && ref($_[2]) eq "HASH" && keys %{$_[2]};
  0   33     0  
181              
182 15         47 return $_[0]->{$_[1]};
183              
184             }
185              
186             =item load_singleDate
187              
188             Loads a date creating a DateTime::Format::ICal object. Thanks Dave!
189              
190             =cut
191              
192             sub load_singleDate {
193              
194 0     0 1 0 my $val=$_[3];
195 0 0       0 unless (%DateTime::Format::ICal::) {
196 0         0 eval "use DateTime::Format::ICal";
197 0 0 0     0 warn "Cannot create date/time objects: $@\n" and return if $@;
198             }
199              
200 0         0 eval {
201 0         0 $_[0]->{$_[1]}{'value'}=DateTime::Format::ICal->parse_datetime( iso8601 => $val );
202 0 0       0 }; if ($@) {
203 0         0 warn "$val; $@\n";
204             }
205              
206 0 0 0     0 $_[0]->{$_[1]}{'attr'}=$_[2] if $_[2] && ref($_[2]) eq "HASH" && keys %{$_[2]};
  0   0     0  
207              
208 0         0 return $_[0]->{$_[1]};
209              
210             }
211              
212             =item load_singleDuration
213              
214             Loads a data duration using DateTime::Format::ICal.
215              
216             =cut
217              
218             sub load_singleDuration {
219              
220 0     0 1 0 my $val=$_[3];
221              
222 0 0       0 unless (%DateTime::Format::ICal::) {
223 0         0 eval "use DateTime::Format::ICal";
224 0 0 0     0 warn "Cannot create date/time objects: $@\n" and return if $@;
225             }
226              
227 0         0 eval {
228 0         0 $_[0]->{$_[1]}{'value'}=DateTime::Format::ICal->parse_duration( $val );
229 0 0       0 }; if ($@) {
230 0         0 warn "$val; $@\n";
231             }
232              
233 0 0 0     0 $_[0]->{$_[1]}{'attr'}=$_[2] if $_[2] && ref($_[2]) eq "HASH" && keys %{$_[2]};
  0   0     0  
234              
235 0         0 return $_[0]->{$_[1]};
236              
237             }
238              
239             =item load_multipleText
240              
241             This is text that is separated by commas. The text is then unescaped. An array
242             of items is created.
243              
244             =cut
245              
246             sub load_multipleText {
247              
248 0     0 1 0 my @vals=split /(?
249 0         0 map { s/\\,/,/ } @vals;
  0         0  
250              
251 0         0 $_[0]->{$_[1]}{'value'}=\@vals;
252 0 0 0     0 $_[0]->{$_[1]}{'attr'}=$_[2] if $_[2] && ref($_[2]) eq "HASH" && keys %{$_[2]};
  0   0     0  
253              
254 0         0 return $_[0]->{$_[1]};
255              
256             }
257              
258             =item load_singleTextType
259              
260             Load text that has a type attribute. Each text of different type attributes
261             will be handled independantly in as a hash entry. If no type attribute is supplied
262             then the typeDefaults types will be used. A line can have multiple types. In the
263             case where multiple types have the same value "_alias" indicators are created.
264             The preferred type is stored in "_pref"
265              
266             =cut
267              
268             sub load_singleTextTyped {
269            
270 60     60 0 132 my $typeDefault=$_[0]->typeDefault;
271              
272 60         76 my $attr=$_[2];
273              
274 60         96 my %type=();
275 60         62 map { map { $type{lc $_}=1 } (split /,/, $_) } @{$attr->{'type'}};
  90         168  
  105         299  
  60         107  
276             # delete $attr->{'type'};
277 60 50       172 map { $type{ lc $_ }=1 } @{$typeDefault->{$_[1]}} unless scalar(keys %type);
  0         0  
  0         0  
278              
279 60         83 my $item={};
280 60         67 push @{$_[0]->{$_[1]}}, $item;
  60         188  
281 60         124 $item->{'value'}=$_[3];
282 60         108 $item->{'type'}=\%type;
283 60 50       176 $item->{'attr'}=$attr if keys %$attr;
284 60         106 $item->{'sequence'}=_nextSequence();
285              
286 60         149 return $item;
287              
288             }
289              
290             =item load_singleBinary
291              
292             Not done as I don't have example data yet.
293              
294             =cut
295              
296             sub load_singleBinary {
297 0     0 1   my ($self, $name, $attr, $value) = @_;
298              
299 0   0       my $encoding = $attr->{'encoding'} || $attr->{'ENCODING'};
300              
301             # type=b means Base64; I don't know about others
302 0 0         if ($encoding) {
303              
304 0 0         if (lc $encoding eq "b") {
305 0           eval "use MIME::Base64";
306 0 0 0       warn "Cannot decode binary MIME encoded objects: $@\n" and return if $@;
307 0           $self->{$name}{'value'} = MIME::Base64::decode_base64($value);
308             } else {
309 0           warn "Unknown encoding $encoding for $name\n";
310 0           return undef;
311             }
312              
313             } else {
314            
315             # This must be an URL
316              
317             }
318 0 0 0       $self->{$name}{'attr'}=$attr if $attr && ref($attr) eq "HASH" && keys %{$attr};
  0   0        
319              
320 0           die "_singleBinary not done\n";
321             }
322              
323              
324             =item @split = $self->split_value($line [, $delimiter]);
325              
326             This method returns a array ref containing the $line elements
327             split by the delimiter, but ignores escaped delimiters.
328             If no $delimiter is supplied then a comma "," is used by default.
329              
330             =cut
331              
332             sub split_value {
333 0     0 1   my ($self, $line, $delim) = @_;
334              
335 0 0         $delim = ',' unless $delim;
336              
337 0           my @list = split(/(?
338              
339 0 0         return wantarray ? @list : \@list;
340             }
341              
342             =back
343              
344             =head1 SUPPORT
345              
346             For technical support please email to jlawrenc@cpan.org ...
347             for faster service please include "Text::vFile" and "help" in your subject line.
348              
349             =head1 AUTHOR
350              
351             Jay J. Lawrence - jlawrenc@cpan.org
352             Infonium Inc., Canada
353             http://www.infonium.ca/
354              
355             =head1 COPYRIGHT
356              
357             Copyright (c) 2003 Jay J. Lawrence, Infonium Inc. All rights reserved.
358             This program is free software; you can redistribute
359             it and/or modify it under the same terms as Perl itself.
360              
361             The full text of the license can be found in the
362             LICENSE file included with this module.
363              
364             =head1 ACKNOWLEDGEMENTS
365              
366             =head1 SEE ALSO
367              
368             =cut
369              
370              
371             1;
372