File Coverage

blib/lib/WWW/Tumblr.pm
Criterion Covered Total %
statement 104 119 87.3
branch 12 30 40.0
condition 1 3 33.3
subroutine 22 24 91.6
pod 0 4 0.0
total 139 180 77.2


line stmt bran cond sub pod time code
1             package WWW::Tumblr;
2              
3 16     16   4203390 use strict;
  16         33  
  16         672  
4 16     16   78 use warnings;
  16         33  
  16         1858  
5              
6             our $VERSION = '5.4';
7              
8             =pod
9              
10             =head1 NAME
11              
12             WWW::Tumblr - Perl bindings for the Tumblr API
13              
14             =head1 VERSION
15              
16             5.4
17              
18             =head1 SYNOPSIS
19              
20             my $t = WWW::Tumblr->new(
21             consumer_key => $consumer_key,
22             secret_key => $secret_key,
23             token => $token,
24             token_secret => $token_secret,
25             );
26              
27             my $blog = $t->blog('perlapi.tumblr.com');
28              
29             print Dumper $blog->info;
30              
31             =head1 DESCRIPTION
32              
33             This module makes use of some sort of the same models as the upstream API,
34             meaning that you will have User, Blog and Tagged methods:
35              
36             my $t = WWW::Tumblr->new(
37             consumer_key => $consumer_key,
38             secret_key => $secret_key,
39             token => $token,
40             token_secret => $token_secret,
41             );
42              
43             # Once you have a WWW::Tumblr object, you can get a WWW::Tumblr::Blog object
44             # by calling the blog() method from the former object:
45              
46             my $blog = $t->blog('perlapi.tumblr.com');
47              
48             # And then just use WWW::Tumblr::Blog methods from it:
49             if ( my $post = $blog->post( type => 'text', body => 'Hell yeah, son!' ) ) {
50             say "I have published post id: " . $post->{id};
51             } else {
52             print STDERR Dumper $blog->error;
53             die "I couldn't post it :(";
54             }
55              
56             You can also work directly with a L<WWW::Tumblr::Blog> class for example:
57              
58             # You will need to set base_hostname:
59             my $blog = WWW::Tumblr::Blog->new(
60             %four_tokens,
61             base_hostname => 'myblogontumblr.com'
62             );
63              
64             All operation methods on the entire API will return false in case of an
65             upstream error and you can check the status with C<error()>:
66              
67             die Dumper $blog->error unless $blog->info();
68              
69             On success, methods will return a hash reference with the JSON representation
70             of the upstream response. This behavior has not changed from previous versions
71             of this module.
72              
73             =head1 METHOD PARAMETERS
74              
75             All methods require the same parameters as the upstream API, passed as hash
76             where the keys are the request parameters and the values the corresponding
77             data.
78              
79             =head1 DOCUMENTATION
80              
81             Please refer to each module for further tips, tricks and slightly more detailed
82             documentation:
83              
84             =over
85              
86             =item *
87              
88             L<WWW::Tumblr::Blog>
89              
90             =item *
91              
92             L<WWW::Tumblr::User>
93              
94             =item *
95              
96             L<WWW::Tumblr::Tagged>
97              
98             =item *
99              
100             L<WWW::Tumblr::ResponseError>
101              
102             =back
103              
104             Take also a look at the C<t/> directory inside the distribution. There you can see
105             how you can do a bunch of things: get posts, submissions, post quotes, text,
106             etc, etc.
107              
108             =head1 AUTHORIZATION
109              
110             It is possible to generate authorization URLs and do the whole OAuth dance. Please
111             refer to the C<examples/> directory within the distribution to learn more.
112              
113             =head1 CAVEATS
114              
115             This is considered an experimental version of the module. The request engine
116             needs a complete rewrite, as well as proper documentation. The main author of the
117             module wanted to release it like this to have people interested on Tumblr and Perl
118             give it a spin.
119              
120             =head1 BUGS
121              
122             Please report as many as you want/can. File them up at GitHub:
123             L<https://github.com/damog/www-tumblr/issues/new>. Please don't use the CPAN RT.
124              
125             =head1 MODULE AND TUMBLR API VERSION NOTE
126              
127             This module supports Tumblr API v2, starting from module version 5. Since the
128             previous API was deprecated upstream anyway, there's no backwards compatibility
129             with < 5 versions.
130              
131             =head1 AUTHOR(S)
132              
133             L<David Moreno|http://damog.net/> is the main author and maintainer of this module.
134             The following amazing people have also contributed from version 5 onwards: Artem
135             Krivopolenov, Squeeks, Fernando Vezzosi.
136              
137             =head1 SEE ALSO
138              
139             =over
140              
141             =item *
142              
143             L<Net::OAuth> because, you know, we're based off it.
144              
145             =item *
146              
147             L<Moose>, likewise.
148              
149             =back
150              
151             =head1 COPYRIGHT and LICENSE
152              
153             This software is copyright (c) 2013 by David Moreno.
154              
155             This is free software; you can redistribute it and/or modify it under
156             the same terms as the Perl 5 programming language system itself.
157              
158             =head1 DISCLAIMER
159              
160             The author is in no way affiliated to Tumblr or Yahoo! Inc. If either of them
161             want to show their appreciation for this work, they can contact the author directly
162             or donate a few of those billion dollars Yahoo! paid for Tumblr, to the Perl
163             Foundation at L<http://donate.perlfoundation.org/>.
164              
165             =cut
166              
167 16     16   9544 use Moose;
  16         9799336  
  16         133  
