File Coverage

blib/lib/Object/eBay.pm
Criterion Covered Total %
statement 52 112 46.4
branch 4 42 9.5
condition 1 14 7.1
subroutine 14 21 66.6
pod 8 9 88.8
total 79 198 39.9


line stmt bran cond sub pod time code
1             package Object::eBay;
2             our $VERSION = '0.5.1';
3              
4 6     6   79362 use Class::Std; {
  6         24679  
  6         36  
5 6     6   516 use warnings;
  6         10  
  6         147  
6 6     6   28 use strict;
  6         19  
  6         197  
7 6     6   29 use Carp;
  6         10  
  6         495  
8 6     6   33 use Scalar::Util qw( blessed );
  6         20  
  6         8179  
9              
10             my $net_ebay; # holds a singleton object
11             my %details_for :ATTR;
12             my %inputs_for :ATTR( :get ); # inputs to the API call
13              
14             sub init {
15 0     0 1 0 my ($pkg, $net_ebay_object) = @_;
16 0 0       0 croak "init() requires a valid Net::eBay object"
17             if !defined $net_ebay_object;
18 0         0 $net_ebay = $net_ebay_object;
19             }
20              
21             sub BUILD {
22 1     1 0 99 my ($self, $ident, $args_ref) = @_;
23              
24 1         3 my $object_details = delete $args_ref->{object_details};
25 1   50     10 my $needs_methods = delete $args_ref->{needs_methods} || [];
26 1         8 $inputs_for{$ident} = $self->_convert_args($args_ref);
27              
28 1         3 for my $method_name (@$needs_methods) {
29 0         0 $self->_add_inputs( $self->$method_name(':meta') );
30             }
31              
32 1 50       7 $details_for{$ident} = $object_details if $object_details;
33             }
34             sub _convert_args {
35 1     1   3 my ($self, $args) = @_;
36              
37 1         2 my %new_args;
38 1         4 for my $method_name (keys %$args) {
39 1         8 my $ebay_name = $self->method_name_to_ebay_name($method_name);
40 1         6 $new_args{$ebay_name} = $args->{$method_name};
41             }
42              
43 1         5 return \%new_args;
44             }
45             sub _add_inputs {
46 0     0   0 my ($self, $meta) = @_;
47 0         0 my $ident = ident $self;
48            
49             # handle each extra input
50 0         0 for my $input ( grep { /\A[A-Z]/ } keys %$meta ) {
  0         0  
51 0         0 my $new_value = $meta->{$input};
52              
53             # handle conflicts
54             # TODO allow conflict resolution to be specified by subclasses
55 0 0       0 if ( exists $inputs_for{$ident}{$input} ) {
56 0         0 my $old_value = $inputs_for{$ident}{$input};
57 0 0       0 croak "Conflicting $input: '$old_value' and '$new_value'"
58             if $input ne 'DetailLevel';
59 0         0 $new_value = 'ReturnAll';
60             }
61              
62 0         0 $inputs_for{$ident}{$input} = $new_value;
63             }
64             }
65             sub _make_datetime {
66 0     0   0 my ($self, $iso) = @_;
67 0         0 require DateTime;
68 0         0 my ($y, $m, $d, $h, $min, $s) = split /[-T:.]/, $iso;
69 0         0 return DateTime->new(
70             year => $y,
71             month => $m,
72             day => $d,
73             hour => $h,
74             minute => $min,
75             second => $s,
76             time_zone => 'UTC',
77             );
78             }
79              
80             ##########################################################################
81             # Usage : $method_name = Object::eBay->ebay_name_to_method_name($name)
82             # Purpose : Convert eBay names into method names
83             # Returns : a method name equivalent to the given eBay name
84             # Arguments : $name - an eBay name such as (Title or SellingStatus)
85             # Throws : no exceptions
86             # Comments : none
87             # See Also : n/a
88             sub ebay_name_to_method_name {
89 54     54 1 2833 my ($pkg, $ebay_name) = @_;
90 54 50       117 return $ebay_name if !$ebay_name;
91 54         352 $ebay_name =~ s{
92             ([[:lower:]]) # lower case letter
93             ([[:upper:]]) # followed by an upper case letter
94             }{$1_\l$2}xmsg;
95 54         199 return lc $ebay_name;
96             }
97              
98            
99             #########################################################################
100             # Usage : $ebay_name = Object::eBay->method_name_to_ebay_name($name)
101             # Purpose : Convert a method name into an eBay name
102             # Returns : an ebay name equivalent to the given method name
103             # Arguments : $name - a method name such as (title or selling_status)
104             # Throws : no exceptions
105             # Comments : none
106             # See Also : n/a
107             sub method_name_to_ebay_name {
108 6     6 1 12 my ($pkg, $method_name) = @_;
109              
110 10 100       46 my $ebay_name = join('',
111 6         19 map { $_ eq 'id' ? uc : ucfirst }
112             split(/_/, $method_name)
113             );
114 6         25 return $ebay_name;
115             }
116              
117             ##########################################################################
118             # Usage : $result = Object::eBay->ask_ebay(
119             # 'GetItem',
120             # { ItemID => 123455678 }
121             # );
122             # Purpose : dispatch an API call to eBay
123             # Returns : a hashref with eBay's response
124             # Arguments : $api_call - the name of the eBay API call to make
125             # $inputs - a hashref giving input fields for the API call
126             # Throws : "Unable to process the command ..."
127             # "eBay error (...): ..."
128             # Comments : throws an error if the API call couldn't be completed or
129             # if eBay returns an error value
130             # See Also : n/a
131             sub ask_ebay {
132 0     0 1 0 my ( $class, $command, $arguments ) = @_;
133              
134 0         0 my $result = $net_ebay->submitRequest( $command, $arguments );
135 0 0       0 croak "Unable to process the command $command"
136             if !$result;
137              
138 0 0       0 if ( exists $result->{Errors} ) {
139 0         0 my $errors = $result->{Errors};
140 0         0 my $severity = $errors->{SeverityCode};
141 0 0       0 if ( $severity ne 'Warning' ) {
142 0         0 my $code = $errors->{ErrorCode};
143 0         0 my $message = $errors->{LongMessage};
144 0         0 croak "eBay error ($code): $message";
145             }
146             }
147              
148 0         0 return $result;
149             }
150              
151             #########################################################################
152             # Usage : $details = $self->get_details()
153             # Purpose : Retrieves the details for this object from eBay and caches
154             # the results for later use.
155             # Returns : A hash reference representing the details (this is very
156             # similar to the result returned by Net::eBay::submitRequest
157             # Arguments : none
158             # Throws : no exceptions
159             # Comments : none
160             # See Also : n/a
161             sub get_details {
162 0     0 1 0 my ($self) = @_;
163 0         0 my $ident = ident $self;
164              
165             # look for a cached copy
166 0         0 my $details = $details_for{$ident};
167 0 0       0 return $details if $details;
168              
169             # otherwise, ask eBay for the details
170 0         0 my $response = $self->ask_ebay(
171             $self->api_call(),
172             $self->api_inputs(),
173             );
174              
175             # and cache the response
176 0         0 return $details_for{$ident} = $response->{ $self->response_field() };
177             }
178              
179             #############################################################################
180             # Usage : __PACKAGE__->simple_attributes('Title', 'Quantity')
181             # Purpose : Define simple attributes for an Object::eBay subclass
182             # Returns : none
183             # Arguments : a list of method names to implement
184             # Throws : no exceptions
185             # Comments : none
186             # See Also : n/a
187             sub simple_attributes {
188 8     8 1 31 my ($pkg, @ebay_names) = @_;
189              
190             # install a method for each eBay name
191 8         18 for my $ebay_name (@ebay_names) {
192 6     6   108 no strict 'refs';
  6         10  
  6         1498  
193 20         209 my $method_name = $pkg->ebay_name_to_method_name($ebay_name);
194 20         135 *{ $pkg . "::$method_name" } = sub {
195 0     0   0 my ($self) = @_;
196 0         0 my $value = eval { $self->get_details->{$ebay_name} };
  0         0  
197 0 0 0     0 croak $@ if $@ && $@ =~ /\A eBay/xms;
198 0 0       0 croak "Can't find '$ebay_name' via ${pkg}::$method_name()"
199             if !defined $value;
200 0         0 return $value;
201 20         81 };
202             }
203             }
204              
205             sub complex_attributes {
206 8     8 1 16 my ($pkg, $args) = @_;
207              
208 8         41 while ( my ($ebay_name, $meta) = each %$args ) {
209 6     6   44 no strict 'refs';
  6         16  
  6         3447  
210 29         71 my $method_name = $pkg->ebay_name_to_method_name($ebay_name);
211 29         270 *{ $pkg . "::$method_name" } = sub {
212 0     0   0 my ($self, $args) = @_;
213              
214             # return meta info if requested
215 0 0 0     0 return $meta if $args && $args eq ':meta';
216              
217 0         0 my $value = eval { $self->get_details->{$ebay_name} };
  0         0  
218 0 0 0     0 croak $@ if $@ && $@ =~ /\A eBay/xms;
219 0 0 0     0 croak "Can't find '$ebay_name' via ${pkg}::$method_name()"
220             if !defined($value) and !$meta->{undefined_value_ok};
221              
222 0 0       0 return $value if blessed $value; # already inflated the value
223              
224             # inflate value into an object
225 0 0       0 if ( my $class_stub = $meta->{class} ) {
    0          
226 0         0 my $class = "Object::eBay::$class_stub";
227 0         0 $value = eval {
228 0         0 eval "require $class";
229 0         0 $class->new({ object_details => $value })
230             };
231 0 0       0 croak "Error inflating '$ebay_name': $@\n" if $@;
232 0 0       0 croak "Can't inflate '$ebay_name' via ${pkg}::$method_name()"
233             if !defined $value;
234 0         0 $self->get_details->{$ebay_name} = $value;
235 0         0 return $value;
236             }
237             elsif ( my $converter = $meta->{convert_value} ) {
238 0 0       0 croak "The value of 'convert_value' should be\n"
239             . "a code reference.\n" if ref($converter) ne 'CODE';
240 0         0 return $converter->($value);
241             }
242              
243 0         0 return $value;
244 29         170 };
245             }
246             }
247              
248 2     2 1 12 sub api_inputs { $_[0]->get_api_inputs() }
249             }
250              
251             1;
252              
253             __END__