File Coverage

blib/lib/WWW/Google/Contacts/Contact.pm
Criterion Covered Total %
statement 22 69 31.8
branch 1 24 4.1
condition 0 5 0.0
subroutine 7 16 43.7
pod 4 9 44.4
total 34 123 27.6


line stmt bran cond sub pod time code
1             package WWW::Google::Contacts::Contact;
2             {
3             $WWW::Google::Contacts::Contact::VERSION = '0.39';
4             }
5              
6 17     17   103 use Moose;
  17         34  
  17         134  
7 17     17   132648 use MooseX::Types::Moose qw( Str );
  17         1131045  
  17         186  
8 17         173 use WWW::Google::Contacts::Types qw(
9             Category
10             Name
11             PhoneNumber ArrayRefOfPhoneNumber
12             Email ArrayRefOfEmail
13             IM ArrayRefOfIM
14             Organization ArrayRefOfOrganization
15             PostalAddress ArrayRefOfPostalAddress
16             CalendarLink ArrayRefOfCalendarLink
17             Birthday
18             ContactEvent ArrayRefOfContactEvent
19             ExternalId ArrayRefOfExternalId
20             Gender
21             GroupMembership ArrayRefOfGroupMembership
22             Hobby ArrayRefOfHobby
23             Jot ArrayRefOfJot
24             Language ArrayRefOfLanguage
25             Priority
26             Sensitivity
27             Relation ArrayRefOfRelation
28             UserDefined ArrayRefOfUserDefined
29             Website ArrayRefOfWebsite
30             Photo
31 17     17   108330 );
  17         29270  
32 17     17   363610 use WWW::Google::Contacts::Meta::Attribute::Trait::XmlField;
  17         49  
  17         408  
33 17     17   102 use Carp;
  17         42  
  17         33619  
