File Coverage

blib/lib/WebService/LibraryThing/ThingISBN.pm
Criterion Covered Total %
statement 76 114 66.6
branch 22 56 39.2
condition 6 18 33.3
subroutine 15 17 88.2
pod 1 1 100.0
total 120 206 58.2


line stmt bran cond sub pod time code
1             package WebService::LibraryThing::ThingISBN;
2              
3 2     2   76186 use Business::ISBN;
  2         132287  
  2         103  
4 2     2   21 use Carp qw( carp );
  2         4  
  2         93  
5 2     2   1993 use HTTP::Request;
  2         88515  
  2         77  
6 2     2   2360 use LWP::UserAgent;
  2         66868  
  2         85  
7 2     2   23 use base qw( Exporter );
  2         5  
  2         199  
8 2     2   12 use warnings;
  2         4  
  2         67  
9 2     2   12 use strict;
  2         4  
  2         3062  
10              
11             our @EXPORT_OK = qw( thing_isbn_list );
12              
13             =head1 NAME
14              
15             WebService::LibraryThing::ThingISBN - Get ISBNs for all editions of a book
16              
17             =head1 VERSION
18              
19             Version 0.503
20              
21             =cut
22              
23             our $VERSION = '0.503';
24              
25             =head1 SYNOPSIS
26              
27             This is a Perl interface to the LibraryThing social cataloging
28             website's thingISBN web service, which "takes an ISBN and returns a
29             list of ISBNs from the same 'work' (ie., other editions and
30             translations)." The web service is freely available for noncommercial
31             use, per the terms at L.
32              
33             use WebService::LibraryThing::ThingISBN qw( thing_isbn_list );
34             my @alternate_isbns = thing_isbn_list( isbn => '0060987049' );
35              
36             =head1 EXPORT
37              
38             Exports nothing by default. C can be optionally exported:
39              
40             use WebService::LibraryThing::ThingISBN qw( thing_isbn_list );
41              
42             =head1 FUNCTIONS
43              
44             =head2 C
45              
46             This function takes a single ISBN as an argument, and queries
47             LibraryThing's thingISBN web service to get a list of ISBNs
48             corresponding to other editions of the same book, based on
49             LibraryThing's work definitions.
50              
51             ISBNs can be either strings (hyphenated or unhyphenated; ISBN-10 or
52             ISBN-13) or Business::ISBN objects.
53              
54             C returns a list of unhyphenated ISBN strings. If
55             there's an error, it returns an empty list.
56              
57             Per LibraryThing's API terms of use, requests are limited to run no
58             more than once per second. (Users hitting the service over 1000 times
59             a day are required to notify LibraryThing.)
60              
61             =head1 AUTHOR
62              
63             Anirvan Chatterjee, C<< >>
64              
65             =head1 BUGS
66              
67             Please report any bugs or feature requests to
68             C, or through
69             the web interface at
70             L.
71             I will be notified, and then you'll automatically be notified of
72             progress on your bug as I make changes.
73              
74             Bug reports about the underlying thingISBN API should be sent to
75             LibraryThing. Contact them at L.
76              
77             =head1 SUPPORT
78              
79             The thingISBN API is documented at:
80              
81             =over 4
82              
83             =item * LibraryThing APIs
84            
85             L
86              
87             =item * "Introducing thingISBN"
88              
89             L
90              
91             =back
92              
93             You can find documentation for this module with the perldoc command.
94              
95             perldoc WebService::LibraryThing::ThingISBN
96              
97             You can also look for information at:
98              
99             =over 4
100              
101             =item * RT: CPAN's request tracker
102              
103             L
104              
105             =item * AnnoCPAN: Annotated CPAN documentation
106              
107             L
108              
109             =item * CPAN Ratings
110              
111             L
112              
113             =item * Search CPAN
114              
115             L
116              
117             =back
118              
119             =head1 ACKNOWLEDGEMENTS
120              
121             Thanks to LibraryThing for making thingISBN available.
122              
123             =head1 COPYRIGHT & LICENSE
124              
125             Copyright 2009 Anirvan Chatterjee.
126              
127             This program is free software; you can redistribute it and/or modify it
128             under the terms of either: the GNU General Public License as published
129             by the Free Software Foundation; or the Artistic License.
130              
131             See http://dev.perl.org/licenses/ for more information.
132              
133             Use of the LibraryThing thingISBN API is governed by the terms of use
134             listed at L.
135              
136             =cut
137              
138             sub thing_isbn_list {
139 9     9 1 9724 my $isbn = shift;
140 9         34 my @isbns = _thing_isbn_all( isbn => $isbn );
141 9         211 return @isbns;
142             }
143              
144             sub _thing_isbn_all {
145              
146 9     9   17 my ( $input_type, $input_value ) = @_;
147              
148 9         51 my %valid_input_types = ( isbn => 1, lccn => 1, oclc => 1 );
149 9 50 33     69 unless ( $input_type and $valid_input_types{$input_type} ) {
150 0 0       0 my $input_type_printable
151             = ( defined $input_type ? $input_type : '[undef]' );
152 0         0 carp
153             qq{Expected input type "isbn", "lccn", or "oclc", got "$input_type_printable"};
154 0         0 return ();
155             }
156              
157 9         16 my $input_value_clean = $input_value;
158 9 50       29 if ( $input_type eq 'isbn' ) {
    0          
    0          
159 9         30 $input_value_clean = _clean_isbn_input($input_value_clean);
160             } elsif ( $input_type eq 'lccn' ) {
161 0         0 $input_value_clean = _clean_lccn_input($input_value_clean);
162             } elsif ( $input_type eq 'oclc' ) {
163 0         0 $input_value_clean = _clean_oclc_input($input_value_clean);
164             }
165              
166 9 100 66     196 unless ( defined $input_value_clean and length $input_value_clean ) {
167 4         14 return ();
168             }
169              
170             return
171 5         21 _internal_lookup( type => $input_type,
172             value => $input_value_clean );
173             }
174              
175             # internal functions
176              
177             my $next_lookup_ok_time = 0;
178             my $have_time_hi_res;
179              
180             # http://www.librarything.com/api
181             # http://www.librarything.com/thingology/2008/02/thingisbn-adds-lccns-oclc-numbers.php
182              
183             sub _internal_lookup {
184 5     5   21 my %args = @_;
185              
186 5 100       16 unless ( defined $have_time_hi_res ) {
187 1   50 1   438 $have_time_hi_res = eval q{ use Time::HiRes; 1} || 0;
  1         2026  
  1         3678  
  1         6  
188             }
189              
190 5         17 my $type = $args{type};
191 5         10 my $value = $args{value};
192 5   33     34 my $ua = $args{ua} ||= _default_ua();
193              
194 5         10 my $arg;
195 5 50       53 if ( $type eq 'isbn' ) {
    0          
    0          
196 5         11 $arg = $value;
197             } elsif ( $type eq 'lccn' ) {
198 0         0 $arg = "lccn$value";
199             } elsif ( $type eq 'oclc' ) {
200 0         0 $arg = "ocm$value";
201             }
202              
203 5         50 my $request = HTTP::Request->new(
204             GET => "http://www.librarything.com/api/thingISBN/$arg&allids=1" );
205              
206 5 100       20244 if ( _get_time() < $next_lookup_ok_time ) {
207 4         16 _sleep_until_time($next_lookup_ok_time);
208             }
209              
210 5         70 my $result = $ua->request($request);
211              
212 5         1406603 $next_lookup_ok_time = _get_time() + 1;
213              
214 5 50       29 if ( $result->is_success ) {
215 5         76 my @isbns = $result->content =~ m|(.*?)|ig;
216 5         810 shift @isbns; # remove argument from list
217 5         249 return @isbns;
218             } else {
219 0         0 return ();
220             }
221             }
222              
223             sub _clean_isbn_input {
224 9     9   16 my $isbn_input = shift;
225              
226 9         14 my $isbn;
227 9 50 33     59 if ( !defined $isbn_input ) {
    50          
    50          
228 0         0 return;
229             } elsif (
230             ref $isbn_input
231             and eval {
232 0         0 $isbn_input->isa('Business::ISBN');
233             }
234             ) {
235 0         0 return $isbn_input->as_string( [] );
236             } elsif ( !length $isbn_input ) {
237 0         0 return;
238             } else {
239 9         84 my $isbn_object = Business::ISBN->new($isbn_input);
240 9 100       1772 if ($isbn_object) {
241 5         29 return $isbn_object->as_string( [] );
242             }
243             }
244 4         7 return;
245             }
246              
247             sub _clean_lccn_input {
248 0     0   0 my $lccn_input = shift;
249              
250 0         0 my $lccn;
251 0 0       0 if ( !defined $lccn_input ) {
    0          
252 0         0 return;
253             } elsif ( !length $lccn_input ) {
254 0         0 return;
255             } else {
256 0         0 $lccn = $lccn_input;
257 0         0 $lccn =~ s/\s+//g;
258 0 0       0 if ( $lccn !~ m/\d/ ) {
259 0         0 return;
260             } else {
261 0         0 return $lccn;
262             }
263             }
264             }
265              
266             sub _clean_oclc_input {
267 0     0   0 my $oclc_input = shift;
268              
269 0         0 my $oclc;
270 0 0       0 if ( !defined $oclc_input ) {
    0          
271 0         0 return;
272             } elsif ( !length $oclc_input ) {
273 0         0 return;
274             } else {
275 0         0 $oclc = $oclc_input;
276 0         0 $oclc =~ s/\s+//g;
277 0         0 $oclc =~ s/^ocm//;
278 0 0       0 if ( $oclc !~ m/\d/ ) {
279 0         0 return;
280             } else {
281 0         0 return $oclc;
282             }
283             }
284             }
285              
286             my $_default_ua;
287              
288             sub _default_ua {
289 5 100   5   18 unless ($_default_ua) {
290 1         11 $_default_ua = new LWP::UserAgent;
291              
292             # set ua agent string
293 1         6549 my $lwp_agent = $_default_ua->agent();
294 1         382 $_default_ua->agent(
295             "WebService::LibraryThing::ThingISBN/$VERSION ($lwp_agent)");
296             }
297 5         83 return $_default_ua;
298             }
299              
300             sub _get_time {
301 18 50   18   67 unless ( defined $have_time_hi_res ) {
302 0   0     0 $have_time_hi_res = eval q{ use Time::HiRes; 1} || 0;
303             }
304 18 50       51 if ($have_time_hi_res) {
305 18         99 return Time::HiRes::time();
306             } else {
307 0         0 return time;
308             }
309             }
310              
311             sub _sleep_until_time {
312 4     4   8 my $time_to_sleep_until = shift;
313 4 50       12 if ( _get_time >= $time_to_sleep_until ) {
314 0         0 return;
315             } else {
316 4 50       13 unless ( defined $have_time_hi_res ) {
317 0   0     0 $have_time_hi_res = eval q{ use Time::HiRes; 1} || 0;
318             }
319 4         12 my $seconds_to_sleep = $time_to_sleep_until - _get_time;
320 4 50       11 if ($have_time_hi_res) {
321 4         3991157 Time::HiRes::sleep($seconds_to_sleep);
322             } else {
323 0         0 sleep $seconds_to_sleep;
324             }
325             }
326 4         29 return;
327             }
328              
329             1; # End of WebService::LibraryThing::ThingISBN
330              
331             # Local Variables:
332             # mode: perltidy
333             # End: