File Coverage

blib/lib/WebService/ISBNDB/API/Publishers.pm
Criterion Covered Total %
statement 54 85 63.5
branch 5 22 22.7
condition 1 3 33.3
subroutine 14 18 77.7
pod 7 7 100.0
total 81 135 60.0


line stmt bran cond sub pod time code
1             ###############################################################################
2             #
3             # This file copyright (c) 2006-2008 by Randy J. Ray, all rights reserved
4             #
5             # See "LICENSE" in the documentation for licensing and redistribution terms.
6             #
7             ###############################################################################
8             #
9             # $Id: Publishers.pm 49 2008-04-06 10:45:43Z $
10             #
11             # Description: This is an extension of the API class that implements
12             # the Publishers class of objects
13             #
14             # Functions: BUILD
15             # new
16             # find
17             # set_id
18             # set_categories
19             # get_categories
20             # normalize_args
21             #
22             # Libraries: Class::Std
23             # Error
24             # WebService::ISBNDB::API
25             #
26             # Global Consts: $VERSION
27             #
28             ###############################################################################
29              
30             package WebService::ISBNDB::API::Publishers;
31              
32 3     3   9117 use 5.006;
  3         7  
  3         98  
33 3     3   14 use strict;
  3         4  
  3         99  
34 3     3   15 use warnings;
  3         6  
  3         97  
35 3     3   13 no warnings 'redefine';
  3         5  
  3         109  
36 3     3   14 use vars qw($VERSION);
  3         4  
  3         131  
37 3     3   53 use base 'WebService::ISBNDB::API';
  3         6  
  3         246  
38              
39 3     3   19 use Class::Std;
  3         5  
  3         16  
40 3     3   285 use Error;
  3         6  
  3         20  
41              
42             $VERSION = "0.21";
43              
44             my %id : ATTR(:init_arg :get :default<>);
45             my %name : ATTR(:name :default<>);
46             my %location : ATTR(:name :default<>);
47             my %categories : ATTR(:init_arg :default<>);
48              
49             ###############################################################################
50             #
51             # Sub Name: new
52             #
53             # Description: Pass off to the super-class constructor, which handles
54             # the special cases for arguments.
55             #
56             ###############################################################################
57             sub new
58             {
59 3     3   664 shift->SUPER::new(@_);
60             }
61              
62             ###############################################################################
63             #
64             # Sub Name: BUILD
65             #
66             # Description: Builder for this class. See Class::Std.
67             #
68             # Arguments: NAME IN/OUT TYPE DESCRIPTION
69             # $self in ref Object
70             # $id in scalar This object's unique ID
71             # $args in hashref The set of arguments currently
72             # being considered for the
73             # constructor.
74             #
75             # Returns: Success: void
76             # Failure: throws Error::Simple
77             #
78             ###############################################################################
79             sub BUILD
80             {
81 2     2 1 112 my ($self, $id, $args) = @_;
82              
83 2         6 $self->set_type('Publishers');
84              
85 2 50 33     7 throw Error::Simple("'categories' must be a list-reference")
86             if ($args->{categories} and (ref($args->{categories}) ne 'ARRAY'));
87              
88 2         5 return;
89             }
90              
91             ###############################################################################
92             #
93             # Sub Name: copy
94             #
95             # Description: Copy the Publishers-specific attributes over from target
96             # object to caller.
97             #
98             # Arguments: NAME IN/OUT TYPE DESCRIPTION
99             # $self in ref Object
100             # $target in ref Object of the same class
101             #
102             # Globals: %id
103             # %name
104             # %location
105             # %categories
106             #
107             # Returns: Success: void
108             # Failure: throws Error::Simple
109             #
110             ###############################################################################
111             sub copy : CUMULATIVE
112             {
113 0     0 1 0 my ($self, $target) = @_;
114              
115 0 0       0 throw Error::Simple("Argument to 'copy' must be the same class as caller")
116             unless (ref($self) eq ref($target));
117              
118 0         0 my $id1 = ident $self;
119 0         0 my $id2 = ident $target;
120              
121             # Do the simple (scalar) attributes first
122 0         0 $id{$id1} = $id{$id2};
123 0         0 $name{$id1} = $name{$id2};
124 0         0 $location{$id1} = $location{$id2};
125              
126             # This must be tested and copied by value
127 0 0       0 $categories{$id1} = [ @{$categories{$id2}} ] if ref($categories{$id2});
  0         0  
128              
129 0         0 return;
130 3     3   988 }
  3         5  
  3         17  
131              
132             ###############################################################################
133             #
134             # Sub Name: set_id
135             #
136             # Description: Set the ID attribute on the object. Done manually so that
137             # we can restrict it to this package.
138             #
139             # Arguments: NAME IN/OUT TYPE DESCRIPTION
140             # $self in ref Object
141             # $id in scalar ID, taken from isbndb.com data
142             #
143             # Globals: %id
144             #
145             # Returns: $self
146             #
147             ###############################################################################
148             sub set_id : RESTRICTED
149             {
150 0     0 1 0 my ($self, $id) = @_;
151              
152 0         0 $id{ident $self} = $id;
153 0         0 $self;
154 3     3   683 }
  3         10  
  3         12  
155              
156             ###############################################################################
157             #
158             # Sub Name: set_categories
159             #
160             # Description: Set the list of Categories objects for this instance. The
161             # list will initially be a list of IDs, taken from the
162             # attributes of the XML. Only upon read-access (via
163             # get_categories) will the list be turned into real objects.
164             #
165             # Arguments: NAME IN/OUT TYPE DESCRIPTION
166             # $self in ref Object
167             # $list in ref List-reference of category data
168             #
169             # Globals: %categories
170             #
171             # Returns: Success: $self
172             # Failure: throws Error::Simple
173             #
174             ###############################################################################
175             sub set_categories
176             {
177 0     0 1 0 my ($self, $list) = @_;
178              
179 0 0       0 throw Error::Simple("Argument to 'set_categories' must be a list reference")
180             unless (ref($list) eq 'ARRAY');
181              
182             # Make a copy of the list
183 0         0 $categories{ident $self} = [ @$list ];
184              
185 0         0 $self;
186             }
187              
188             ###############################################################################
189             #
190             # Sub Name: get_categories
191             #
192             # Description: Return a list-reference of the Categories. If this is
193             # the first such request, then the category values are going
194             # to be scalars, not objects, and must be converted to
195             # objects before being returned.
196             #
197             # Arguments: NAME IN/OUT TYPE DESCRIPTION
198             # $self in ref Object
199             #
200             # Globals: %categories
201             #
202             # Returns: Success: list-reference of data
203             # Failure: throws Error::Simple
204             #
205             ###############################################################################
206             sub get_categories
207             {
208 0     0 1 0 my $self = shift;
209              
210 0         0 my $categories = $categories{ident $self};
211              
212             # If any element is not a reference, we need to transform the list
213 0 0       0 if (grep(! ref($_), @$categories))
214             {
215 0         0 my $class = $self->class_for_type('Categories');
216             # Make sure it's loaded
217 0         0 eval "require $class;";
218 0         0 my $cat_id;
219              
220 0         0 for (0 .. $#$categories)
221             {
222 0 0       0 unless (ref($cat_id = $categories->[$_]))
223             {
224 0 0       0 throw Error::Simple("No category found for ID '$cat_id'")
225             unless ref($categories->[$_] = $class->find({ id =>
226             $cat_id }));
227             }
228             }
229             }
230              
231             # Make a copy, so the real reference doesn't get altered
232 0         0 [ @$categories ];
233             }
234              
235             ###############################################################################
236             #
237             # Sub Name: find
238             #
239             # Description: Find a single record using the passed-in search criteria.
240             # Most of the work is done by the super-class: this method
241             # turns a single-argument call into a proper hashref, and/or
242             # turns user-supplied arguments into those recognized by the
243             # API.
244             #
245             # Arguments: NAME IN/OUT TYPE DESCRIPTION
246             # $self in ref Object
247             # $args in variable See text
248             #
249             # Returns: Success: result from SUPER::find
250             # Failure: throws Error::Simple
251             #
252             ###############################################################################
253             sub find
254             {
255 1     1 1 3 my ($self, $args) = @_;
256              
257             # First, see if we were passed a single scalar for an argument. If so, it
258             # needs to become the id argument
259 1 50       8 $args = { publisher_id => $args } unless (ref $args);
260              
261 1         9 $self->SUPER::find($args);
262             }
263              
264             ###############################################################################
265             #
266             # Sub Name: normalize_args
267             #
268             # Description: Normalize the contents of the $args hash reference, turning
269             # the user-visible (and user-friendlier) arguments into the
270             # arguments that the API expects.
271             #
272             # Also adds some "results" values, to tailor the returned
273             # content.
274             #
275             # Arguments: NAME IN/OUT TYPE DESCRIPTION
276             # $class in scalar Object ref or class name
277             # $args in hashref Reference to the arguments hash
278             #
279             # Returns: Success: $args (changed)
280             # Failure: throws Error::Simple
281             #
282             ###############################################################################
283             sub normalize_args
284             {
285 1     1 1 3 my ($class, $args) = @_;
286              
287 1         2 my ($key, $value, @keys, $count, $results, %seen);
288              
289             # Turn the collection of arguments into a set that the isbndb.com API can
290             # use. Each key/value pair has to become a pair of the form "indexX" and
291             # "valueX". Some keys, like author and publisher, have to be handled with
292             # more attention.
293 1         6 @keys = keys %$args;
294 1         3 $count = 0; # Used to gradually increment the "indexX" and "valueX" keys
295 1         2 foreach $key (@keys)
296             {
297             # If we see "api_key", it means that WebService::ISBNDB::API::search
298             # curried it into the arglist due to the type-level search being
299             # called as a static method.
300 1 50       4 next if $key eq 'api_key';
301 1         3 $value = $args->{$key};
302 1         3 delete $args->{$key};
303 1         2 $count++;
304              
305             # A key of "id" needs to be translated as "publisher_id"
306 1 50       5 if ($key eq 'id')
307             {
308 0         0 $args->{"index$count"} = 'publisher_id';
309 0         0 $args->{"value$count"} = $value;
310              
311 0         0 next;
312             }
313              
314             # These are the only other allowed search-key(s)
315 1 50       10 if ($key =~ /^(:?name|publisher_id)$/)
316             {
317 1         3 $args->{"index$count"} = $key;
318 1         3 $args->{"value$count"} = $value;
319              
320 1         3 next;
321             }
322              
323 0         0 throw Error::Simple("'$key' is not a valid search-key for publishers");
324             }
325              
326             # Add the "results" values that we want
327 1         4 $args->{results} = [ qw(details categories) ];
328              
329 1         9 $args;
330             }
331              
332             1;
333              
334             =pod
335              
336             =head1 NAME
337              
338             WebService::ISBNDB::API::Publishers - Data class for publisher information
339              
340             =head1 SYNOPSIS
341              
342             use WebService::ISBNDB::API::Publishers;
343              
344             $oreilly = WebService::ISBNDB::API::Publishers->
345             search({ name => 'oreilly' });
346              
347             =head1 DESCRIPTION
348              
349             The B class extends the
350             B class to add attributes specific to the data
351             B provides on publishers.
352              
353             =head1 METHODS
354              
355             The following methods are specific to this class, or overridden from the
356             super-class.
357              
358             =head2 Constructor
359              
360             The constructor for this class may take a single scalar argument in lieu of a
361             hash reference:
362              
363             =over 4
364              
365             =item new($PUBLISHER_ID|$ARGS)
366              
367             This constructs a new object and returns a referent to it. If the parameter
368             passed is a hash reference, it is handled as normal, per B
369             mechanics. If the value is a scalar, it is assumed to be the publisher's ID
370             within the system, and is looked up by that.
371              
372             If the argument is the hash-reference form, then a new object is always
373             constructed; to perform searches see the search() and find() methods. Thus,
374             the following two lines are in fact different:
375              
376             $book = WebService::ISBNDB::API::Publishers->new({ id => "oreilly" });
377              
378             $book = WebService::ISBNDB::API::Publishers->new('oreilly');
379              
380             The first creates a new object that has only the C attribute set. The
381             second returns a new object that represents the publisher with ID C,
382             with all data present.
383              
384             =back
385              
386             The class also defines:
387              
388             =over 4
389              
390             =item copy($TARGET)
391              
392             Copies the target object into the calling object. All attributes (including
393             the ID) are copied. This method is marked "CUMULATIVE" (see L),
394             and any sub-class of this class should provide their own copy() and also mark
395             it "CUMULATIVE", to ensure that all attributes at all levels are copied.
396              
397             =back
398              
399             See the copy() method in L.
400              
401             =head2 Accessors
402              
403             The following attributes are used to maintain the content of a publisher
404             object:
405              
406             =over 4
407              
408             =item id
409              
410             The unique ID within the B system for this publisher.
411              
412             =item name
413              
414             The name of the publisher.
415              
416             =item location
417              
418             The publisher's location.
419              
420             =item categories
421              
422             A list of category objects for the categories in which this publisher has
423             books.
424              
425             =back
426              
427             The following accessors are provided to manage these attributes:
428              
429             =over 4
430              
431             =item get_id
432              
433             Return the publisher ID.
434              
435             =item set_id($ID)
436              
437             Sets the publisher ID. This method is restricted to this class, and cannot be
438             called outside of it. In general, you shouldn't need to set the ID after the
439             object is created, since B is a read-only source.
440              
441             =item get_name
442              
443             Return the publisher's name.
444              
445             =item set_name($NAME)
446              
447             Set the name to the value in C<$NAME>.
448              
449             =item get_location
450              
451             Get the publisher's location.
452              
453             =item set_locataion($LOCATION)
454              
455             Set the location to the value in C<$LOCATION>.
456              
457             =item get_categories
458              
459             Return a list-reference of the categories for the publisher. Each element of
460             the list will be an instance of B.
461              
462             =item set_categories($CATEGORIES)
463              
464             Set the categories to the list-reference given in C<$CATEGORIES>. When the
465             publisher object is first created from the XML data, this list is populated
466             with the IDs of the categories. They are not converted to objects until
467             requested (via get_categories()) by the user.
468              
469             =back
470              
471             =head2 Utility Methods
472              
473             Besides the constructor and the accessors, the following methods are provided
474             for utility:
475              
476             =over 4
477              
478             =item find($ARG|$ARGS)
479              
480             This is a specialization of find() from the parent class. It allows the
481             argument passed in to be a scalar in place of the usual hash reference. If the
482             value is a scalar, it is searched for as if it were the ID. If the value is a
483             hash reference, it is passed to the super-class method.
484              
485             =item normalize_args($ARGS)
486              
487             This method maps the user-visible arguments as defined for find() and search()
488             into the actual arguments that must be passed to the service itself. In
489             addition, some arguments are added to the request to make the service return
490             extra data used for retrieving categories, location, etc. The
491             method changes C<$ARGS> in place, and also returns C<$ARGS> as the value from
492             the method.
493              
494             =back
495              
496             See the next section for an explanation of the available keys for searches.
497              
498             =head1 SEARCHING
499              
500             Both find() and search() allow the user to look up data in the B
501             database. The allowable search fields are limited to a certain set, however.
502             When either of find() or search() are called, the argument to the method
503             should be a hash reference of key/value pairs to be passed as arguments for
504             the search (the exception being that find() can accept a single string, which
505             has special meaning as detailed earlier).
506              
507             Searches in the text fields are done in a case-insensitive manner.
508              
509             The available search keys are:
510              
511             =over 4
512              
513             =item name
514              
515             The value should be a text string. The search returns publishers whose name
516             matches the string.
517              
518             =item id|publisher_id
519              
520             The value should be a text string. The search returns the publisher whose ID
521             in the system matches the value.
522              
523             =back
524              
525             Note that the names above may not be the same as the corresponding parameters
526             to the service. The names are chosen to match the related attributes as
527             closely as possible, for ease of understanding.
528              
529             =head1 EXAMPLES
530              
531             Get the record for the ID C:
532              
533             $oreilly = WebService::ISBNDB::API::Publishers->find('oreilly');
534              
535             Find all publisher records containing C:
536              
537             $ora = WebService::ISBNDB::API::Publishers->
538             search({ name => 'oreilly' });
539              
540             =head1 CAVEATS
541              
542             The data returned by this class is only as accurate as the data retrieved from
543             B.
544              
545             The list of results from calling search() is currently limited to 10 items.
546             This limit will be removed in an upcoming release, when iterators are
547             implemented.
548              
549             =head1 SEE ALSO
550              
551             L, L
552              
553             =head1 AUTHOR
554              
555             Randy J. Ray Erjray@blackperl.comE
556              
557             =head1 LICENSE
558              
559             This module and the code within are
560             released under the terms of the Artistic License 2.0
561             (http://www.opensource.org/licenses/artistic-license-2.0.php). This
562             code may be redistributed under either the Artistic License or the GNU
563             Lesser General Public License (LGPL) version 2.1
564             (http://www.opensource.org/licenses/lgpl-license.php).
565              
566             =cut