| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package WWW::CPANRatings::RSS; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 1 |  |  | 1 |  | 99320 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 27 |  | 
| 4 | 1 |  |  | 1 |  | 4 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 40 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | our $VERSION = '0.0307'; | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 1 |  |  | 1 |  | 391 | use XML::Simple; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | use LWP::UserAgent; | 
| 11 |  |  |  |  |  |  | use Storable qw/lock_retrieve lock_store/; | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | use base 'Class::Data::Accessor'; | 
| 14 |  |  |  |  |  |  | __PACKAGE__->mk_classaccessors (qw/ | 
| 15 |  |  |  |  |  |  | error | 
| 16 |  |  |  |  |  |  | ua | 
| 17 |  |  |  |  |  |  | ratings | 
| 18 |  |  |  |  |  |  | ratings_unique | 
| 19 |  |  |  |  |  |  | /); | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | sub new { | 
| 22 |  |  |  |  |  |  | my $class = shift; | 
| 23 |  |  |  |  |  |  | my %args = @_; | 
| 24 |  |  |  |  |  |  | $args{ua}{timeout} ||= 30; | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | my $self = bless {}, $class; | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | $self->ua( LWP::UserAgent->new( %{ $args{ua} || {} } ) ); | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | return $self; | 
| 31 |  |  |  |  |  |  | } | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | sub fetch { | 
| 34 |  |  |  |  |  |  | my $self = shift; | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | $self->$_(undef) | 
| 37 |  |  |  |  |  |  | for qw/error ratings/; | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | my $response = $self->ua->get('http://cpanratings.perl.org/index.rss'); | 
| 40 |  |  |  |  |  |  | unless ( $response->is_success ) { | 
| 41 |  |  |  |  |  |  | $self->error( 'Network error: ' . $response->status_line ); | 
| 42 |  |  |  |  |  |  | return; | 
| 43 |  |  |  |  |  |  | } | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | my $feed; | 
| 46 |  |  |  |  |  |  | # calling XMLin caused warnings to print out on my system.... | 
| 47 |  |  |  |  |  |  | # YES OMG!! LOOOK AT THIS!!1oneone ^_^ | 
| 48 |  |  |  |  |  |  | { | 
| 49 |  |  |  |  |  |  | local *STDERR; | 
| 50 |  |  |  |  |  |  | open STDERR, '>', \my $crap; | 
| 51 |  |  |  |  |  |  | $feed = XMLin( $response->content ); | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | my @ratings; | 
| 55 |  |  |  |  |  |  | for my $item ( @{ $feed->{item} || [] } ) { | 
| 56 |  |  |  |  |  |  | my ( $rating, $comment ) = $item->{description} | 
| 57 |  |  |  |  |  |  | =~ /Rating: \s* ([\d.]+) \s* stars \s* (.+)/sx; | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | $rating = 'N/A' | 
| 60 |  |  |  |  |  |  | unless defined $rating; | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | $comment = $item->{description} | 
| 63 |  |  |  |  |  |  | unless defined $comment; | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | push @ratings, { | 
| 66 |  |  |  |  |  |  | creator     => $item->{'dc:creator'}, | 
| 67 |  |  |  |  |  |  | link        => $item->{link}, | 
| 68 |  |  |  |  |  |  | dist        => $item->{title}, | 
| 69 |  |  |  |  |  |  | comment     => $comment, | 
| 70 |  |  |  |  |  |  | rating      => $rating, | 
| 71 |  |  |  |  |  |  | }; | 
| 72 |  |  |  |  |  |  | } | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | return $self->ratings( \@ratings ); | 
| 75 |  |  |  |  |  |  | } | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | sub fetch_unique { | 
| 78 |  |  |  |  |  |  | my ( $self, $file ) = @_; | 
| 79 |  |  |  |  |  |  |  | 
| 80 |  |  |  |  |  |  | $self->ratings_unique(undef); | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | unless ( defined $file ) { | 
| 83 |  |  |  |  |  |  | $file = 'cpan_ratings.rss.storable'; | 
| 84 |  |  |  |  |  |  | } | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | my $old_ratings_ref; | 
| 87 |  |  |  |  |  |  | eval { | 
| 88 |  |  |  |  |  |  | $old_ratings_ref = lock_retrieve( $file ); | 
| 89 |  |  |  |  |  |  | }; | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | $old_ratings_ref ||= []; | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | my $ratings_ref = $self->fetch | 
| 94 |  |  |  |  |  |  | or return; # error will be set by fetch(); | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | my %existing = map { $_->{link} => 1 } @$old_ratings_ref; | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | my @new_ratings; | 
| 99 |  |  |  |  |  |  | for ( reverse @$ratings_ref ) { | 
| 100 |  |  |  |  |  |  | next if exists $existing{ $_->{link} }; | 
| 101 |  |  |  |  |  |  | unshift @new_ratings, {%$_}; | 
| 102 |  |  |  |  |  |  | }; | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | unshift @$old_ratings_ref, @new_ratings; | 
| 105 |  |  |  |  |  |  | @$old_ratings_ref = splice @$old_ratings_ref, 0, 60; | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | eval { | 
| 108 |  |  |  |  |  |  | lock_store($old_ratings_ref, $file); | 
| 109 |  |  |  |  |  |  | }; | 
| 110 |  |  |  |  |  |  | if ( $@ ) { | 
| 111 |  |  |  |  |  |  | $self->error("Error with file [$file] [$@]"); | 
| 112 |  |  |  |  |  |  | return; | 
| 113 |  |  |  |  |  |  | } | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | return $self->ratings_unique( \@new_ratings ); | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | 1; | 
| 119 |  |  |  |  |  |  | __END__ |