File Coverage

blib/lib/WebService/Flixster/Movie.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             # $Id: Movie.pm 7375 2012-04-10 11:49:08Z chris $
2              
3             =head1 NAME
4              
5             WebService::Flixster::Movie
6              
7             =cut
8              
9             package WebService::Flixster::Movie;
10              
11 2     2   12 use strict;
  2         4  
  2         59  
12 2     2   11 use warnings;
  2         5  
  2         73  
13              
14             our $VERSION = '0.02';
15              
16 2     2   10 use base qw(WebService::Flixster::Base);
  2         3  
  2         150  
17              
18 2     2   12 use Carp;
  2         4  
  2         166  
19             our @CARP_NOT = qw(WebService::Flixster WebService::Flixster::Base WebService::Flixster::Actor);
20              
21 2     2   17 use HTTP::Request::Common;
  2         30  
  2         114  
22              
23 2     2   10 use URI;
  2         4  
  2         42  
24              
25 2     2   1932 use WebService::Flixster::Actor::Stub;
  2         6  
  2         19  
26 2     2   1304 use WebService::Flixster::Currency;
  0            
  0            
27             use WebService::Flixster::Date;
28             use WebService::Flixster::Director;
29             use WebService::Flixster::Photo;
30             use WebService::Flixster::Poster;
31             use WebService::Flixster::Reviews;
32             use WebService::Flixster::RunningTime;
33             use WebService::Flixster::Trailer;
34             use WebService::Flixster::URL;
35              
36             __PACKAGE__->mk_accessors(qw(
37             __actors
38             __boxOffice
39             __directors
40             __photos
41             __poster
42             __products
43             __releaseDates
44             __reviews
45             __runningTime
46             __trailer
47             __urls
48             ));
49              
50             use constant {
51             PAGE_MAIN => 1,
52             PAGE_PHOTOS => 2,
53             PAGE_REVIEWS => 3,
54              
55             PAGE_LAST => 3,
56             };
57              
58              
59             =head1 METHODS
60              
61             =head2 id
62              
63             =head2 actors
64              
65             =head2 boxOffice
66              
67             =head2 dvdReleaseDate
68              
69             =head2 directors
70              
71             =head2 mpaa
72              
73             =head2 photos
74              
75             =head2 playing
76              
77             =head2 poster
78              
79             =head2 products
80              
81             =head2 reviews
82              
83             =head2 runningTime
84              
85             =head2 status
86              
87             =head2 synopsis
88              
89             =head2 tags
90              
91             =head2 theaterReleaseDate
92              
93             =head2 thumbnail
94              
95             =head2 title
96              
97             =head2 trailer
98              
99             =head2 url
100              
101             =head2 urls
102              
103             =cut
104              
105             ################################
106             #
107             # Primary properties
108             #
109             ################################
110              
111             sub _url {
112             my $self = shift;
113             my $page = shift;
114              
115             my $uri = URI->new();
116             $uri->scheme("http");
117             $uri->host($self->_domain());
118             if ($page == PAGE_MAIN) {
119             $uri->path(sprintf("/iphone/api/v1/movies/%d.json", $self->_id()));
120             $uri->query_form('limit' => 13, 'offset' => 0); # TODO: Suitable limit (and use offset?)
121             } elsif ($page == PAGE_PHOTOS) {
122             $uri->path("/iphone/api/v1/photos.json");
123             $uri->query_form('movie' => $self->_id(), 'limit' => 100, 'offset' => 0); # TODO: Suitable limit (iPhone uses 100) (and use offset?)
124             } elsif ($page == PAGE_REVIEWS) {
125             $uri->path(sprintf("/iphone/api/v1/movies/%d/reviews.json", $self->_id()));
126             $uri->query_form('type' => 'critics', 'limit' => 100, 'offset' => 0); # TODO: Type other than critics? TODO: Suitable limit (iPhone uses 20) (and use offset?)
127             }
128              
129             return $uri->as_string();
130             }
131              
132              
133             sub id {
134             my $self = shift;
135             return $self->_content(PAGE_MAIN)->{'id'};
136             }
137              
138             sub actors {
139             my $self = shift;
140             return $self->_actors();
141             }
142              
143              
144             sub boxOffice {
145             my $self = shift;
146             return $self->_boxOffice()
147             }
148              
149             sub dvdReleaseDate {
150             my $self = shift;
151             return $self->_releaseDates()->{'dvd'};
152             }
153              
154             sub directors {
155             my $self = shift;
156             return $self->_directors();
157             }
158              
159             sub mpaa {
160             my $self = shift;
161             return $self->_content(PAGE_MAIN)->{'mpaa'};
162             }
163              
164             sub photos {
165             my $self = shift;
166             return $self->_photos();
167             }
168              
169             sub playing {
170             my $self = shift;
171             return !!$self->_content(PAGE_MAIN)->{'playing'};
172             }
173              
174             sub poster {
175             my $self = shift;
176             return $self->_poster();
177             }
178              
179             sub products {
180             my $self = shift;
181             return $self->_products();
182             }
183              
184             sub reviews {
185             my $self = shift;
186             return $self->_reviews();
187             }
188              
189             sub runningTime {
190             my $self = shift;
191             return $self->_runningTime();
192             }
193              
194             sub status {
195             my $self = shift;
196             return $self->_content(PAGE_MAIN)->{'status'};
197             }
198              
199             sub synopsis {
200             my $self = shift;
201             return $self->_content(PAGE_MAIN)->{'synopsis'};
202             }
203              
204             sub tags {
205             my $self = shift;
206             return [ @{$self->_content(PAGE_MAIN)->{'tags'}} ];
207             }
208              
209             sub theaterReleaseDate {
210             my $self = shift;
211             return $self->_releaseDates()->{'theater'};
212             }
213              
214             sub thumbnail {
215             my $self = shift;
216             return $self->_content(PAGE_MAIN)->{'thumbnail'};
217             }
218              
219             sub title {
220             my $self = shift;
221             return $self->_content(PAGE_MAIN)->{'title'};
222             }
223              
224             sub trailer {
225             my $self = shift;
226             return $self->_trailer();
227             }
228              
229             sub url {
230             my $self = shift;
231             return $self->_content(PAGE_MAIN)->{'url'};
232             }
233              
234             sub urls {
235             my $self = shift;
236             return $self->_urls();
237             }
238              
239              
240             ################################
241             #
242             # Caching accessors
243             #
244             ################################
245              
246             sub _flush {
247             my $self = shift;
248              
249             $self->SUPER::_flush();
250              
251             $self->__actors(undef);
252             $self->__boxOffice(undef);
253             $self->__directors(undef);
254             $self->__photos(undef);
255             $self->__poster(undef);
256             $self->__products(undef);
257             $self->__releaseDates(undef);
258             $self->__reviews(undef);
259             $self->__runningTime(undef);
260             $self->__trailer(undef);
261             $self->__urls(undef);
262             }
263              
264             sub _actors {
265             my $self = shift;
266              
267             if (!defined $self->__actors()) {
268             $self->__actors($self->_get_actors());
269             }
270             return $self->__actors();
271             }
272              
273             sub _boxOffice {
274             my $self = shift;
275              
276             if (!defined $self->__boxOffice()) {
277             $self->__boxOffice($self->_get_boxOffice())
278             }
279             return $self->__boxOffice();
280             }
281              
282             sub _directors {
283             my $self = shift;
284              
285             if (!defined $self->__directors()) {
286             $self->__directors($self->_get_directors());
287             }
288             return $self->__directors();
289             }
290              
291             sub _photos {
292             my $self = shift;
293              
294             if (!defined $self->__photos()) {
295             $self->__photos($self->_get_photos());
296             }
297             return $self->__photos();
298             }
299              
300             sub _poster {
301             my $self = shift;
302              
303             if (!defined $self->__poster()) {
304             $self->__poster($self->_get_poster());
305             }
306             return $self->__poster();
307             }
308              
309             sub _products {
310             my $self = shift;
311              
312             if (!defined $self->__products()) {
313             $self->__products($self->_get_products());
314             }
315             return $self->__products();
316             }
317              
318             sub _releaseDates {
319             my $self = shift;
320              
321             if (!defined $self->__releaseDates()) {
322             $self->__releaseDates($self->_get_releaseDates());
323             }
324             return $self->__releaseDates();
325             }
326              
327             sub _reviews {
328             my $self = shift;
329              
330             if (!defined $self->__reviews()) {
331             $self->__reviews($self->_get_reviews());
332             }
333             return $self->__reviews();
334             }
335              
336             sub _runningTime {
337             my $self = shift;
338              
339             if (!defined $self->__runningTime()) {
340             $self->__runningTime($self->_get_runningTime());
341             }
342             return $self->__runningTime();
343             }
344              
345             sub _trailer {
346             my $self = shift;
347              
348             if (!defined $self->__trailer()) {
349             $self->__trailer($self->_get_trailer());
350             }
351             return $self->__trailer();
352             }
353              
354             sub _urls {
355             my $self = shift;
356              
357             if (!defined $self->__urls()) {
358             $self->__urls($self->_get_urls());
359             }
360             return $self->__urls();
361             }
362              
363              
364             ################################
365             #
366             # Parsing methods
367             #
368             ################################
369              
370             sub _get_id {
371             my $self = shift;
372              
373             # The constructor calls _get_id to validate the supplied id too, such that a
374             # successful return from the constructor is indicative that the id is valid, and
375             # that the resource exists. We do that by trying to use the supplied id to
376             # fetch the resource.
377              
378             if (exists $self->_q()->{'id'}) {
379             my $id = $self->_q()->{'id'};
380              
381             my $uri = URI->new();
382             $uri->scheme("http");
383             $uri->host($self->_domain());
384             $uri->path(sprintf("/iphone/api/v1/movies/%d.json", $id));
385              
386             my $content = $self->_ws()->_response_decoded_content(GET $uri->as_string());
387              
388             if ($content =~ m/^\s*$/) {
389             croak "Resource not found";
390             }
391              
392             my $json = $self->_ws()->_response_decoded_json(GET $uri->as_string());
393              
394             if ($json->{'id'} ne $id) {
395             die "id failed round trip"
396             }
397              
398             return $id;
399              
400             } elsif (exists $self->_q()->{'imdbid'}) {
401             my $imdbid = $self->_q()->{'imdbid'};
402             my ($imdbcode) = $imdbid =~ m/^(?:tt)?(\d+)$/ or die "Failed to parse '$imdbid'";
403              
404             my $uri = URI->new();
405             $uri->scheme("http");
406             $uri->host("www.rottentomatoes.com");
407             $uri->path("/alias");
408             $uri->query_form('type' => "imdbid", 's' => sprintf("%07d", $imdbcode));
409              
410             my $request = GET $uri->as_string();
411             my $response = $self->_ws()->_response($request, {'200' => 1, '404' => 1});
412              
413             if($response->code() eq "200") {
414             my $id;
415             if ( !(
416             (($id) = $response->decoded_content() =~ m/movieid=(\d+)/) || # in URL
417             (($id) = $response->decoded_content() =~ m/movieid\s*=\s*"(\d+)"/) || # tag attribute
418             (($id) = $response->decoded_content() =~ m/'?movieId'?\s*:\s*(\d+)/) # javascript
419             ) ) {
420              
421             die "Failed to extract movie id";
422              
423             }
424              
425             # Some imdbids resolve to a flixster id with no associated data. Detect these.
426             my $uri = URI->new();
427             $uri->scheme("http");
428             $uri->host($self->_domain());
429             $uri->path(sprintf("/iphone/api/v1/movies/%d.json", $id));
430              
431             my $content = $self->_ws()->_response_decoded_content(GET $uri->as_string());
432              
433             if ($content =~ m/^\s*$/) {
434             croak "Resource not found";
435             }
436              
437             return $id;
438              
439             } elsif ($response->code eq "404") {
440             croak "Resource not found";
441             } else {
442             croak "URL (", $request->uri(), ") Request Failed - Code: ", $response->code(), " Error: ", $response->message(), "\n";
443             }
444              
445             } else {
446             croak "No valid search criteria";
447             }
448              
449             }
450              
451             sub _get_actors {
452             my $self = shift;
453              
454             return [ map { WebService::Flixster::Actor::Stub->_new($self->_ws(), $_) } @{$self->_content(PAGE_MAIN)->{'actors'}} ];
455              
456             }
457              
458             sub _get_boxOffice {
459             my $self = shift;
460              
461             return WebService::Flixster::Currency->_new($self->_ws(), $self->_content(PAGE_MAIN)->{'boxOffice'}, $self->_content(PAGE_MAIN)->{'boxOfficeCurrencySymbol'});
462              
463             }
464              
465             sub _get_directors {
466             my $self = shift;
467              
468             return [ map { WebService::Flixster::Director->_new($self->_ws(), $_) } @{$self->_content(PAGE_MAIN)->{'directors'}} ];
469              
470             }
471              
472             sub _get_photos {
473             my $self = shift;
474              
475             return [ map { WebService::Flixster::Photo->_new($self->_ws(), $_) } @{$self->_content(PAGE_MAIN)->{'photos'}} ];
476              
477             }
478              
479             sub _get_poster {
480             my $self = shift;
481              
482             return WebService::Flixster::Poster->_new($self->_ws(), $self->_content(PAGE_MAIN)->{'poster'});
483              
484             }
485              
486             sub _get_products {
487             my $self = shift;
488              
489             return [ map { WebService::Flixster::URL->_new($self->_ws(), $_) } @{$self->_content(PAGE_MAIN)->{'products'}} ];;
490              
491             }
492              
493             sub _get_releaseDates {
494             my $self = shift;
495              
496             return {
497             'dvd' => WebService::Flixster::Date->_new($self->_ws(), $self->_content(PAGE_MAIN)->{'dvdReleaseDate'}),
498             'theater' => WebService::Flixster::Date->_new($self->_ws(), $self->_content(PAGE_MAIN)->{'theaterReleaseDate'}),
499             };
500              
501             }
502              
503             sub _get_reviews {
504             my $self = shift;
505              
506             return WebService::Flixster::Reviews->_new($self->_ws(), $self->_content(PAGE_MAIN)->{'reviews'}, $self->_content(PAGE_REVIEWS)->{'reviews'});
507              
508             }
509              
510             sub _get_runningTime {
511             my $self = shift;
512              
513             return WebService::Flixster::RunningTime->_new($self->_ws(), $self->_content(PAGE_MAIN)->{'runningTime'});
514              
515             }
516              
517             sub _get_trailer {
518             my $self = shift;
519              
520             return WebService::Flixster::Trailer->_new($self->_ws(), $self->_content(PAGE_MAIN)->{'trailer'});
521              
522             }
523              
524             sub _get_urls {
525             my $self = shift;
526              
527             return [ map { WebService::Flixster::URL->_new($self->_ws(), $_) } @{$self->_content(PAGE_MAIN)->{'urls'}} ];
528              
529             }
530              
531              
532             ################################
533             #
534             # Debug / dev code
535             #
536             ################################
537              
538             sub _unparsed {
539             my $self = shift;
540              
541             use Storable qw(dclone);
542             my $d = { map {$_ => dclone($self->_content($_))} (1..PAGE_LAST) };
543              
544             delete $d->{PAGE_MAIN()}->{'id'};
545             delete $d->{PAGE_MAIN()}->{'actors'};
546             delete $d->{PAGE_MAIN()}->{'boxOffice'};
547             delete $d->{PAGE_MAIN()}->{'boxOfficeCurrencySymbol'};
548             delete $d->{PAGE_MAIN()}->{'directors'};
549             delete $d->{PAGE_MAIN()}->{'dvdReleaseDate'};
550             delete $d->{PAGE_MAIN()}->{'mpaa'};
551             delete $d->{PAGE_MAIN()}->{'photos'};
552             delete $d->{PAGE_MAIN()}->{'playing'};
553             delete $d->{PAGE_MAIN()}->{'poster'};
554             delete $d->{PAGE_MAIN()}->{'products'};
555             delete $d->{PAGE_MAIN()}->{'reviews'};
556             delete $d->{PAGE_MAIN()}->{'runningTime'};
557             delete $d->{PAGE_MAIN()}->{'status'};
558             delete $d->{PAGE_MAIN()}->{'synopsis'};
559             delete $d->{PAGE_MAIN()}->{'tags'};
560             delete $d->{PAGE_MAIN()}->{'theaterReleaseDate'};
561             delete $d->{PAGE_MAIN()}->{'thumbnail'};
562             delete $d->{PAGE_MAIN()}->{'title'};
563             delete $d->{PAGE_MAIN()}->{'trailer'};
564             delete $d->{PAGE_MAIN()}->{'url'};
565             delete $d->{PAGE_MAIN()}->{'urls'};
566             delete $d->{PAGE_REVIEWS()}->{'reviews'};
567              
568             # TODO: Check that these really aren't required
569             delete $d->{PAGE_MAIN()}->{'photoCount'};
570              
571             return $d;
572             }
573              
574             1;