34              
35             sub create_url {
36 0     0 0 0 my $self = shift;
37 0         0 return sprintf( "%s://www.google.com/m8/feeds/contacts/default/full",
38             $self->server->protocol );
39             }
40              
41             extends 'WWW::Google::Contacts::Base';
42              
43             with 'WWW::Google::Contacts::Roles::CRUD';
44              
45             has id => (
46             isa => Str,
47             is => 'ro',
48             writer => '_set_id',
49             predicate => 'has_id',
50             traits => ['XmlField'],
51             xml_key => 'id',
52             );
53              
54             has etag => (
55             isa => Str,
56             is => 'ro',
57             writer => '_set_etag',
58             predicate => 'has_etag',
59             traits => ['XmlField'],
60             xml_key => 'gd:etag',
61             include_in_xml => sub { 0 }, # This is set in HTTP headers
62             );
63              
64             has link => (
65             is => 'rw',
66             trigger => \&_set_link,
67             traits => ['XmlField'],
68             xml_key => 'link',
69             include_in_xml => sub { 0 },
70             );
71              
72             # What to do with different link types
73             my $link_map = {
74             'self' => sub { my ( $self, $link ) = @_; $self->_set_id( $link->{href} ) },
75             'http://schemas.google.com/contacts/2008/rel#photo' => sub {
76             my ( $self, $link ) = @_;
77             $self->photo( { %$link, server => $self->server } );
78             },
79             };
80              
81             sub _set_link {
82 0     0   0 my ( $self, $links ) = @_;
83 0         0 foreach my $link ( @{$links} ) {
  0         0  
84 0 0       0 next unless ( defined $link_map->{ $link->{rel} } );
85 0         0 my $code = $link_map->{ $link->{rel} };
86 0         0 $self->$code($link);
87             }
88             }
89              
90             has photo => (
91             isa => Photo,
92             is => 'rw',
93             coerce => 1,
94             );
95              
96             has category => (
97             isa => Category,
98             is => 'rw',
99             predicate => 'has_category',
100             traits => ['XmlField'],
101             xml_key => 'category',
102             default => sub { undef },
103             coerce => 1,
104             );
105              
106             has notes => (
107             isa => Str,
108             is => 'rw',
109             predicate => 'has_notes',
110             traits => ['XmlField'],
111             xml_key => 'content',
112             is_element => 1,
113             );
114              
115             has name => (
116             isa => Name,
117             is => 'rw',
118             predicate => 'has_name',
119             traits => ['XmlField'],
120             xml_key => 'gd:name',
121             handles => [
122             qw( given_name additional_name family_name
123             name_prefix name_suffix full_name )
124             ],
125             default => sub { undef }, # empty Name object, so handles will work
126             coerce => 1,
127             );
128              
129             has phone_number => (
130             isa => ArrayRefOfPhoneNumber,
131             is => 'rw',
132             predicate => 'has_phone_number',
133             traits => ['XmlField'],
134             xml_key => 'gd:phoneNumber',
135             coerce => 1,
136             );
137              
138             has email => (
139             isa => ArrayRefOfEmail,
140             is => 'rw',
141             predicate => 'has_email',
142             traits => ['XmlField'],
143             xml_key => 'gd:email',
144             coerce => 1,
145             );
146              
147             has im => (
148             isa => ArrayRefOfIM,
149             is => 'rw',
150             predicate => 'has_im',
151             traits => ['XmlField'],
152             xml_key => 'gd:im',
153             coerce => 1,
154             );
155              
156             has organization => (
157             isa => ArrayRefOfOrganization,
158             is => 'rw',
159             predicate => 'has_organization',
160             traits => ['XmlField'],
161             xml_key => 'gd:organization',
162             coerce => 1,
163             );
164              
165             has postal_address => (
166             isa => ArrayRefOfPostalAddress,
167             is => 'rw',
168             predicate => 'has_postal_address',
169             traits => ['XmlField'],
170             xml_key => 'gd:structuredPostalAddress',
171             coerce => 1,
172             );
173              
174             has billing_information => (
175             isa => Str,
176             is => 'rw',
177             predicate => 'has_billing_information',
178             traits => ['XmlField'],
179             xml_key => 'gContact:billingInformation',
180             is_element => 1,
181             );
182              
183             has birthday => (
184             isa => Birthday,
185             is => 'rw',
186             predicate => 'has_birthday',
187             traits => ['XmlField'],
188             xml_key => 'gContact:birthday',
189             is_element => 1,
190             coerce => 1,
191             );
192              
193             has calendar_link => (
194             isa => ArrayRefOfCalendarLink,
195             is => 'rw',
196             predicate => 'has_calendar_link',
197             traits => ['XmlField'],
198             xml_key => 'gContact:calendarLink',
199             coerce => 1,
200             );
201              
202             has directory_server => (
203             isa => Str,
204             is => 'rw',
205             predicate => 'has_directory_server',
206             traits => ['XmlField'],
207             xml_key => 'gContact:directoryServer',
208             is_element => 1,
209             );
210              
211             has event => (
212             isa => ArrayRefOfContactEvent,
213             is => 'rw',
214             predicate => 'has_event',
215             traits => ['XmlField'],
216             xml_key => 'gContact:event',
217             coerce => 1,
218             );
219              
220             has external_id => (
221             isa => ArrayRefOfExternalId,
222             is => 'rw',
223             predicate => 'has_external_id',
224             traits => ['XmlField'],
225             xml_key => 'gContact:excternalId',
226             coerce => 1,
227             );
228              
229             has gender => (
230             isa => Gender,
231             is => 'rw',
232             predicate => 'has_gender',
233             traits => ['XmlField'],
234             xml_key => 'gContact:gender',
235             coerce => 1,
236             );
237              
238             has group_membership => (
239             isa => ArrayRefOfGroupMembership,
240             is => 'rw',
241             predicate => 'has_group_membership',
242             traits => ['XmlField'],
243             xml_key => 'gContact:groupMembershipInfo',
244             coerce => 1,
245             );
246              
247             has hobby => (
248             isa => ArrayRefOfHobby,
249             is => 'rw',
250             predicate => 'has_hobby',
251             traits => ['XmlField'],
252             xml_key => 'gContact:hobby',
253             coerce => 1,
254             );
255              
256             has initials => (
257             isa => Str,
258             is => 'rw',
259             predicate => 'has_initials',
260             traits => ['XmlField'],
261             xml_key => 'gContact:initials',
262             is_element => 1,
263             );
264              
265             has jot => (
266             isa => ArrayRefOfJot,
267             is => 'rw',
268             predicate => 'has_jot',
269             traits => ['XmlField'],
270             xml_key => 'gContact:jot',
271             coerce => 1,
272             );
273              
274             has language => (
275             isa => ArrayRefOfLanguage,
276             is => 'rw',
277             predicate => 'has_language',
278             traits => ['XmlField'],
279             xml_key => 'gContact:language',
280             coerce => 1,
281             );
282              
283             has maiden_name => (
284             isa => Str,
285             is => 'rw',
286             predicate => 'has_maiden_name',
287             traits => ['XmlField'],
288             xml_key => 'gContact:maidenName',
289             is_element => 1,
290             );
291              
292             has mileage => (
293             isa => Str,
294             is => 'rw',
295             predicate => 'has_mileage',
296             traits => ['XmlField'],
297             xml_key => 'gContact:mileage',
298             is_element => 1,
299             );
300              
301             has nickname => (
302             isa => Str,
303             is => 'rw',
304             predicate => 'has_nickname',
305             traits => ['XmlField'],
306             xml_key => 'gContact:nickname',
307             is_element => 1,
308             );
309              
310             has occupation => (
311             isa => Str,
312             is => 'rw',
313             predicate => 'has_occupation',
314             traits => ['XmlField'],
315             xml_key => 'gContact:occupation',
316             is_element => 1,
317             );
318              
319             has priority => (
320             isa => Priority,
321             is => 'rw',
322             predicate => 'has_priority',
323             traits => ['XmlField'],
324             xml_key => 'gContact:priority',
325             coerce => 1,
326             );
327              
328             has relation => (
329             isa => ArrayRefOfRelation,
330             is => 'rw',
331             predicate => 'has_relation',
332             traits => ['XmlField'],
333             xml_key => 'gContact:relation',
334             coerce => 1,
335             );
336              
337             has sensitivity => (
338             isa => Sensitivity,
339             is => 'rw',
340             predicate => 'has_sensitivity',
341             traits => ['XmlField'],
342             xml_key => 'gContact:sensitivity',
343             is_element => 1,
344             coerce => 1,
345             );
346              
347             has shortname => (
348             isa => Str,
349             is => 'rw',
350             predicate => 'has_shortname',
351             traits => ['XmlField'],
352             xml_key => 'gContact:shortname',
353             is_element => 1,
354             );
355              
356             has subject => (
357             isa => Str,
358             is => 'rw',
359             predicate => 'has_subject',
360             traits => ['XmlField'],
361             xml_key => 'gContact:subject',
362             is_element => 1,
363             );
364              
365             has user_defined => (
366             isa => ArrayRefOfUserDefined,
367             is => 'rw',
368             predicate => 'has_user_defined',
369             traits => ['XmlField'],
370             xml_key => 'gContact:userDefinedField',
371             coerce => 1,
372             );
373              
374             has website => (
375             isa => ArrayRefOfWebsite,
376             is => 'rw',
377             predicate => 'has_website',
378             traits => ['XmlField'],
379             xml_key => 'gContact:website',
380             coerce => 1,
381             );
382              
383             # Stolen from Meta/Attribute/Native/MethodProvider/Array.pm, need coercion
384             sub add_phone_number {
385 0     0 1 0 my ( $self, $phone ) = @_;
386 0 0       0 $self->phone_number( [] ) unless $self->has_phone_number;
387 0         0 push @{ $self->phone_number }, to_PhoneNumber($phone);
  0         0  
388             }
389              
390             sub add_email {
391 1     1 1 8 my ( $self, $email ) = @_;
392 1 50       44 $self->email( [] ) unless $self->has_email;
393 1         2 push @{ $self->email }, to_Email($email);
  1         40  
394             }
395              
396             sub add_user_defined {
397 0     0 0   my ( $self, $user_def ) = @_;
398 0 0         $self->user_defined( [] ) unless $self->has_user_defined;
399 0           push @{ $self->user_defined }, to_UserDefined($user_def);
  0            
400             }
401              
402             sub add_event {
403 0     0 0   my ( $self, $event ) = @_;
404 0 0         $self->event( [] ) unless $self->has_event;
405 0           push @{ $self->event }, to_ContactEvent($event);
  0            
406             }
407              
408             sub add_website {
409 0     0 0   my ( $self, $website ) = @_;
410 0 0         $self->website( [] ) unless $self->has_website;
411 0           push @{ $self->website }, to_Website($website);
  0            
412             }
413              
414             sub add_relation {
415 0     0 0   my ( $self, $relation ) = @_;
416 0 0         $self->relation( [] ) unless $self->has_relation;
417 0           push @{ $self->relation }, to_Relation($relation);
  0            
418             }
419              
420             sub add_group_membership {
421 0     0 1   my ( $self, $group ) = @_;
422 0 0         $self->group_membership( [] ) unless $self->has_group_membership;
423 0 0 0       if ( not ref($group) and $group !~ m{^http} ) {
424              
425             # It's probably a group name.
426             # As it stands right now, can't deal with this in the coercion, need access to server obj
427 0           my @groups =
428             WWW::Google::Contacts::GroupList->new( server => $self->server )
429             ->search( { title => $group } );
430 0 0         if ( scalar @groups == 0 ) {
    0          
431 0           croak "Can not find a group with name: $group";
432             }
433             elsif ( scalar @groups > 1 ) {
434 0           croak
435             "Can not add group membership. Found several groups with group name: $group";
436             }
437 0           $group = shift @groups;
438             }
439 0           push @{ $self->group_membership }, to_GroupMembership($group);
  0            
440             }
441              
442             sub groups {
443 0     0 1   my $self = shift;
444              
445 0           my $to_ret = [];
446 0   0       my $membership = $self->group_membership || [];
447 0           foreach my $member ( @{$membership} ) {
  0            
448 0           push @{$to_ret},
  0            
449             WWW::Google::Contacts::Group->new(
450             id => $member->href,
451             server => $self->server
452             )->retrieve;
453             }
454 0 0         return wantarray ? @{$to_ret} : $to_ret;
  0            
455             }
456              
457 17     17   130 no Moose;
  17         39  
  17         146  
