File Coverage

blib/lib/Syndication/ESF.pm
Criterion Covered Total %
statement 75 75 100.0
branch 17 22 77.2
condition 2 3 66.6
subroutine 12 12 100.0
pod 9 9 100.0
total 115 121 95.0


line stmt bran cond sub pod time code
1             package Syndication::ESF;
2              
3             =head1 NAME
4              
5             Syndication::ESF - Create and update ESF files
6              
7             =head1 SYNOPSIS
8              
9             use Syndication::ESF;
10              
11             my $esf = Syndication::ESF->new;
12              
13             $esf->parsefile( 'my.esf' );
14              
15             $esf->channel( title => 'My channel' );
16              
17             $esf->add_item(
18             date => time,
19             title => 'new item',
20             link => 'http://example.org/#foo'
21             );
22              
23             print "Channel: ", $esf->channel( 'title' ), "\n";
24             print "Items : ", scalar @{ $esf->{ items } }, "\n";
25              
26             my $output = $esf->as_string;
27              
28             $esf->save( 'my.esf' );
29              
30             =head1 DESCRIPTION
31              
32             This module is the basic framework for creating and maintaing Epistula Syndication
33             Format (ESF) files. More information on the format can be found at the Aquarionics
34             web site: http://www.aquarionics.com/article/name/esf
35              
36             This module tries to copy the XML::RSS module's interface. All applicable methods
37             have been copied and should respond in the same manner.
38              
39             Like in XML::RSS, channel data is accessed through the C sub, and item
40             data is accessed straight out of the items array.
41              
42             =head1 INSTALLATION
43              
44             perl Makefile.PL
45             make
46             make test
47             make install
48              
49             =cut
50              
51 3     3   184687 use strict;
  3         9  
  3         285  
52 3     3   17 use warnings;
  3         6  
  3         82  
53 3     3   14 use Carp;
  3         10  
  3         5338  
