File Coverage

blib/lib/WWW/Lovefilm/API.pm
Criterion Covered Total %
statement 95 165 57.5
branch 21 52 40.3
condition 6 15 40.0
subroutine 19 26 73.0
pod 11 11 100.0
total 152 269 56.5


line stmt bran cond sub pod time code
1             package WWW::Lovefilm::API;
2              
3 9     9   222992 use warnings;
  9         23  
  9         287  
4 9     9   49 use strict;
  9         18  
  9         456  
5              
6             our $VERSION = '0.13';
7              
8 9     9   47 use base qw(Class::Accessor);
  9         30  
  9         8813  
9              
10 9     9   28197 use Net::OAuth;
  9         6694  
  9         306  
11 9     9   56637 use HTTP::Request::Common;
  9         287812  
  9         832  
12 9     9   10177 use LWP::UserAgent;
  9         263916  
  9         307  
13 9     9   91 use URI::Escape;
  9         17  
  9         17591  
14              
15             __PACKAGE__->mk_accessors(qw/
16             consumer_key
17             consumer_secret
18             content_filter
19             ua
20             access_token
21             access_secret
22             user_id
23             _levels
24             rest_url
25             _url
26             _params
27             privateapi
28              
29             content_ref
30             _filtered_content
31             content_error
32             /);
33              
34             sub new {
35 3     3 1 38 my $self = shift;
36 3   50     12 my $fields = shift || {};
37 3   33     43 $fields->{ua} ||= LWP::UserAgent->new();
38 3         932609 return $self->SUPER::new( $fields, @_ );
39             }
40              
41             sub content {
42 4     4 1 5881 my $self = shift;
43 4 50       17 return $self->_filtered_content if $self->_filtered_content;
44 4 100       60 return unless $self->content_ref;
45 2 100 66     23 return ${$self->content_ref} unless $self->content_filter && ref($self->content_filter);
  1         12  
46 1         12 return $self->_filtered_content(
47 1         26 &{$self->content_filter}( ${$self->content_ref}, @_ )
  1         5  
48             );
49             }
50              
51             sub original_content {
52 4     4 1 3069 my $self = shift;
53 4 100       11 return $self->content_ref ? ${$self->content_ref} : undef;
  2         22  
54             }
55              
56             sub _set_content {
57 10     10   29955 my $self = shift;
58 10         15 my $content_ref = shift;
59 10         34 $self->content_error( undef );
60 10         109 $self->_filtered_content( undef );
61 10         106 return $self->content_ref( $content_ref );
62             }
63              
64             sub REST {
65 6     6 1 7835 my $self = shift;
66 6         13 my $url = shift;
67 6         24 $self->_levels([]);
68 6         102 $self->_set_content(undef);
69 6 50       99 if( $url ){
70 0         0 my ($url, $querystring) = split '\?', $url, 2;
71 0         0 $self->_url($url);
72 0         0 $self->_params({
73             map {
74 0   0     0 my ($k,$v) = split /=/, $_, 2;
75 0 0       0 $k !~ /^oauth_/
76             ? ( $k => uri_unescape($v) )
77             : ()
78             }
79             split /&/, $querystring||''
80             });
81 0         0 return $self->url;
82             }
83 6         20 $self->_url(undef);
84 6         70 $self->_params({});
85 6         68 return WWW::Lovefilm::API::_UrlAppender->new( stack => $self->_levels, append => {users=>$self->user_id} );
86             }
87              
88             sub _base_url {
89 8     8   38 my $self = shift;
90 8 100       24 return $self->privateapi ? 'http://api.lovefilm.com' : 'http://openapi.lovefilm.com';
91             }
92              
93             sub url {
94 10     10 1 9389 my $self = shift;
95 10 100       40 return $self->_url if $self->_url;
96 8         106 my $api_url = $self->_base_url;
97              
98 8 100       97 return join '/', $api_url, @{ $self->_levels || [] };
  8         24  
99             }
100              
101             sub _submit {
102 0     0   0 my $self = shift;
103 0         0 my $method = shift;
104 0 0       0 my %options = ( %{$self->_params || {}}, @_ );
  0         0  
105 0 0       0 my $which = $self->access_token ? 'protected resource' : 'consumer';
106             my $res = $self->__OAuth_Request(
107             $which,
108             request_url => $self->url,
109             request_method => $method,
110             token => $self->access_token,
111             token_secret => $self->access_secret,
112             extra_params => \%options,
113 0 0       0 ) or do {
114 0         0 warn $self->content_error;
115 0         0 return;
116             };
117              
118 0         0 return 1;
119             }
120             sub Get {
121 0     0 1 0 my $self = shift;
122 0         0 return $self->_submit('GET', @_);
123             }
124             sub Post {
125 0     0 1 0 my $self = shift;
126 0         0 return $self->_submit('POST', @_);
127             }
128             sub Delete {
129 0     0 1 0 my $self = shift;
130 0         0 return $self->_submit('DELETE', @_);
131             }
132              
133             sub rest2sugar {
134 6     6 1 54 my $self = shift;
135 6         11 my $url = shift;
136 6         15 my @stack = ( '$lovefilm', 'REST' );
137 6         8 my @params;
138              
139 6         21 $url =~ s#^http://openapi.lovefilm.com##;
140 6         18 $url =~ s#^http://api.lovefilm.com##;
141 6         38 $url =~ s#(/users/)(\w|-){30,}/#$1#i;
142 6         38 $url =~ s#/(\d+)(?=/|$)#('$1')#;
143              
144 6 100       29 if( $url =~ s#\?(.+)## ){
145 2         7 my $querystring = $1;
146 8         67 @params = map {
147 2         15 my ($k,$v) = split /=/, $_, 2;
148 8         28 [ $k, uri_unescape($v) ]
149             }
150             split /&/, $querystring;
151             }
152 16         66 push @stack, map {
153 16         34 join '_', map { ucfirst } split '_', lc $_
  22         37  
154             }
155 6         43 grep { length($_) }
156             split '/', $url
157             ;
158             return (
159 8         41 join('->', @stack),
160             sprintf('$lovefilm->Get(%s)',
161 6         81 join( ', ', map { sprintf "'%s' => '%s'", @$_ } @params ),
162             ),
163             );
164            
165             }
166              
167             sub RequestToken {
168 0     0 1 0 my $self = shift;
169 0         0 my ($request, $response);
170              
171             $request = $self->__OAuth_Request(
172             'request token',
173             request_url => $self->_base_url . '/oauth/request_token',
174             request_method => 'POST',
175 0 0       0 ) or do {
176 0         0 warn $self->content_error;
177 0         0 return;
178             };
179 0         0 $response = Net::OAuth->response('request token')->from_post_body( $self->original_content );
180 0 0       0 my $request_token = $response->token
181             or return;
182              
183             return (
184 0         0 token => $request_token,
185             login_url => $response->extra_params->{login_url},
186             token_secret => $response->token_secret
187             );
188             }
189              
190             sub RequestAccessToken {
191 0     0 1 0 my $self = shift;
192 0         0 my (%args) = @_;
193 0         0 my ($request_token, $request_secret) = @_;
194              
195             my $request = $self->__OAuth_Request(
196             'access token',
197             request_url => $self->_base_url . '/oauth/access_token',
198             request_method => 'POST',
199             token => $args{oauth_token},
200             token_secret => $args{token_secret},
201 0 0       0 ) or do {
202 0         0 warn $self->content_error;
203 0         0 return;
204             };
205              
206 0         0 my $response = Net::OAuth->response('access token')->from_post_body( $self->original_content );
207              
208 0         0 $self->access_token( $response->token );
209 0         0 $self->access_secret( $response->token_secret );
210              
211             # Get the uses ID. See POD for this routine as to why
212             #
213 0         0 $self->REST->Users();
214 0         0 $self->Get();
215              
216 0 0       0 if ($self->content) {
217 0         0 my $href = $self->content->{link}->{href};
218 0         0 my ($user_id) = ($href =~ m!([^/]+)$!);
219 0         0 $self->user_id($user_id);
220             }
221              
222 0         0 return (access_token => $self->access_token, access_secret => $self->access_secret, user_id => $self->user_id);
223             }
224              
225              
226             sub __OAuth_Request {
227 0     0   0 my $self = shift;
228 0         0 my $request_type = shift;
229 0         0 my $params = { # Options to pass-through to Net::OAuth::*Request constructor
230             # Static:
231             consumer_key => $self->consumer_key,
232             consumer_secret => $self->consumer_secret,
233             signature_method => 'HMAC-SHA1',
234             timestamp => time,
235             nonce => join('::', $0, $$),
236             version => '1.0',
237              
238             # Defaults:
239             request_url => $self->url,
240             request_method => 'POST',
241              
242             # User overrides/additions:
243             @_
244              
245             # Most common user-provided params will be:
246             # request_url
247             # request_method
248             # token
249             # token_secret
250             # extra_params
251             };
252 0         0 $self->_set_content(undef);
253              
254 0         0 my $request = Net::OAuth->request( $request_type )->new( %$params );
255 0         0 $request->sign;
256              
257 0         0 my $url = $request->to_url;
258 0         0 $self->rest_url( "$url" );
259              
260 0         0 my $method = $params->{request_method};
261 0         0 my $req;
262 0 0       0 if( $method eq 'GET' ){
    0          
    0          
263 0         0 $req = GET $url;
264             }elsif( $method eq 'POST' ){
265 0         0 $req = POST $url;
266             }elsif( $method eq 'DELETE' ){
267 0         0 $req = HTTP::Request->new( 'DELETE', $url );
268             }else{
269 0         0 $self->content_error( "Unknown method '$method'" );
270 0         0 return;
271             }
272             # if content_filter exists and is a scalar, then use it as the filename to write to instead of content being in memory.
273 0 0 0     0 my $response = $self->ua->request( $req, ($self->content_filter && !ref($self->content_filter) ? $self->content_filter : ()) );
274              
275 0 0       0 if ( ! $response->is_success ) {
  0 0       0  
276 0         0 $self->content_error( sprintf '%s Request to "%s" failed (%s): "%s"', $method, $url, $response->status_line, $response->content );
277 0         0 return;
278             }elsif( ! length ${$response->content_ref} ){
279 0         0 $self->content_error( sprintf '%s Request to "%s" failed (%s) (__EMPTY_CONTENT__): "%s"', $method, $url, $response->status_line, $response->content );
280 0         0 return;
281             }
282 0         0 $self->_set_content( $response->content_ref );
283              
284 0         0 return $response;
285             }
286              
287             ########################################
288              
289             package WWW::Lovefilm::API::_UrlAppender;
290              
291 9     9   64 use strict;
  9         16  
  9         326  
292 9     9   68 use warnings;
  9         18  
  9         2196  
293             our $AUTOLOAD;
294              
295             sub new {
296 8     8   142 my $self = shift;
297 8         28 my $params = { @_ };
298 8   100     101 return bless { stack => $params->{stack}, append => $params->{append}||{} }, $self;
299             }
300              
301             sub AUTOLOAD {
302 22     22   4539 my $self = shift;
303 22         43 my $dir = lc $AUTOLOAD;
304 22         85 $dir =~ s/.*:://;
305 22 50       60 if( $dir ne 'destroy' ){
306 22         24 push @{ $self->{stack} }, $dir;
  22         57  
307 22 100       52 push @{ $self->{stack} }, @_ if scalar @_;
  3         7  
308 22 100       65 push @{ $self->{stack} }, $self->{append}->{$dir} if exists $self->{append}->{$dir};
  5         15  
309             }
310 22         130 return $self;
311             }
312              
313             ########################################
314              
315             1; # End of WWW::Lovefilm::API
316              
317             __END__