458             __PACKAGE__->meta->make_immutable;
459             1;
460             __END__
461              
462             =head1 SYNOPSIS
463              
464             use WWW::Google::Contacts;
465              
466             my $google = WWW::Google::Contacts->new( username => "your.username", password => "your.password" );
467              
468             my $contact = $google->new_contact;
469             $contact->full_name("Emmett Brown");
470              
471             A lot of fields, such as email, phone number and so on, are accessible as array refs.
472              
473             foreach my $email (@{ $contact->email }) {
474             print "He got email address: " . $email->value . "\n";
475             }
476              
477             When you have made changes to your contact, you need to save them back to Google. This is done either
478             by a B<create> call (for new contacts) or an B<update> call (for existing contacts).
479              
480             $contact->create;
481              
482             Alternatively, you can use the B<create_or_update> method, which will do the right thing.
483              
484             $contact->create_or_update;
485              
486              
487             =head1 METHODS
488              
489             =head2 $contact->create
490              
491             Writes the contact to your Google account.
492              
493             =head2 $contact->retrieve
494              
495             Fetches contact details from Google account.
496              
497             =head2 $contact->update
498              
499             Updates existing contact in your Google account.
500              
501             =head2 $contact->delete
502              
503             Deletes contact from your Google account.
504              
505             =head2 $contact->create_or_update
506              
507             Creates or updates contact, depending on if it already exists.
508              
509             =head1 ATTRIBUTES
510              
511             All these attributes are gettable and settable on Contact objects.
512              
513             =head2 given_name
514              
515             $contact->given_name("Arnold");
516              
517             =head2 additional_name
518              
519             $contact->additional_name("J");
520              
521             =head2 family_name
522              
523             $contact->family_name("Rimmer");
524              
525             =head2 name_prefix
526              
527             $contact->name_prefix("Mrs");
528              
529             =head2 name_suffix
530              
531             $contact->name_suffix("III");
532              
533             =head2 full_name
534              
535             If this is set to what seems like "$given_name $family_name", those attributes will be automatically set.
536              
537             =head2 email
538              
539             $contact->email is, if defined, an array reference with 1 or more Email objects.
540             The Email objects have the following accessors;
541              
542             =over 4
543              
544             =item type
545              
546             This is an object in itself, which has 2 accessors; B<name> and B<uri>.
547              
548             =item label
549              
550             If you don't want to use the predefined types (defined by Google) you can set this label instead.
551              
552             =item value
553              
554             The email address.
555              
556             =item display_name
557              
558             An optional display name.
559              
560             =item primary
561              
562             A boolean stating whether this is the primary email address.
563              
564             =back
565              
566             Example code (set the first work email as the primary address):
567              
568             foreach my $email (@{ $contact->email }) {
569             if ( $email->type->name eq 'work' ) {
570             $email->primary(1);
571             last;
572             }
573             }
574              
575              
576             Explicitly setting all email details:
577              
578             $contact->email({
579             type => "work",
580             value => 'shenanigans@example.com',
581             display_name => 'Shenanigans',
582             primary => 1,
583             });
584              
585             Note that this will overwrite any previous email addresses for the contact. To add rather than replace,
586             see I<add_email> below.
587              
588             If you're just setting the email value, type will default to "work" and leave other fields empty.
589              
590             $contact->email( 'smeghead@reddwarf.net' );
591              
592             To specify several email addresses, you could either;
593              
594             =over 4
595              
596             =item * provide them all in an array
597              
598             $contact->email([
599             { type => "work", value => 'underpaid@bigcompany.com' },
600             { type => "home", value => 'angryblogger@someblogsite.com' },
601             ]);
602              
603             =item * call add_email
604              
605             $contact->add_email( 'homer@simpson.name' );
606              
607             =back
608              
609             =head2 phone_number
610              
611             $contact->phone_number is, if defined, an array reference with 1 or more PhoneNumber objects.
612             The PhoneNumber objects have the following accessors;
613              
614             =over 4
615              
616             =item type
617              
618             This is an object in itself, which has 2 accessors; B<name> and B<uri>.
619              
620             =item label
621              
622             If you don't want to use the predefined types (defined by Google) you can set this label instead.
623              
624             =item value
625              
626             The phone number
627              
628             =back
629              
630              
631             Explicitly setting all phone details:
632              
633             $contact->phone_number({
634             type => "mobile",
635             value => "+449812323",
636             });
637              
638             Just setting the value will set type to default value "mobile".
639              
640             $contact->phone_number( "+1666666" );
641              
642             To specify several phone numbers, you could either;
643              
644             =over 4
645              
646             =item * provide them all in an array
647              
648             $contact->phone_number([
649             { type => "mobile", value => "12345" },
650             { type => "home", value => "666" },
651             ]);
652              
653             =item * call add_phone_number
654              
655             $contact->add_phone_number({
656             type => "home",
657             value => "02078712345"
658             });
659              
660             =back
661              
662             =head2 im (Instant Messaging)
663              
664             $contact->im is, if defined, an array reference with 1 or more IM objects.
665             The IM objects have the following accessors;
666              
667             =over 4
668              
669             =item type
670              
671             This is an object in itself, which has 2 accessors; B<name> and B<uri>.
672              
673             =item label
674              
675             If you don't want to use the predefined types (defined by Google) you can set this label instead.
676              
677             =item protocol
678              
679             This is an object in itself, which has 2 accessors; B<name> and B<uri>.
680              
681             Which protocol is used for this IM address. Possible name values include AIM, MSN, YAHOO. SKYPE, QQ, GOOGLE_TALK, ICQ, JABBER.
682              
683             =item value
684              
685             Email address for the IM account.
686              
687             =back
688              
689             You can specify all IM details:
690              
691             $contact->im({
692             type => "home",
693             protocol => "MSN",
694             value => 'some.email@example.com',
695             });
696              
697             Or you can just choose to give the IM address:
698              
699             $contact->im( 'some.email@example.com' );
700              
701             =head2 organization
702              
703             $contact->organization is, if defined, an array reference with 1 or more Organization objects.
704             The Organization objects have the following accessors;
705              
706             =over 4
707              
708             =item type
709              
710             This is an object in itself, which has 2 accessors; B<name> and B<uri>.
711              
712             =item label
713              
714             If you don't want to use the predefined types (defined by Google) you can set this label instead.
715              
716             =item department
717              
718             Specifies a department within the organization.
719              
720             =item job_description
721              
722             Description of a job within the organization.
723              
724             =item name
725              
726             The name of the organization.
727              
728             =item symbol
729              
730             Symbol of the organization.
731              
732             =item title
733              
734             The title of a person within the organization.
735              
736             =item primary
737              
738             Boolean. When multiple organizations extensions appear in a contact kind, indicates which is primary. At most one organization may be primary.
739              
740             =item where
741              
742             A place associated with the organization, e.g. office location.
743              
744             =back
745              
746             =head2 postal_address
747              
748             $contact->postal_address is, if defined, an array reference with 1 or more PostalAddress objects.
749             The PostalAddress objects have the following accessors;
750              
751             =over 4
752              
753             =item type
754              
755             This is an object in itself, which has 2 accessors; B<name> and B<uri>.
756              
757             =item label
758              
759             If you don't want to use the predefined types (defined by Google) you can set this label instead.
760              
761             =item mail_class
762              
763             This is an object in itself, which has 2 accessors; B<name> and B<uri>.
764              
765             Classes of mail accepted at the address. Possible name values are I<both>, I<letters>, I<parcels> and I<neither>. Unless specified I<both> is assumed.
766              
767             =item usage
768              
769             This is an object in itself, which has 2 accessors; B<name> and B<uri>
770              
771             The context in which this addess can be used. Possible values are I<general> and I<local>. Local addresses may differ in layout from general addresses, and frequently use local script (as opposed to Latin script) as well, though local script is allowed in general addresses. Unless specified general usage is assumed.
772              
773             =item primary
774              
775             Boolean. Specifies the address as primary.
776              
777             =item agent
778              
779             The agent who actually receives the mail. Used in work addresses. Also for 'in care of' or 'c/o'.
780              
781             =item house_name
782              
783             Used in places where houses or buildings have names (and not necessarily numbers), eg. "The Pillars".
784              
785             =item street
786              
787             Can be street, avenue, road, etc. This element also includes the house number and room/apartment/flat/floor number.
788              
789             =item pobox
790              
791             Covers actual P.O. boxes, drawers, locked bags, etc. This is usually but not always mutually exclusive with street.
792              
793             =item neighborhood
794              
795             This is used to disambiguate a street address when a city contains more than one street with the same name, or to specify a small place whose mail is routed through a larger postal town. In China it could be a county or a minor city.
796              
797             =item city
798              
799             Can be city, village, town, borough, etc. This is the postal town and not necessarily the place of residence or place of business.
800              
801             =item subregion
802              
803             Handles administrative districts such as U.S. or U.K. counties that are not used for mail addressing purposes. Subregion is not intended for delivery addresses.
804              
805             =item region
806              
807             A state, province, county (in Ireland), Land (in Germany), departement (in France), etc.
808              
809             =item postcode
810              
811             Postal code. Usually country-wide, but sometimes specific to the city (e.g. "2" in "Dublin 2, Ireland" addresses).
812              
813             =item country
814              
815             An object with two accessors; B<name> and B<code>.
816              
817             =item formatted
818              
819             The full, unstructured postal address.
820              
821             =back
822              
823             =head2 billing_information
824              
825             Specifies billing information of the entity represented by the contact.
826              
827             =head2 notes
828              
829             Arbitrary notes about your friend.
830              
831             $contact->notes( "He's a lumberjack, but he's ok" );
832              
833             =head2 birthday
834              
835             If defined, returns an object with one accessor;
836              
837             =over 4
838              
839             =item when
840              
841             Birthday date, given in format YYYY-MM-DD (with the year), or --MM-DD (without the year).
842              
843             =back
844              
845             =head2 ...tba
846              
847             Sorry, haven't documented all attributes yet :(
848              
849             =head1 INTERACTION WITH GROUPS
850              
851             Contacts can belong to 0 or more groups. This section describes how to get and set group memberships.
852              
853             =head2 $contact->groups
854              
855             Returns an array reference of all groups, as L<WWW::Google::Contacts::Group> objects.
856              
857             =head2 $contact->add_group_membership( group )
858              
859             The I<group> argument can either be:
860              
861             =over 4
862              
863             =item An L<WWW::Google::Contacts::Group> object
864              
865             =item The ID of a group, as a URL
866              
867             =item The name of a group
868              
869             =back
870              
871             Do note that the group has to exist on the Google servers before you can add this membership.
872              
873             =head1 AUTHOR
874              
875             Magnus Erixzon <magnus@erixzon.com>
876              
877             =head1 COPYRIGHT AND LICENSE
878              
879             This software is copyright (c) 2010 by Magnus Erixzon.
880              
881             This is free software; you can redistribute it and/or modify it under
882             the same terms as perl itself.
883              
884             =cut