File Coverage

blib/lib/XML/RSS/Headline.pm
Criterion Covered Total %
statement 27 116 23.2
branch 0 46 0.0
condition 0 19 0.0
subroutine 9 24 37.5
pod 13 13 100.0
total 49 218 22.4


line stmt bran cond sub pod time code
1             package XML::RSS::Headline;
2 6     6   34824 use strict;
  6         14  
  6         249  
3 6     6   32 use warnings;
  6         11  
  6         177  
4 6     6   31 use Digest::MD5 qw(md5_base64);
  6         8  
  6         703  
5 6     6   25968 use Encode qw(encode_utf8);
  6         113337  
  6         659  
6 6     6   7279 use URI;
  6         58504  
  6         290  
7 6     6   10043 use Time::HiRes;
  6         17999  
  6         39  
8 6     6   13000 use HTML::Entities qw(decode_entities);
  6         61963  
  6         1499  
9 6     6   66 use Carp qw(carp);
  6         12  
  6         413  
10              
11             # length of headline when from description
12 6     6   32 use constant DESCRIPTION_HEADLINE => 45;
  6         9  
  6         9286  
13              
14             our $VERSION = 2.32;
15              
16             sub new {
17 0     0 1   my ( $class, %args ) = @_;
18 0           my $self = bless {}, $class;
19 0           my $first_seen = $args{first_seen};
20 0   0       my $headline_as_id = $args{headline_as_id} || 0;
21 0 0         delete $args{first_seen} if exists $args{first_seen};
22 0 0         delete $args{headline_as_id} if exists $args{headline_as_id};
23              
24 0 0         if ( $args{item} ) {
25 0 0 0       unless ( ( $args{item}->{title} || $args{item}->{description} )
      0        
26             && $args{item}->{link} )
27             {
28 0           carp 'item must contain either title/link or description/link';
29 0           return;
30             }
31             }
32             else {
33 0 0 0       unless ( $args{url} && ( $args{headline} || $args{description} ) ) {
      0        
34 0           carp 'Either item, url/headline. or url/description are required';
35 0           return;
36             }
37             }
38              
39 0           $self->headline_as_id($headline_as_id);
40              
41 0           for my $method ( keys %args ) {
42 0 0         if ( $self->can($method) ) {
43 0           $self->$method( $args{$method} );
44             }
45             else {
46 0           carp "Invalid argument: '$method'";
47             }
48             }
49              
50 0 0         unless ( $self->headline ) {
51 0           carp 'Failed to set headline';
52 0           return;
53             }
54              
55 0           $self->set_first_seen($first_seen);
56 0           return $self;
57             }
58              
59             sub id {
60 0     0 1   my ($self) = @_;
61 0 0         return $self->{_rss_headline_id} if $self->headline_as_id;
62 0   0       return $self->guid || $self->url;
63             }
64              
65             sub guid {
66 0     0 1   my ( $self, $guid ) = @_;
67 0 0         $self->{guid} = $guid if $guid;
68 0           return $self->{guid};
69             }
70              
71             sub _cache_id {
72 0     0     my ($self) = @_;
73 0 0         $self->{_rss_headline_id}
74             = md5_base64( encode_utf8( $self->{safe_headline} ) )
75             if $self->{safe_headline};
76 0           return;
77             }
78              
79             sub multiline_headline {
80 0     0 1   my ($self) = @_;
81 0           my @multiline_headline = split /\n/, $self->headline;
82 0 0         return wantarray ? @multiline_headline : \@multiline_headline;
83             }
84              
85             sub item {
86 0     0 1   my ( $self, $item ) = @_;
87 0 0         return unless $item;
88 0           $self->url( $item->{link} );
89 0           $self->headline( $item->{title} );
90 0           $self->description( $item->{description} );
91 0           $self->guid( $item->{guid} );
92 0           return;
93             }
94              
95             sub set_first_seen {
96 0     0 1   my ( $self, $hires_time ) = @_;
97 0           $self->{hires_timestamp} = $hires_time;
98 0 0         $self->{hires_timestamp} = Time::HiRes::time() unless $hires_time;
99 0           return 1;
100             }
101              
102             sub first_seen {
103 0     0 1   my ($self) = @_;
104 0           return int $self->{hires_timestamp};
105             }
106              
107             sub first_seen_hires {
108 0     0 1   my ($self) = @_;
109 0           return $self->{hires_timestamp};
110             }
111              
112             sub headline {
113 0     0 1   my ( $self, $headline ) = @_;
114 0 0         if ($headline) {
115 0           $self->{headline} = decode_entities $headline;
116 0 0         if ( $self->{headline_as_id} ) {
117 0           $self->{safe_headline} = $headline;
118 0           $self->_cache_id;
119             }
120             }
121 0           return $self->{headline};
122             }
123              
124             sub url {
125 0     0 1   my ( $self, $url ) = @_;
126              
127             # clean the URL up a bit
128 0 0         $self->{url} = URI->new($url)->canonical if $url;
129 0           return $self->{url};
130             }
131              
132             sub description {
133 0     0 1   my ( $self, $description ) = @_;
134 0 0         if ($description) {
135 0           $self->{description} = decode_entities $description;
136 0 0         $self->_description_headline unless $self->headline;
137             }
138 0           return $self->{description};
139             }
140              
141             sub _description_headline {
142 0     0     my ($self) = @_;
143 0           my $punctuation = qr/[.,?!:;]+/s;
144              
145 0           my $description = $self->{description};
146 0           $description =~ s/
/\n/g; # turn br into newline
147 0           $description =~ s/<.+?>/ /g;
148              
149 0   0       my $headline = ( split $punctuation, $description )[0] || '';
150 0           $headline =~ s/^\s+//;
151 0           $headline =~ s/\s+$//;
152              
153 0           my $build_headline = '';
154 0           for my $word ( split /\s+/, $headline ) {
155 0 0         $build_headline .= ' ' if $build_headline;
156 0           $build_headline .= $word;
157 0 0         last if length $build_headline > DESCRIPTION_HEADLINE;
158             }
159              
160 0 0         return unless $build_headline;
161 0           $self->headline( $build_headline .= '...' );
162 0           return;
163             }
164              
165             sub headline_as_id {
166 0     0 1   my ( $self, $bool ) = @_;
167 0 0         if ( defined $bool ) {
168 0           $self->{headline_as_id} = $bool;
169 0           $self->_cache_id;
170             }
171 0           return $self->{headline_as_id};
172             }
173              
174             sub timestamp {
175 0     0 1   my ( $self, $timestamp ) = @_;
176 0 0         $self->{timestamp} = $timestamp if $timestamp;
177 0           return $self->{timestamp};
178             }
179              
180             1;
181              
182             __END__