54              
55             our $VERSION = '0.13';
56              
57             # Defines the set of valid fields for a channel and its items
58             my @channel_fields = qw( title contact link );
59             my @item_fields = qw( date title link );
60              
61             =head1 METHODS
62              
63             =head2 new()
64              
65             Creates a new Syndication::ESF object. It currently does not accept any parameters.
66              
67             =cut
68              
69             sub new {
70 3     3 1 454 my $class = shift;
71 3         16 my $self = {
72             channel => {},
73             items => []
74             };
75              
76 3         12 bless $self, $class;
77              
78 3         10 return $self;
79             }
80              
81             =head2 channel(title => $title, contact => $contact, link => $link)
82              
83             Supplying no parameters will give you a reference to the channel data. Specifying
84             a field name returns the value of the field. Giving it a hash will update the channel
85             data with the supplied values.
86              
87             =cut
88              
89             sub channel {
90 29     29 1 12009 my $self = shift;
91              
92             # accessor; if there's only one arg
93 29 100       85 if ( @_ == 1 ) {
    50          
94 19         109 return $self->{ channel }->{ $_[ 0 ] };
95             }
96              
97             # mutator; if there's more than one arg
98             elsif ( @_ > 1 ) {
99 10         33 my %options = @_;
100              
101 10         27 for ( keys %options ) {
102 15         43 $self->{ channel }->{ $_ } = $options{ $_ };
103              
104             # extract email and name from contact info
105 15 100       49 if ( $_ eq 'contact' ) {
106 3         15 my @contact = split( / /, $options{ $_ }, 2 );
107 3         13 $contact[ 1 ] =~ s/[\(\)]//g;
108 3         17 $self->channel(
109             'contact_name' => $contact[ 1 ],
110             'contact_email' => $contact[ 0 ]
111             );
112             }
113             }
114             }
115              
116 10         32 return $self->{ channel };
117             }
118              
119             =head2 contact_name()
120              
121             shortcut to get the contact name
122              
123             =cut
124              
125             sub contact_name {
126 2     2 1 3 my $self = shift;
127 2         8 return $self->channel( 'contact_name' );
128             }
129              
130             =head2 contact_email()
131              
132             shortcut to get the contact email
133              
134             =cut
135              
136             sub contact_email {
137 2     2 1 4 my $self = shift;
138 2         7 return $self->channel( 'contact_email' );
139             }
140              
141             =head2 add_item(date => $date, title => $title, link => $link, mode => $mode)
142              
143             By default, this will append the new item to the end of the list. Specifying
144             C<'insert'> for the C parameter adds it to the front of the list.
145              
146             =cut
147              
148             sub add_item {
149 2     2 1 1111 my $self = shift;
150 2         7 my $options = { @_ };
151 2         5 my $mode = $options->{ mode };
152              
153             # depending on the mode, add the item to the
154             # start or end of the feed
155 2 100 66     12 if ( $mode and $mode eq 'insert' ) {
156 1         2 unshift( @{ $self->{ items } }, $options );
  1         3  
157             }
158             else {
159 1         2 push( @{ $self->{ items } }, $options );
  1         3  
160             }
161              
162 2         9 return $self->{ items };
163             }
164              
165             =head2 parse($string)
166              
167             Parse the supplied raw ESF data.
168              
169             =cut
170              
171             sub parse {
172 2     2 1 775 my $self = shift;
173 2         5 my $data = shift;
174              
175             # boolean to indicate if we're parsing the meta data or the items.
176 2         3 my $metamode = 1;
177              
178 2         50 foreach my $line ( split /(?:\015\012|\012|\015)/, $data ) {
179              
180             # skip to the next line if it's a comment
181 26 100       62 next if $line =~ /^#/;
182              
183 22         29 chomp( $line );
184              
185             # if it's a blank line, get out of meta-mode.
186 22 100       49 if ( $line eq '' ) {
187 2         3 $metamode = 0;
188 2         3 next;
189             }
190              
191 20         59 my @data = split /\t/, $line;
192              
193             # depending on what mode we're in, insert the channel, or item data.
194 20 100       41 if ( $metamode ) {
195 6         18 $self->channel( $data[ 0 ] => $data[ 1 ] );
196             }
197             else {
198 14         36 push @{ $self->{ items } },
  42         130  
199 14         14 { map { $item_fields[ $_ ] => $data[ $_ ] }
200             0 .. $#item_fields };
201             }
202             }
203             }
204              
205             =head2 parsefile($filename)
206              
207             Same as C, but takes a filename as input.
208              
209             =cut
210              
211             sub parsefile {
212 1     1 1 24 my $self = shift;
213 1         2 my $file = shift;
214              
215 1 50       62 open( my $esf, $file ) or croak "File open error ($file): $!";
216              
217 1         3 my $data = do { local $/; <$esf>; };
  1         6  
  1         45  
218              
219 1 50       16 close( $esf ) or carp( "File close error ($file): $!" );
220              
221 1         23 $self->parse( $data );
222             }
223              
224             =head2 as_string()
225              
226             Returns the current data stored in the object as a string.
227              
228             =cut
229              
230             sub as_string {
231 2     2 1 402 my $self = shift;
232              
233 2         5 my $data;
234              
235             # append channel data
236 2         14 $data .= "$_\t" . $self->channel( $_ ) . "\n" for @channel_fields;
237 2         6 $data .= "\n";
238              
239             # append item data
240 2         5 foreach my $item ( @{ $self->{ items } } ) {
  2         8  
241 9         59 $data .= $item->{ $_ } . "\t" for @item_fields;
242 9         48 $data =~ s/\t$/\n/;
243             }
244              
245 2         18 return $data;
246             }
247              
248             =head2 save($filename)
249              
250             Saves the value of C to the supplied filename.
251              
252             =cut
253              
254             sub save {
255 1     1 1 847 my $self = shift;
256 1         3 my $file = shift;
257              
258 1 50       169 open( my $esf, ">$file" ) or croak "File open error ($file): $!";
259              
260 1         3 print { $esf } $self->as_string;
  1         6  
261              
262 1 50       85 close( $esf ) or carp( "File close error ($file): $!" );
263             }
264              
265             =head1 AUTHOR
266              
267             Brian Cassidy Ebricas@cpan.orgE
268              
269             =head1 COPYRIGHT AND LICENSE
270              
271             Copyright 2003-2009 by Brian Cassidy
272              
273             This library is free software; you can redistribute it and/or modify
274             it under the same terms as Perl itself.
275              
276             =head1 SEE ALSO
277              
278             =over 4
279              
280             =item * L
281              
282             =back
283              
284             =cut
285              
286             1;