File Coverage

blib/lib/Net/Google/DataAPI/Role/Service.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Net::Google::DataAPI::Role::Service;
2 5     5   321336 use Any::Moose '::Role';
  5         91263  
  5         33  
3 5     5   6174 use Carp;
  5         8  
  5         334  
4 5     5   2200 use LWP::UserAgent;
  5         118256  
  5         201  
5 5     5   47 use URI;
  5         7  
  5         114  
6 5     5   2140 use XML::Atom;
  0            
  0            
7             use XML::Atom::Entry;
8             use XML::Atom::Feed;
9             use Net::Google::DataAPI::Types;
10             use Net::Google::DataAPI::Auth::Null;
11             our $VERSION = '0.04';
12              
13             $XML::Atom::ForceUnicode = 1;
14             $XML::Atom::DefaultVersion = 1;
15              
16             has gdata_version => (
17             isa => 'Str',
18             is => 'ro',
19             required => 1,
20             default => '2.0',
21             );
22              
23             has ua => (
24             isa => 'LWP::UserAgent',
25             is => 'ro',
26             required => 1,
27             lazy_build => 1,
28             );
29              
30             has service => (
31             does => 'Net::Google::DataAPI::Role::Service',
32             is => 'ro',
33             required => 1,
34             lazy_build => 1,
35             );
36              
37             has source => (
38             isa => 'Str',
39             is => 'ro',
40             required => 1,
41             default => __PACKAGE__,
42             );
43              
44             has auth => (
45             is => 'ro',
46             does => 'Net::Google::DataAPI::Types::Auth',
47             required => 1,
48             lazy_build => 1,
49             handles => ['sign_request'],
50             coerce => 1,
51             );
52              
53             has namespaces => (
54             isa => 'HashRef[Str]',
55             is => 'ro',
56             );
57              
58             sub ns {
59             my ($self, $name) = @_;
60              
61             if ($name eq 'gd') {
62             return XML::Atom::Namespace->new('gd', 'http://schemas.google.com/g/2005')
63             }
64             $self->namespaces->{$name} or confess "Namespace '$name' is not defined!";
65             return XML::Atom::Namespace->new($name, $self->namespaces->{$name});
66             };
67              
68             sub _build_ua {
69             my $self = shift;
70             my $ua = LWP::UserAgent->new(
71             agent => $self->source,
72             requests_redirectable => [],
73             env_proxy => 1,
74             );
75             $ua->default_headers(
76             HTTP::Headers->new(
77             GData_Version => $self->gdata_version,
78             )
79             );
80             return $ua;
81             }
82              
83             sub _build_auth { Net::Google::DataAPI::Auth::Null->new }
84              
85             sub _build_service {return $_[0]}
86              
87             sub request {
88             my ($self, $args) = @_;
89             my $req = $self->prepare_request($args);
90             my $uri = $req->uri;
91             my $res = eval {$self->ua->request($req)};
92             if ($ENV{GOOGLE_DATAAPI_DEBUG} && $res) {
93             warn $res->request ? $res->request->as_string : $req->as_string;
94             warn $res->as_string;
95             }
96             if ($@ || $res->is_error) {
97             confess sprintf(
98             "request for '%s' failed:\n\t%s\n\t%s\n\t",
99             $uri,
100             ($res ? $res->status_line : $@),
101             ($res ? $res->content : $!),
102             );
103             }
104             if (my $res_obj = $args->{response_object}) {
105             my $type = $res->content_type;
106             if ($res->content_length && $type !~ m{^application/atom\+xml}) {
107             confess sprintf(
108             "Content-Type of response for '%s' is not 'application/atom+xml': %s",
109             $uri,
110             $type
111             );
112             }
113             my $obj = eval {$res_obj->new(\($res->content))};
114             confess sprintf(
115             "response for '%s' is broken: %s",
116             $uri,
117             $@
118             ) if $@;
119             return $obj;
120             }
121             return $res;
122             }
123              
124             sub prepare_request {
125             my ($self, $args) = @_;
126             if (ref($args) eq 'HTTP::Request') {
127             return $args;
128             }
129             my $method = delete $args->{method};
130             $method = $args->{content} || $args->{parts} ? 'POST' : 'GET' unless $method;
131             my $uri = URI->new($args->{uri});
132             my @existing_query = $uri->query_form;
133             $uri->query_form(
134             {
135             @existing_query,
136             %{$args->{query}}
137             }
138             ) if $args->{query};
139             my $req = HTTP::Request->new($method => "$uri");
140             if (my $parts = $args->{parts}) {
141             $req->header('Content-Type' => 'multipart/related');
142             for my $part (@$parts) {
143             ref $part eq 'HTTP::Message'
144             or confess "part argument should be a HTTP::Message object";
145             $req->add_part($part);
146             }
147             }
148             $req->content($args->{content}) if $args->{content};
149             $req->header('Content-Type' => $args->{content_type}) if $args->{content_type};
150             if ($args->{header}) {
151             while (my @pair = each %{$args->{header}}) {
152             $req->header(@pair);
153             }
154             }
155             $self->sign_request($req, $args->{sign_host});
156             return $req;
157             }
158              
159             sub get_feed {
160             my ($self, $url, $query) = @_;
161             return $self->request(
162             {
163             uri => $url,
164             query => $query,
165             response_object => 'XML::Atom::Feed',
166             }
167             );
168             }
169              
170             sub get_entry {
171             my ($self, $url) = @_;
172             return $self->request(
173             {
174             uri => $url,
175             response_object => 'XML::Atom::Entry',
176             }
177             );
178             }
179              
180             sub post {
181             my ($self, $url, $entry, $header) = @_;
182             return $self->request(
183             {
184             uri => $url,
185             content => $entry->as_xml,
186             header => $header || undef,
187             content_type => 'application/atom+xml',
188             response_object => ref $entry,
189             }
190             );
191             }
192              
193             sub put {
194             my ($self, $args) = @_;
195             return $self->request(
196             {
197             method => 'PUT',
198             uri => $args->{self}->editurl,
199             content => $args->{entry}->as_xml,
200             header => {'If-Match' => $args->{self}->etag },
201             content_type => 'application/atom+xml',
202             response_object => 'XML::Atom::Entry',
203             }
204             );
205             }
206              
207             sub delete {
208             my ($self, $args) = @_;
209             my $res = $self->request(
210             {
211             uri => $args->{self}->editurl,
212             method => 'DELETE',
213             header => {'If-Match' => $args->{self}->etag},
214             }
215             );
216             return $res;
217             }
218              
219             no Any::Moose '::Role';
220              
221             1;
222              
223             __END__
224              
225             =pod
226              
227             =head1 NAME
228              
229             Net::Google::DataAPI::Role::Service - provides base functionalities for Google Data API service
230              
231             =head1 SYNOPSIS
232              
233             package MyService;
234             use Any::Moose;
235             use Net::Google::DataAPI;
236             with 'Net::Google::DataAPI::Role::Service' => {
237             service => 'wise',
238             source => __PACKAGE__,
239             ns => {
240             foobar => 'http://example.com/schema#foobar',
241             },
242             }
243              
244             feedurl hoge => (
245             is => 'ro',
246             isa => 'Str',
247             entry_class => 'MyService::Hoge',
248             default => 'http://example.com/feed/hoge',
249             );
250              
251             1;
252              
253             =head1 DESCRIPTION
254              
255             =head1 AUTHOR
256              
257             Nobuo Danjou E<lt>danjou@soffritto.orgE<gt>
258              
259             =head1 SEE ALSO
260              
261             L<Net::Google::AuthSub>
262              
263             L<Net::Google::DataAPI>
264              
265             =cut