File Coverage

blib/lib/Net/Google/DataAPI.pm
Criterion Covered Total %
statement 15 17 88.2
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 21 23 91.3


line stmt bran cond sub pod time code
1             package Net::Google::DataAPI;
2 9     9   894949 use 5.008001;
  9         27  
3 9     9   453 use Any::Moose;
  9         21012  
  9         46  
4 9     9   4018 use Any::Moose '::Exporter';
  9         24  
  9         29  
5 9     9   1862 use Carp;
  9         13  
  9         694  
6 9     9   5328 use Lingua::EN::Inflect::Number qw(to_PL);
  9         212037  
  9         66  
7 9     9   7174 use XML::Atom;
  0            
  0            
8             our $VERSION = '0.2802_2';
9              
10             any_moose('::Exporter')->setup_import_methods(
11             as_is => ['feedurl', 'entry_has'],
12             );
13              
14             sub feedurl {
15             my ($name, %args) = @_;
16              
17             my $class = caller;
18              
19             my $entry_class = delete $args{entry_class}
20             or confess 'entry_class not specified';
21              
22             my $can_add = delete $args{can_add};
23             $can_add = 1 unless defined $can_add;
24              
25             my $arg_builder = delete $args{arg_builder}
26             || sub {
27             my ($self, $args) = @_;
28             return $args || {};
29             };
30              
31             my $query_builder = delete $args{query_builder}
32             || sub {
33             my ($self, $args) = @_;
34             return $args || {};
35             };
36              
37             my $from_atom = delete $args{from_atom};
38             my $rel = delete $args{rel};
39             my $as_content_src = delete $args{as_content_src};
40             my $default = delete $args{default} || '';
41              
42             my $attr_name = "${name}_feedurl";
43              
44             my $class_meta = any_moose('::Meta::Class')->initialize($class);
45             $class_meta->add_attribute(
46             $attr_name => (
47             isa => 'Str',
48             is => 'ro',
49             lazy_build => 1,
50             %args,
51             )
52             );
53             $class_meta->add_method(
54             "${name}_entryclass" => sub { $entry_class }
55             );
56              
57             $class_meta->add_method(
58             "_build_$attr_name" => sub {
59             my $self = shift;
60             return $rel ?
61             [
62             map { $_->href }
63             grep { $_->rel eq $rel }
64             $self->atom->link
65             ]->[0] :
66             $as_content_src ?
67             $self->atom->content->elem->getAttribute('src') :
68             $from_atom ?
69             $from_atom->($self, $self->atom) : $default;
70             }
71             );
72             my $pl_name = to_PL($name);
73              
74             if ($can_add) {
75             $class_meta->add_method(
76             "add_$name" => sub {
77             my ($self, $args) = @_;
78             $self->$attr_name or confess "$attr_name is not set";
79             Any::Moose::load_class($entry_class);
80             $args = $arg_builder->($self, $args);
81             my %parent =
82             $self->can('sync') ?
83             ( container => $self ) : ( service => $self );
84             my $entry = $entry_class->new(
85             {
86             %parent,
87             %$args
88             }
89             )->to_atom;
90             my $atom = $self->service->post($self->$attr_name, $entry);
91             $self->sync if $self->can('sync');
92             my $e = $entry_class->new(
93             %parent,
94             atom => $atom,
95             );
96             return $e;
97             }
98             );
99             }
100             $class_meta->add_method(
101             $pl_name => sub {
102             my ($self, $cond) = @_;
103             my $feed = do {
104             if (ref($cond) eq 'XML::Atom::Feed') {
105             $cond;
106             } else {
107             $self->$attr_name or confess "$attr_name is not set";
108             Any::Moose::load_class($entry_class);
109             $self->can("${name}_feed")->($self, $cond);
110             }
111             };
112             return map {
113             $entry_class->new(
114             $self->can('sync') ?
115             ( container => $self ) : ( service => $self ),
116             atom => $_,
117             )
118             } $feed->entries;
119             }
120             );
121             $class_meta->add_method(
122             "${name}_feed" => sub {
123             my ($self, $cond) = @_;
124             $self->$attr_name or confess "$attr_name is not set";
125             $cond = $query_builder->($self, $cond);
126             return $self->service->get_feed($self->$attr_name, $cond);
127             }
128             );
129             $class_meta->add_method(
130             $name => sub {
131             my ($self, $cond) = @_;
132             return [ $self->$pl_name($cond) ]->[0];
133             }
134             );
135              
136             }
137              
138             sub entry_has {
139             my ($name, %args) = @_;
140              
141             my $class = caller;
142             my $class_meta = any_moose('::Meta::Class')->initialize($class);
143             $class_meta->does_role('Net::Google::DataAPI::Role::Entry')
144             or confess 'Net::Google::DataAPI::Role::Entry required to use entry_has';
145              
146             my $tagname = delete $args{tagname};
147             my $ns = delete $args{ns};
148              
149             my $from_atom = delete $args{from_atom};
150             my $to_atom = delete $args{to_atom};
151             my $default = delete $args{default} || '';
152             $default = $default->() if ref $default eq 'CODE';
153              
154             $class_meta->add_attribute(
155             $name => (
156             isa => 'Str',
157             is => 'ro',
158             $to_atom || $tagname ? (
159             trigger => sub {$_[0]->update }
160             ) : (),
161             $tagname || $from_atom ? (
162             lazy_build => 1,
163             ) : (),
164             %args,
165             )
166             );
167             if ($tagname) {
168             $class_meta->add_around_method_modifier(
169             to_atom => sub {
170             my ($next, $self) = @_;
171             my $entry = $next->($self);
172             my $ns_obj = $ns ? $self->ns($ns) : $entry->ns;
173             $entry->set($ns_obj, $tagname, $self->$name) if $self->$name;
174             return $entry;
175             }
176             );
177             $class_meta->add_method(
178             "_build_$name" => sub {
179             my $self = shift;
180             $self->atom or return $default;
181             my $ns_obj = $ns ? $self->ns($ns) : $self->atom->ns;
182             return $self->atom->get($ns_obj, $tagname) || $default;
183             }
184             );
185             }
186             if ($to_atom) {
187             $class_meta->add_around_method_modifier(
188             to_atom => sub {
189             my ($next, $self) = @_;
190             my $entry = $next->($self);
191             $to_atom->($self, $entry) if $self->$name;
192             return $entry;
193             }
194             );
195             }
196             if ($from_atom) {
197             $class_meta->add_method(
198             "_build_$name" => sub {
199             my $self = shift;
200             $self->atom or return $default;
201             return $from_atom->($self, $self->atom) || $default;
202             }
203             );
204             }
205             }
206              
207             __PACKAGE__->meta->make_immutable;
208             no Any::Moose;
209             no Any::Moose '::Exporter';
210              
211             1;
212             __END__
213              
214             =head1 NAME
215              
216             Net::Google::DataAPI - Base implementations for modules to negotiate with Google Data APIs
217              
218             =head1 SYNOPSIS
219              
220             package MyService;
221             use Any::Moose;
222             use Net::Google::DataAPI;
223              
224             with 'Net::Google::DataAPI::Role::Service';
225              
226             # registering xmlns
227             has '+namespaces' => (
228             default => {
229             foobar => 'http://example.com/schema#foobar',
230             },
231             );
232              
233             # registering feed url
234             feedurl myentry => (
235             entry_class => 'MyEntry',
236             # class name for the entry
237             default => 'http://example.com/myfeed',
238             );
239              
240             sub _build_auth {
241             my ($self) = @_;
242             # .. authsub login things, this is optional.
243             # see Net::Google::Spreadsheets for typical implementation
244             my $authsub = Net::Google::DataAPI::Auth::AuthSub->new(
245             service => 'wise',
246             account_type => 'HOSTED_OR_GOOGLE',
247             );
248             $authsub->login('foo.bar@gmail.com', 'p4ssw0rd');
249             return $authsub;
250             }
251              
252             1;
253              
254             package MyEntry;
255             use Any::Moose;
256             use Net::Google::DataAPI;
257             with 'Net::Google::DataAPI::Role::Entry';
258              
259             entry_has some_value => (
260             is => 'rw',
261             isa => 'Str',
262             # tagname
263             tagname => 'some_value',
264             # namespace
265             namespace => 'gd',
266             );
267              
268             1;
269              
270             =head1 DESCRIPTION
271              
272             Net::Google::DataAPI is base implementations for modules to negotiate with Google Data APIs.
273              
274             =head1 METHODS
275              
276             =head2 feedurl
277              
278             define a feed url.
279              
280             =head2 entry_has
281              
282             define a entry attribute.
283              
284             =head1 DEBUGGING
285              
286             You can set environment variable GOOGLE_DATAAPI_DEBUG=1 to see the raw requests and responses Net::Google::DataAPI sends and receives.
287              
288             =head1 AUTHOR
289              
290             Nobuo Danjou E<lt>danjou@soffritto.orgE<gt>
291              
292             =head1 TODO
293              
294             more pods.
295              
296             =head1 SEE ALSO
297              
298             L<Net::Google::AuthSub>
299              
300             L<Net::Google::DataAPI::Auth::AuthSub>
301              
302             L<Net::Google::DataAPI::Auth::ClientLogin::Multiple>
303              
304             L<Net::Google::DataAPI::Auth::OAuth>
305              
306             L<Net::Google::DataAPI::Role::Service>
307              
308             L<Net::Google::DataAPI::Role::Entry>
309              
310             =head1 LICENSE
311              
312             This library is free software; you can redistribute it and/or modify
313             it under the same terms as Perl itself.
314              
315             =cut