168 16     16   149066 use Carp;
  16         51  
  16         1840  
169 16     16   13898 use Data::Dumper;
  16         173242  
  16         1640  
170 16     16   11889 use JSON 'decode_json';
  16         285984  
  16         146  
171 16     16   6683 use Encode 'encode_utf8';
  16         106047  
  16         3141  
172 16     16   9118 use HTTP::Request::Common;
  16         502669  
  16         1938  
173 16     16   9338 use Net::OAuth::Client;
  16         1002573  
  16         237  
174 16     16   10393 use WWW::Tumblr::API;
  16         118  
  16         79  
175 16     16   29590 use WWW::Tumblr::Blog;
  16         72  
  16         930  
176 16     16   10177 use WWW::Tumblr::User;
  16         75  
  16         2865  
177 16     16   9614 use WWW::Tumblr::Authentication;
  16         86  
  16         1061  
178 16     16   155 use LWP::UserAgent;
  16         29  
  16         27858  
179              
180             has 'consumer_key', is => 'rw', isa => 'Str';
181             has 'secret_key', is => 'rw', isa => 'Str';
182             has 'token', is => 'rw', isa => 'Str';
183             has 'token_secret', is => 'rw', isa => 'Str';
184              
185             has 'callback', is => 'rw', isa => 'Str';
186             has 'error', is => 'rw', isa => 'WWW::Tumblr::ResponseError';
187             has 'ua', is => 'rw', isa => 'LWP::UserAgent', default => sub { LWP::UserAgent->new };
188              
189             has 'session_store', is => 'rw', isa => 'HashRef', default => sub { {} };
190              
191             has 'oauth', is => 'rw', isa => 'Net::OAuth::Client', default => sub {
192             my $self = shift;
193             Net::OAuth::Client->new(
194             $self->consumer_key,
195             $self->secret_key,
196             request_token_path => 'https://www.tumblr.com/oauth/request_token',
197             authorize_path => 'https://www.tumblr.com/oauth/authorize',
198             access_token_path => 'https://www.tumblr.com/oauth/access_token',
199             callback => $self->callback,
200             session => sub { if (@_ > 1) { $self->_session($_[0] => $_[1]) }; return $self->_session($_[0]) },
201             );
202             };
203              
204             sub user {
205 6     6 0 17 my ( $self ) = shift;
206 6         233 return WWW::Tumblr::User->new({
207             consumer_key => $self->consumer_key,
208             secret_key => $self->secret_key,
209             token => $self->token,
210             token_secret => $self->token_secret,
211             });
212             }
213              
214             sub blog {
215 8     8 0 28 my ( $self ) = shift;
216 8 50       47 my $name = shift or croak "A blog host name is needed.";
217              
218 8         469 return WWW::Tumblr::Blog->new({
219             consumer_key => $self->consumer_key,
220             secret_key => $self->secret_key,
221             token => $self->token,
222             token_secret => $self->token_secret,
223             base_hostname => $name,
224             });
225             }
226              
227             sub tagged {
228 1     1 0 9 my $self = shift;
229 1         5 my $args = { @_ };
230              
231 1         10 my $response = $self->_tumblr_api_request({
232             auth => 'apikey',
233             http_method => 'GET',
234             url_path => 'tagged',
235             extra_args => $args,
236             });
237              
238 1 50       994654 if ( $response->is_success ) {
239 1         20 return decode_json($response->decoded_content)->{response};
240             } else {
241 0         0 $self->error( WWW::Tumblr::ResponseError->new(
242             response => $response
243             ));
244 0         0 return;
245             }
246             }
247              
248             sub oauth_tools {
249 0     0 0 0 my ( $self ) = shift;
250 0         0 return WWW::Tumblr::Authentication::OAuth->new(
251             consumer_key => $self->consumer_key,
252             secret_key => $self->secret_key,
253             callback => $self->callback,
254             );
255             }
256              
257             sub _tumblr_api_request {
258 33     33   95 my $self = shift;
259 33         85 my $r = shift; #args
260              
261 33         95 my $method_to_call = '_' . $r->{auth} . '_request';
262             return $self->$method_to_call(
263             $r->{http_method}, $r->{url_path}, $r->{extra_args}
264 33         309 );
265             }
266              
267             sub _none_request {
268 10     10   19 my $self = shift;
269 10         22 my $method = shift;
270 10         24 my $url_path = shift;
271 10         24 my $params = shift;
272              
273 10         22 my $req;
274 10 50       45 if ( $method eq 'GET' ) {
    0          
275             # print "Requesting... " .'https://api.tumblr.com/v2/' . $url_path, "\n";
276              
277 10         50 my $request = $self->_oauth_request_sign(
278             $method, $url_path, $params
279             );
280 10         104 $req = HTTP::Request->new(
281             $method => 'https://api.tumblr.com/v2/' . $url_path,
282             [ Accept => 'application/json', Authorization => $request->to_authorization_header ]
283             );
284              
285             } elsif ( $method eq 'POST' ) {
286 0         0 Carp::croak "Unimplemented";
287             } else {
288 0         0 die "dude, wtf.";
289             }
290              
291 10         11080 my $res = $self->ua->request( $req );
292              
293 10 50       4380940 if ( my $prev = $res->previous ) {
294 10         455 return $prev;
295 0         0 } else { return $res };
296             }
297              
298             sub _apikey_request {
299 6     6   13 my $self = shift;
300 6         14 my $method = shift;
301 6         12 my $url_path = shift;
302 6         14 my $params = shift;
303              
304 6         29 my $req; # request object
305 6 50       44 if ( $method eq 'GET' ) {
    0          
306             $req = HTTP::Request->new(
307             $method => 'https://api.tumblr.com/v2/' . $url_path . '?api_key='.$self->consumer_key . '&' .
308 6         260 ( join '&', map { $_ .'='. $params->{ $_} } keys %$params )
  2         31  
309             );
310             } elsif ( $method eq 'POST' ) {
311 0         0 Carp::croak "Unimplemented";
312             } else {
313 0         0 die "$method misunderstood";
314             }
315              
316 6         49628 my $res = $self->ua->request( $req );
317              
318             }
319              
320             sub _oauth_request_sign {
321 27     27   128 my $self = shift;
322 27         127 my $method = shift;
323 27         79 my $url_path = shift;
324 27         74 my $params = shift;
325              
326 27         1245 my $request = $self->oauth->_make_request(
327             'protected resource',
328             request_method => uc $method,
329             request_url => 'https://api.tumblr.com/v2/' . $url_path,
330             consumer_key => $self->consumer_key,
331             consumer_secret => $self->secret_key,
332             token => $self->token,
333             token_secret => $self->token_secret,
334             extra_params => $params,
335             );
336 27         59441 $request->sign;
337 27         241953 return $request;
338             }
339              
340             sub _oauth_request {
341 17     17   36 my $self = shift;
342 17         40 my $method = shift;
343 17         37 my $url_path= shift;
344 17         36 my $params = shift;
345              
346 17         57 my $data = delete $params->{data};
347              
348 17         117 my $request = $self->_oauth_request_sign(
349             $method, $url_path, $params
350             );
351              
352 17         127 my $authorization_header = $request->to_authorization_header;
353              
354 17         11423 my $message;
355 17 100       137 if ( $method eq 'GET' ) {
    50          
356 8         65 $message = GET 'https://api.tumblr.com/v2/' . $url_path . '?' . $request->normalized_message_parameters, 'Authorization' => $authorization_header;
357             } elsif ( $method eq 'POST' ) {
358             # Encode string parameters to UTF-8 bytes (fixes UTF-8 posting, PR #15)
359 9         45 for my $key ( keys %$params ) {
360 14 50 33     97 next unless defined $params->{$key} && !ref($params->{$key});
361 14         90 $params->{$key} = encode_utf8($params->{$key});
362             }
363             $message = POST('https://api.tumblr.com/v2/' . $url_path,
364             Content_Type => 'form-data',
365             Authorization => $authorization_header,
366             Content => [
367 9 100       118 %$params, ( $data ? do {
368 2 50       9 if (ref($data) eq 'ARRAY') {
369 2         3 my $i = -1;
370 2         6 map { $i++; 'data[' . $i .']' => [ $_ ] } @$data
  3         6  
  3         25  
371             } else {
372 0         0 'data' => [ $data ]
373             }
374             } : () )
375             ]);
376             }
377              
378 17         65575 my $res = $self->ua->request( $message );
379              
380 17         11503125 return $res;
381             }
382              
383             sub _session {
384 0     0     my $self = shift;
385              
386 0 0         if ( ref $_[0] eq 'HASH' ) {
    0          
387 0           $self->session_store($_[0]);
388             } elsif ( @_ > 1 ) {
389 0           $self->session_store->{$_[0]} = $_[1]
390             }
391 0 0         return $_[0] ? $self->session_store->{$_[0]} : $self->session_store;
392             }
393              
394             1;