Load XML-Feed-0.06 into trunk. v0.06
Simon Wistow [Tue, 22 Apr 2008 18:00:32 +0000 (18:00 +0000)]
13 files changed:
Changes
MANIFEST
META.yml
Makefile.PL
README
lib/XML/Feed.pm
lib/XML/Feed/Atom.pm
lib/XML/Feed/Content.pm
lib/XML/Feed/Entry.pm
lib/XML/Feed/RSS.pm
t/00-compile.t
t/01-parse.t
t/02-create.t [new file with mode: 0644]

diff --git a/Changes b/Changes
index 8887d36..732bc33 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,7 +1,15 @@
-# $Id: Changes 1762 2005-01-01 17:35:44Z btrott $
+# $Id: Changes 1868 2005-08-09 20:42:29Z btrott $
 
 Revision history for XML::Feed
 
+0.06  2005.08.09
+    - Added Feed->convert and Entry->convert methods to allow conversion
+      between formats.
+    - Added ability to create new Feed and Entry objects, add entries, etc.
+    - Added $PREFERRED_PARSER variable to allow usage of compatible
+      RSS parsers, like XML::RSS::LibXML. Thanks to Tatsuhiko Miyagawa
+      for the patch.
+
 0.05  2005.01.01
     - Call URI::Fetch::URI_GONE() instead of URI::Fetch::FEED_GONE(). Thanks
       to Richard Clamp for the patch.
index 62905cb..3ca848a 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -23,6 +23,7 @@ META.yml
 README
 t/00-compile.t
 t/01-parse.t
+t/02-create.t
 t/samples/atom.xml
 t/samples/rss10.xml
 t/samples/rss20-no-summary.xml
index 7a29758..21b4070 100644 (file)
--- a/META.yml
+++ b/META.yml
@@ -1,7 +1,7 @@
 name: XML-Feed
-version: 0.05
+version: 0.06
 abstract: XML Syndication Feed Support
-author: Benjamin Trott <ben+cpan@stupidfool.org>
+author: Six Apart <cpan@sixapart.com>
 license: perl
 distribution_type: module
 requires:
index 1e684c0..951c7b8 100644 (file)
@@ -1,10 +1,10 @@
-# $Id: Makefile.PL 942 2004-12-31 23:01:21Z btrott $
+# $Id: Makefile.PL 1869 2005-08-10 00:02:25Z btrott $
 
 use inc::Module::Install;
 
 name('XML-Feed');
 abstract('XML Syndication Feed Support');
-author('Benjamin Trott <ben+cpan@stupidfool.org>');
+author('Six Apart <cpan@sixapart.com>');
 version_from('lib/XML/Feed.pm');
 license('perl');
 no_index(directory => 't');
diff --git a/README b/README
index f553efc..af1caa1 100644 (file)
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-$Id: README 942 2004-12-31 23:01:21Z btrott $
+$Id: README 1869 2005-08-10 00:02:25Z btrott $
 
 This is XML::Feed, an abstraction above the RSS and Atom syndication
 feed formats. It supports both parsing and autodiscovery of feeds.
@@ -34,4 +34,4 @@ Then install it:
 
     % make install
 
-Benjamin Trott / ben+cpan@stupidfool.org
+Six Apart / cpan@sixapart.com
index 634cdb0..6bb8bad 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Feed.pm 1762 2005-01-01 17:35:44Z btrott $
+# $Id: Feed.pm 1869 2005-08-10 00:02:25Z btrott $
 
 package XML::Feed;
 use strict;
@@ -6,8 +6,23 @@ use strict;
 use base qw( Class::ErrorHandler );
 use Feed::Find;
 use URI::Fetch;
+use Carp;
 
-our $VERSION = '0.05';
+our $VERSION = '0.06';
+
+sub new {
+    my $class = shift;
+    my($format) = @_;
+    $format ||= 'Atom';
+    my $format_class = 'XML::Feed::' . $format;
+    eval "use $format_class";
+    Carp::croak("Unsupported format $format: $@") if $@;
+    my $feed = bless {}, join('::', __PACKAGE__, $format);
+    $feed->init_empty or return $class->error($feed->errstr);
+    $feed;
+}
+
+sub init_empty { 1 }
 
 sub parse {
     my $class = shift;
@@ -78,17 +93,33 @@ sub find_feeds {
     @feeds;
 }
 
+sub convert {
+    my $feed = shift;
+    my($format) = @_;
+    my $new = __PACKAGE__->new($format);
+    for my $field (qw( title link description language copyright modified generator )) {
+        $new->$field($feed->$field());
+    }
+    for my $entry ($feed->entries) {
+        $new->add_entry($entry->convert($format));
+    }
+    $new;
+}
+
 sub format;
 sub title;
 sub link;
 sub description;
 sub language;
+sub author;
 sub copyright;
 sub modified;
 sub generator;
+sub add_entry;
 sub entries;
+sub as_xml;
 
-sub tagline { $_[0]->description }
+sub tagline { shift->description(@_) }
 sub items   { $_[0]->entries     }
 
 1;
@@ -142,6 +173,10 @@ I<DateTime> objects, which it then returns to the caller.
 
 =head1 USAGE
 
+=head2 XML::Feed->new($format)
+
+Creates a new empty I<XML::Feed> object using the format I<$format>.
+
 =head2 XML::Feed->parse($stream)
 
 Parses a syndication feed identified by I<$stream>. I<$stream> can be any
@@ -174,39 +209,50 @@ from that page (using I<E<lt>linkE<gt>> tags).
 
 Returns a list of feed URIs.
 
+=head2 $feed->convert($format)
+
+Converts the I<XML::Feed> object into the I<$format> format, and returns
+the new object.
+
 =head2 $feed->format
 
 Returns the format of the feed (C<Atom>, or some version of C<RSS>).
 
-=head2 $feed->title
+=head2 $feed->title([ $title ])
 
 The title of the feed/channel.
 
-=head2 $feed->link
+=head2 $feed->link([ $uri ])
 
 The permalink of the feed/channel.
 
-=head2 $feed->tagline
+=head2 $feed->tagline([ $tagline ])
 
 The description or tagline of the feed/channel.
 
-=head2 $feed->description
+=head2 $feed->description([ $description ])
 
 Alias for I<$feed-E<gt>tagline>.
 
-=head2 $feed->language
+=head2 $feed->author([ $author ])
+
+The author of the feed/channel.
+
+=head2 $feed->language([ $language ])
 
 The language of the feed.
 
-=head2 $feed->copyright
+=head2 $feed->copyright([ $copyright ])
 
 The copyright notice of the feed.
 
-=head2 $feed->modified
+=head2 $feed->modified([ $modified ])
 
 A I<DateTime> object representing the last-modified date of the feed.
 
-=head2 $feed->generator
+If present, I<$modified> should be a I<DateTime> object.
+
+=head2 $feed->generator([ $generator ])
 
 The generator of the feed.
 
@@ -215,6 +261,32 @@ The generator of the feed.
 A list of the entries/items in the feed. Returns an array containing
 I<XML::Feed::Entry> objects.
 
+=head2 $feed->add_entry($entry)
+
+Adds an entry to the feed. I<$entry> should be an I<XML::Feed::Entry>
+object in the correct format for the feed.
+
+=head2 $feed->as_xml
+
+Returns an XML representation of the feed, in the format determined by
+the current format of the I<$feed> object.
+
+=head1 PACKAGE VARIABLES
+
+=over 4
+
+=item C<$XML::Feed::RSS::PREFERRED_PARSER>
+
+If you want to use another RSS parser class than XML::RSS (default), you can
+change the class by setting C<$PREFERRED_PARSER> variable in XML::Feed::RSS
+package.
+
+    $XML::Feed::RSS::PREFERRED_PARSER = "XML::RSS::LibXML";
+
+B<Note:> this will only work for parsing feeds, not creating feeds.
+
+=back
+
 =head1 LICENSE
 
 I<XML::Feed> is free software; you may redistribute it and/or modify it
@@ -222,7 +294,7 @@ under the same terms as Perl itself.
 
 =head1 AUTHOR & COPYRIGHT
 
-Except where otherwise noted, I<XML::Feed> is Copyright 2004 Benjamin
-Trott, ben+cpan@stupidfool.org. All rights reserved.
+Except where otherwise noted, I<XML::Feed> is Copyright 2004-2005
+Six Apart, cpan@sixapart.com. All rights reserved.
 
 =cut
index 0ae1ad3..cc55d3d 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Atom.pm 942 2004-12-31 23:01:21Z btrott $
+# $Id: Atom.pm 1865 2005-08-09 20:15:31Z btrott $
 
 package XML::Feed::Atom;
 use strict;
@@ -8,6 +8,12 @@ use XML::Atom::Feed;
 use XML::Atom::Util qw( iso2dt );
 use List::Util qw( first );
 
+sub init_empty {
+    my $feed = shift;
+    $feed->{atom} = XML::Atom::Feed->new;
+    $feed;
+}
+
 sub init_string {
     my $feed = shift;
     my($str) = @_;
@@ -20,59 +26,152 @@ sub init_string {
 
 sub format { 'Atom' }
 
-sub title { $_[0]->{atom}->title }
+sub title { shift->{atom}->title(@_) }
 sub link {
-    my $l = first { $_->rel eq 'alternate' } $_[0]->{atom}->link;
-    $l ? $l->href : undef;
+    my $feed = shift;
+    if (@_) {
+        $feed->{atom}->add_link({ rel => 'alternate', href => $_[0],
+                                  type => 'text/html', });
+    } else {
+        my $l = first { $_->rel eq 'alternate' } $feed->{atom}->link;
+        $l ? $l->href : undef;
+    }
+}
+sub description { shift->{atom}->tagline(@_) }
+sub copyright   { shift->{atom}->copyright(@_) }
+sub language    { shift->{atom}->language(@_) }
+sub generator   { shift->{atom}->generator(@_) }
+
+sub author {
+    my $feed = shift;
+    if (@_ && $_[0]) {
+        my $person = XML::Atom::Person->new;
+        $person->name($_[0]);
+        $feed->{atom}->author($person);
+    } else {
+        $feed->{atom}->author ? $feed->{atom}->author->name : undef;
+    }
+}
+
+sub modified {
+    my $feed = shift;
+    if (@_) {
+        $feed->{atom}->modified($_[0]->iso8601 . 'Z');
+    } else {
+        iso2dt($feed->{atom}->modified);
+    }
 }
-sub description { $_[0]->{atom}->tagline }
-sub copyright { $_[0]->{atom}->copyright }
-sub language { $_[0]->{atom}->language }
-sub generator { $_[0]->{atom}->generator }
-sub author { $_[0]->{atom}->author ? $_[0]->{atom}->author->name : undef }
-sub modified { iso2dt($_[0]->{atom}->modified) }
 
 sub entries { 
     my @entries;
     for my $entry ($_[0]->{atom}->entries) {
-        push @entries, XML::Feed::Atom::Entry->wrap($entry);
+        push @entries, XML::Feed::Entry::Atom->wrap($entry);
     }
     @entries;
 }
 
-package XML::Feed::Atom::Entry;
+sub add_entry {
+    my $feed = shift;
+    my($entry) = @_;
+    $feed->{atom}->add_entry($entry->unwrap);
+}
+
+sub as_xml { $_[0]->{atom}->as_xml }
+
+package XML::Feed::Entry::Atom;
 use strict;
 
 use base qw( XML::Feed::Entry );
 use XML::Atom::Util qw( iso2dt );
 use XML::Feed::Content;
+use XML::Atom::Entry;
 use List::Util qw( first );
 
-sub title { $_[0]->{entry}->title }
+sub init_empty {
+    my $entry = shift;
+    $entry->{entry} = XML::Atom::Entry->new;
+    1;
+}
+
+sub title { shift->{entry}->title(@_) }
 sub link {
-    my $l = first { $_->rel eq 'alternate' } $_[0]->{entry}->link;
-    $l ? $l->href : undef;
+    my $entry = shift;
+    if (@_) {
+        $entry->{entry}->add_link({ rel => 'alternate', href => $_[0],
+                                    type => 'text/html', });
+    } else {
+        my $l = first { $_->rel eq 'alternate' } $entry->{entry}->link;
+        $l ? $l->href : undef;
+    }
 }
 
 sub summary {
-    XML::Feed::Content->wrap({ type => 'text/html',
-                               body => $_[0]->{entry}->summary });
+    my $entry = shift;
+    if (@_) {
+        $entry->{entry}->summary(ref($_[0]) eq 'XML::Feed::Content' ?
+            $_[0]->body : $_[0]);
+    } else {
+        XML::Feed::Content->wrap({ type => 'text/html',
+                                   body => $entry->{entry}->summary });
+    }
 }
 
 sub content {
-    my $c = $_[0]->{entry}->content;
-    XML::Feed::Content->wrap({ type => $c ? $c->type : undef,
-                               body => $c ? $c->body : undef });
+    my $entry = shift;
+    if (@_) {
+        my %param;
+        if (ref($_[0]) eq 'XML::Feed::Content') {
+            %param = (Body => $_[0]->body, Type => $_[0]->type || 'text/html');
+        } else {
+            %param = (Body => $_[0], Type => 'text/html');
+        }
+        $entry->{entry}->content(XML::Atom::Content->new(%param));
+    } else {
+        my $c = $entry->{entry}->content;
+        XML::Feed::Content->wrap({ type => $c ? $c->type : undef,
+                                   body => $c ? $c->body : undef });
+    }
 }
 
 sub category {
+    my $entry = shift;
     my $ns = XML::Atom::Namespace->new(dc => 'http://purl.org/dc/elements/1.1/');
-    $_[0]->{entry}->get($ns, 'subject');
+    if (@_) {
+        $entry->{entry}->set($ns, 'subject', $_[0]);
+    } else {
+        $entry->{entry}->get($ns, 'subject');
+    }
+}
+
+sub author {
+    my $entry = shift;
+    if (@_ && $_[0]) {
+        my $person = XML::Atom::Person->new;
+        $person->name($_[0]);
+        $entry->{entry}->author($person);
+    } else {
+        $entry->{entry}->author ? $entry->{entry}->author->name : undef;
+    }
+}
+
+sub id { shift->{entry}->id(@_) }
+
+sub issued {
+    my $entry = shift;
+    if (@_) {
+        $entry->{entry}->issued($_[0]->iso8601 . 'Z') if $_[0];
+    } else {
+        $entry->{entry}->issued ? iso2dt($entry->{entry}->issued) : undef;
+    }
 }
 
-sub author { $_[0]->{entry}->author ? $_[0]->{entry}->author->name : undef }
-sub id { $_[0]->{entry}->id }
-sub issued { iso2dt($_[0]->{entry}->issued) }
-sub modified { iso2dt($_[0]->{entry}->modified) }
+sub modified {
+    my $entry = shift;
+    if (@_) {
+        $entry->{entry}->modified($_[0]->iso8601 . 'Z') if $_[0];
+    } else {
+        $entry->{entry}->modified ? iso2dt($entry->{entry}->modified) : undef;
+    }
+}
 
 1;
index c1bf0c2..45d195c 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: Content.pm 937 2004-10-04 03:38:11Z btrott $
+# $Id: Content.pm 1862 2005-06-20 17:26:11Z btrott $
 
 package XML::Feed::Content;
 use strict;
@@ -10,6 +10,7 @@ sub wrap {
     my($c) = @_;
     bless { %$c }, $class;
 }
+*new = \&wrap;
 
 sub _var {
     my $content = shift;
index 5221e6a..d64a30d 100644 (file)
@@ -1,7 +1,10 @@
-# $Id: Entry.pm 942 2004-12-31 23:01:21Z btrott $
+# $Id: Entry.pm 1865 2005-08-09 20:15:31Z btrott $
 
 package XML::Feed::Entry;
 use strict;
+use base qw( Class::ErrorHandler );
+
+use Carp;
 
 sub wrap {
     my $class = shift;
@@ -9,6 +12,32 @@ sub wrap {
     bless { entry => $item }, $class;
 }
 
+sub unwrap { $_[0]->{entry} }
+
+sub new {
+    my $class = shift;
+    my($format) = @_;
+    $format ||= 'Atom';
+    my $format_class = 'XML::Feed::' . $format;
+    eval "use $format_class";
+    Carp::croak("Unsupported format $format: $@") if $@;
+    my $entry = bless {}, join('::', __PACKAGE__, $format);
+    $entry->init_empty or return $class->error($entry->errstr);
+    $entry;
+}
+
+sub init_empty { 1 }
+
+sub convert {
+    my $entry = shift;
+    my($format) = @_;
+    my $new = __PACKAGE__->new($format);
+    for my $field (qw( title link content summary category author id issued modified )) {
+        $new->$field($entry->$field());
+    }
+    $new;
+}
+
 sub title;
 sub link;
 sub content;
@@ -40,16 +69,26 @@ feed.
 
 =head1 USAGE
 
-=head2 $entry->title
+=head2 XML::Feed::Entry->new($format)
+
+Creates a new I<XML::Feed::Entry> object in the format I<$format>, which
+should be either I<RSS> or I<Atom>.
+
+=head2 $entry->convert($format)
+
+Converts the I<XML::Feed::Entry> object into the I<$format> format, and
+returns the new object.
+
+=head2 $entry->title([ $title ])
 
 The title of the entry.
 
-=head2 $entry->link
+=head2 $entry->link([ $uri ])
 
 The permalink of the entry, in most cases, except in cases where it points
 instead to an offsite URI referenced in the entry.
 
-=head2 $entry->content
+=head2 $entry->content([ $content ])
 
 Bn I<XML::Feed::Content> object representing the full entry body, or as
 much as is available in the feed.
@@ -59,7 +98,7 @@ I<http://purl.org/rss/1.0/modules/content/#encoded> and
 I<http://www.w3.org/1999/xhtml#body> elements, then fall back to a
 I<E<lt>descriptionE<gt>> element.
 
-=head2 $entry->summary
+=head2 $entry->summary([ $summary ])
 
 An I<XML::Feed::Content> object representing a short summary of the entry.
 Possibly.
@@ -72,27 +111,31 @@ or I<http://purl.org/rss/1.0/modules/content/#encoded>--we treat that as
 the summary. Otherwise, we assume that there isn't a summary, and return
 an I<XML::Feed::Content> object with an empty string in the I<body>.
 
-=head2 $entry->category
+=head2 $entry->category([ $category ])
 
 The category in which the entry was posted.
 
-=head2 $entry->author
+=head2 $entry->author([ $author ])
 
 The name or email address of the person who posted the entry.
 
-=head2 $entry->id
+=head2 $entry->id([ $id ])
 
 The unique ID of the entry.
 
-=head2 $entry->issued
+=head2 $entry->issued([ $issued ])
 
 A I<DateTime> object representing the date and time at which the entry
 was posted.
 
-=head2 $entry->modified
+If present, I<$issued> should be a I<DateTime> object.
+
+=head2 $entry->modified([ $modified ])
 
 A I<DateTime> object representing the last-modified date of the entry.
 
+If present, I<$modified> should be a I<DateTime> object.
+
 =head1 AUTHOR & COPYRIGHT
 
 Please see the I<XML::Feed> manpage for author, copyright, and license
index 3f992ef..be50930 100644 (file)
@@ -1,19 +1,27 @@
-# $Id: RSS.pm 942 2004-12-31 23:01:21Z btrott $
+# $Id: RSS.pm 1865 2005-08-09 20:15:31Z btrott $
 
 package XML::Feed::RSS;
 use strict;
 
 use base qw( XML::Feed );
-use XML::RSS;
 use DateTime::Format::Mail;
 use DateTime::Format::W3CDTF;
 
+our $PREFERRED_PARSER = "XML::RSS";
+
+sub init_empty {
+    my $feed = shift;
+    eval "use $PREFERRED_PARSER"; die $@ if $@;
+    $feed->{rss} = $PREFERRED_PARSER->new( version => '2.0' );
+    $feed;
+}
+
 sub init_string {
     my $feed = shift;
     my($str) = @_;
-    my $rss = $feed->{rss} = XML::RSS->new;
+    $feed->init_empty;
     if ($str) {
-        $rss->parse($$str);
+        $feed->{rss}->parse($$str);
     }
     $feed;
 }
@@ -21,35 +29,64 @@ sub init_string {
 sub format { 'RSS ' . $_[0]->{rss}->{'version'} }
 
 ## The following elements are the same in all versions of RSS.
-sub title { $_[0]->{rss}->channel('title') }
-sub link { $_[0]->{rss}->channel('link') }
-sub description { $_[0]->{rss}->channel('description') }
+sub title       { shift->{rss}->channel('title', @_) }
+sub link        { shift->{rss}->channel('link', @_) }
+sub description { shift->{rss}->channel('description', @_) }
 
 ## This is RSS 2.0 only--what's the equivalent in RSS 1.0?
-sub copyright { $_[0]->{rss}->channel('copyright') }
+sub copyright   { shift->{rss}->channel('copyright', @_) }
 
 ## The following all work transparently in any RSS version.
 sub language {
-    $_[0]->{rss}->channel('language') ||
-    $_[0]->{rss}->channel->{dc}{language}
+    my $feed = shift;
+    if (@_) {
+        $feed->{rss}->channel('language', $_[0]);
+        $feed->{rss}->channel->{dc}{language} = $_[0];
+    } else {
+        $feed->{rss}->channel('language') ||
+        $feed->{rss}->channel->{dc}{language};
+    }
 }
 
 sub generator {
-    $_[0]->{rss}->channel('generator') ||
-    $_[0]->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent};
+    my $feed = shift;
+    if (@_) {
+        $feed->{rss}->channel('generator', $_[0]);
+        $feed->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent} =
+            $_[0];
+    } else {
+        $feed->{rss}->channel('generator') ||
+        $feed->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent};
+    }
 }
 
 sub author {
-    $_[0]->{rss}->channel('webMaster') ||
-    $_[0]->{rss}->channel->{dc}{creator};
+    my $feed = shift;
+    if (@_) {
+        $feed->{rss}->channel('webMaster', $_[0]);
+        $feed->{rss}->channel->{dc}{creator} = $_[0];
+    } else {
+        $feed->{rss}->channel('webMaster') ||
+        $feed->{rss}->channel->{dc}{creator};
+    }
 }
 
 sub modified {
-    my $rss = $_[0]->{rss};
-    if (my $ts = $rss->channel('pubDate')) {
-        return DateTime::Format::Mail->parse_datetime($ts);
-    } elsif ($ts = $rss->channel->{dc}{date}) {
-        return DateTime::Format::W3CDTF->parse_datetime($ts);
+    my $rss = shift->{rss};
+    if (@_) {
+        $rss->channel('pubDate',
+            DateTime::Format::Mail->format_datetime($_[0]));
+        ## XML::RSS is so weird... if I set this, it will try to use
+        ## the value for the lastBuildDate, which I don't want--because
+        ## this date is formatted for an RSS 1.0 feed. So it's commented out.
+        #$rss->channel->{dc}{date} =
+        #    DateTime::Format::W3CDTF->format_datetime($_[0]);
+    } else { 
+        if (my $ts = $rss->channel('pubDate')) {
+            return DateTime::Format::Mail->parse_datetime($ts);
+        } elsif ($ts = $rss->channel->{dc}{date}) {
+            return DateTime::Format::W3CDTF->parse_datetime($ts);
+        }
     }
 }
 
@@ -57,73 +94,140 @@ sub entries {
     my $rss = $_[0]->{rss};
     my @entries;
     for my $item (@{ $rss->{items} }) {
-        push @entries, XML::Feed::RSS::Entry->wrap($item);
+        push @entries, XML::Feed::Entry::RSS->wrap($item);
     }
     @entries;
 }
 
-package XML::Feed::RSS::Entry;
+sub add_entry {
+    my $feed = shift;
+    my($entry) = @_;
+    $feed->{rss}->add_item(%{ $entry->unwrap });
+}
+
+sub as_xml { $_[0]->{rss}->as_string }
+
+package XML::Feed::Entry::RSS;
 use strict;
 
 use XML::Feed::Content;
 
 use base qw( XML::Feed::Entry );
 
-sub title { $_[0]->{entry}{title} }
-sub link { $_[0]->{entry}{link} || $_[0]->{entry}{guid} }
+sub init_empty { $_[0]->{entry} = { } }
+
+sub title {
+    my $entry = shift;
+    @_ ? $entry->{entry}{title} = $_[0] : $entry->{entry}{title};
+}
+
+sub link {
+    my $entry = shift;
+    if (@_) {
+        $entry->{entry}{link} = $_[0];
+        ## For RSS 2.0 output from XML::RSS. Sigh.
+        $entry->{entry}{permaLink} = $_[0];
+    } else {
+        $entry->{entry}{link} || $entry->{entry}{guid};
+    }
+}
 
 sub summary {
-    my $item = $_[0]->{entry};
-    ## Some RSS feeds use <description> for a summary, and some use it
-    ## for the full content. Pretty gross. We don't want to return the
-    ## full content if the caller expects a summary, so the heuristic is:
-    ## if the <entry> contains both a <description> and one of the elements
-    ## typically used for the full content, use <description> as the summary.
-    my $txt;
-    if ($item->{description} &&
-        ($item->{'http://purl.org/rss/1.0/modules/content/'}{encoded} ||
-         $item->{'http://www.w3.org/1999/xhtml'}{body})) {
-        $txt = $item->{description};
+    my $item = shift->{entry};
+    if (@_) {
+        $item->{description} = ref($_[0]) eq 'XML::Feed::Content' ?
+            $_[0]->body : $_[0];
+        ## Because of the logic below, we need to add some dummy content,
+        ## so that we'll properly recognize the description we enter as
+        ## the summary.
+        if (!$item->{'http://purl.org/rss/1.0/modules/content/'}{encoded} &&
+            !$item->{'http://www.w3.org/1999/xhtml'}{body}) {
+            $item->{'http://purl.org/rss/1.0/modules/content/'}{encoded} = ' ';
+        }
+    } else {
+        ## Some RSS feeds use <description> for a summary, and some use it
+        ## for the full content. Pretty gross. We don't want to return the
+        ## full content if the caller expects a summary, so the heuristic is:
+        ## if the <entry> contains both a <description> and one of the elements
+        ## typically used for the full content, use <description> as summary.
+        my $txt;
+        if ($item->{description} &&
+            ($item->{'http://purl.org/rss/1.0/modules/content/'}{encoded} ||
+             $item->{'http://www.w3.org/1999/xhtml'}{body})) {
+            $txt = $item->{description};
+        }
+        XML::Feed::Content->wrap({ type => 'text/plain', body => $txt });
     }
-    XML::Feed::Content->wrap({ type => 'text/plain', body => $txt });
 }
 
 sub content {
-    my $item = $_[0]->{entry};
-    my $body =
-        $_[0]->{entry}{'http://purl.org/rss/1.0/modules/content/'}{encoded} ||
-        $_[0]->{entry}{'http://www.w3.org/1999/xhtml'}{body} ||
-        $_[0]->{entry}{description};
-    XML::Feed::Content->wrap({ type => 'text/html', body => $body });
+    my $item = shift->{entry};
+    if (@_) {
+        my $c = ref($_[0]) eq 'XML::Feed::Content' ? $_[0]->body : $_[0];
+        $item->{'http://purl.org/rss/1.0/modules/content/'}{encoded} = $c;
+    } else {
+        my $body =
+            $item->{'http://purl.org/rss/1.0/modules/content/'}{encoded} ||
+            $item->{'http://www.w3.org/1999/xhtml'}{body} ||
+            $item->{description};
+        XML::Feed::Content->wrap({ type => 'text/html', body => $body });
+    }
 }
 
 sub category {
-    $_[0]->{entry}{category} || $_[0]->{entry}{dc}{subject};
+    my $item = shift->{entry};
+    if (@_) {
+        $item->{category} = $item->{dc}{subject} = $_[0];
+    } else {
+        $item->{category} || $item->{dc}{subject};
+    }
 }
 
 sub author {
-    $_[0]->{entry}{author} || $_[0]->{entry}{dc}{creator};
+    my $item = shift->{entry};
+    if (@_) {
+        $item->{author} = $item->{dc}{creator} = $_[0];
+    } else {
+        $item->{author} || $item->{dc}{creator};
+    }
 }
 
 ## XML::RSS doesn't give us access to the rdf:about for the <item>,
 ## so we have to fall back to the <link> element in RSS 1.0 feeds.
 sub id {
-    $_[0]->{entry}{guid} || $_[0]->{entry}{link};
+    my $item = shift->{entry};
+    if (@_) {
+        $item->{guid} = $_[0];
+    } else {
+        $item->{guid} || $item->{link};
+    }
 }
 
 sub issued {
-    if (my $ts = $_[0]->{entry}{pubDate}) {
-        my $parser = DateTime::Format::Mail->new;
-        $parser->loose;
-        return $parser->parse_datetime($ts);
-    } elsif ($ts = $_[0]->{entry}{dc}{date}) {
-        return DateTime::Format::W3CDTF->parse_datetime($ts);
+    my $item = shift->{entry};
+    if (@_) {
+        $item->{dc}{date} = DateTime::Format::W3CDTF->format_datetime($_[0]);
+        $item->{pubDate} = DateTime::Format::Mail->format_datetime($_[0]);
+    } else {
+        if (my $ts = $item->{pubDate}) {
+            my $parser = DateTime::Format::Mail->new;
+            $parser->loose;
+            return $parser->parse_datetime($ts);
+        } elsif ($ts = $item->{dc}{date}) {
+            return DateTime::Format::W3CDTF->parse_datetime($ts);
+        }
     }
 }
 
 sub modified {
-    if (my $ts = $_[0]->{entry}{'http://purl.org/rss/1.0/modules/dcterms/'}{modified}) {
-        return DateTime::Format::W3CDTF->parse_datetime($ts);
+    my $item = shift->{entry};
+    if (@_) {
+        $item->{'http://purl.org/rss/1.0/modules/dcterms/'}{modified} =
+            DateTime::Format::W3CDTF->format_datetime($_[0]);
+    } else {
+        if (my $ts = $item->{'http://purl.org/rss/1.0/modules/dcterms/'}{modified}) {
+            return DateTime::Format::W3CDTF->parse_datetime($ts);
+        }
     }
 }
 
index b555c62..cbf2520 100644 (file)
@@ -1,11 +1,9 @@
-# $Id: 00-compile.t 922 2004-05-29 18:19:50Z btrott $
+# $Id: 00-compile.t 1867 2005-08-09 20:41:15Z btrott $
 
-my $loaded;
-BEGIN { print "1..1\n" }
-use XML::Feed;
-use XML::Feed::Entry;
-use XML::Feed::RSS;
-use XML::Feed::Atom;
-$loaded++;
-print "ok 1\n";
-END { print "not ok 1\n" unless $loaded }
+use strict;
+use Test::More tests => 4;
+
+use_ok('XML::Feed');
+use_ok('XML::Feed::Entry');
+use_ok('XML::Feed::RSS');
+use_ok('XML::Feed::Atom');
index bce2f91..f71f25f 100644 (file)
@@ -1,12 +1,10 @@
-# $Id: 01-parse.t 933 2004-07-29 16:43:33Z btrott $
+# $Id: 01-parse.t 1867 2005-08-09 20:41:15Z btrott $
 
 use strict;
-use Test;
+use Test::More tests => 70;
 use XML::Feed;
 use URI;
 
-BEGIN { plan tests => 70 }
-
 my %Feeds = (
     't/samples/atom.xml' => 'Atom',
     't/samples/rss10.xml' => 'RSS 1.0',
@@ -16,47 +14,52 @@ my %Feeds = (
 ## First, test all of the various ways of calling parse.
 my $feed;
 my $file = 't/samples/atom.xml';
-ok($feed = XML::Feed->parse($file));
-ok($feed->title, 'First Weblog');
+$feed = XML::Feed->parse($file);
+isa_ok($feed, 'XML::Feed::Atom');
+is($feed->title, 'First Weblog');
 open my $fh, $file or die "Can't open $file: $!";
-ok($feed = XML::Feed->parse($fh));
-ok($feed->title, 'First Weblog');
+$feed = XML::Feed->parse($fh);
+isa_ok($feed, 'XML::Feed::Atom');
+is($feed->title, 'First Weblog');
 seek $fh, 0, 0;
 my $xml = do { local $/; <$fh> };
-ok($feed = XML::Feed->parse(\$xml));
-ok($feed->title, 'First Weblog');
-ok($feed = XML::Feed->parse(URI->new("file:$file")));
-ok($feed->title, 'First Weblog');
+$feed = XML::Feed->parse(\$xml);
+isa_ok($feed, 'XML::Feed::Atom');
+is($feed->title, 'First Weblog');
+$feed = XML::Feed->parse(URI->new("file:$file"));
+isa_ok($feed, 'XML::Feed::Atom');
+is($feed->title, 'First Weblog');
 
 ## Then try calling all of the unified API methods.
 for my $file (sort keys %Feeds) {
     my $feed = XML::Feed->parse($file) or die XML::Feed->errstr;
-    ok($feed);
-    ok($feed->format, $Feeds{$file});
-    ok($feed->language, 'en-us');
-    ok($feed->title, 'First Weblog');
-    ok($feed->link, 'http://localhost/weblog/');
-    ok($feed->tagline, 'This is a test weblog.');
-    ok($feed->description, 'This is a test weblog.');
+    my($subclass) = $Feeds{$file} =~ /^(\w+)/;
+    isa_ok($feed, 'XML::Feed::' . $subclass);
+    is($feed->format, $Feeds{$file});
+    is($feed->language, 'en-us');
+    is($feed->title, 'First Weblog');
+    is($feed->link, 'http://localhost/weblog/');
+    is($feed->tagline, 'This is a test weblog.');
+    is($feed->description, 'This is a test weblog.');
     my $dt = $feed->modified;
-    ok(ref($dt), 'DateTime');
+    isa_ok($dt, 'DateTime');
     $dt->set_time_zone('UTC');
-    ok($dt->iso8601, '2004-05-30T07:39:57');
-    ok($feed->author, 'Melody');
+    is($dt->iso8601, '2004-05-30T07:39:57');
+    is($feed->author, 'Melody');
 
     my @entries = $feed->entries;
-    ok(scalar @entries, 2);
+    is(scalar @entries, 2);
     my $entry = $entries[0];
-    ok($entry->title, 'Entry Two');
-    ok($entry->link, 'http://localhost/weblog/2004/05/entry_two.html');
+    is($entry->title, 'Entry Two');
+    is($entry->link, 'http://localhost/weblog/2004/05/entry_two.html');
     $dt = $entry->issued;
-    ok(ref($dt), 'DateTime');
+    isa_ok($dt, 'DateTime');
     $dt->set_time_zone('UTC');
-    ok($dt->iso8601, '2004-05-30T07:39:25');
-    ok($entry->content->body =~ /<p>Hello!<\/p>/);
-    ok($entry->summary->body, 'Hello!...');
-    ok($entry->category, 'Travel');
-    ok($entry->author, 'Melody');
+    is($dt->iso8601, '2004-05-30T07:39:25');
+    like($entry->content->body, qr/<p>Hello!<\/p>/);
+    is($entry->summary->body, 'Hello!...');
+    is($entry->category, 'Travel');
+    is($entry->author, 'Melody');
     ok($entry->id);
 }
 
@@ -64,4 +67,4 @@ $feed = XML::Feed->parse('t/samples/rss20-no-summary.xml')
     or die XML::Feed->errstr;
 my $entry = ($feed->entries)[0];
 ok(!$entry->summary->body);
-ok($entry->content->body =~ m!<p>This is a test.</p>!);
+like($entry->content->body, qr/<p>This is a test.<\/p>/);
diff --git a/t/02-create.t b/t/02-create.t
new file mode 100644 (file)
index 0000000..a55b41b
--- /dev/null
@@ -0,0 +1,74 @@
+# $Id$
+
+use strict;
+use Test::More tests => 66;
+use XML::Feed;
+use XML::Feed::Entry;
+use XML::Feed::Content;
+use DateTime;
+
+for my $format (qw( Atom RSS )) {
+    my $feed = XML::Feed->new($format);
+    isa_ok($feed, 'XML::Feed::' . $format);
+    like($feed->format, qr/^$format/, 'Format is correct');
+    $feed->title('My Feed');
+    is($feed->title, 'My Feed', 'feed title is correct');
+    $feed->link('http://www.example.com/');
+    is($feed->link, 'http://www.example.com/', 'feed link is correct');
+    $feed->description('Wow!');
+    is($feed->description, 'Wow!', 'feed description is correct');
+    is($feed->tagline, 'Wow!', 'tagline works as alias');
+    $feed->tagline('Again');
+    is($feed->tagline, 'Again', 'setting via tagline works');
+    $feed->language('en_US');
+    is($feed->language, 'en_US', 'feed language is correct');
+    $feed->author('Ben');
+    is($feed->author, 'Ben', 'feed author is correct');
+    $feed->copyright('Copyright 2005 Me');
+    is($feed->copyright, 'Copyright 2005 Me', 'feed copyright is correct');
+    my $now = DateTime->now;
+    $feed->modified($now);
+    isa_ok($feed->modified, 'DateTime', 'modified returns a DateTime');
+    is($feed->modified->iso8601, $now->iso8601, 'feed modified is correct');
+    $feed->generator('Movable Type');
+    is($feed->generator, 'Movable Type', 'feed generator is correct');
+    ok($feed->as_xml, 'as_xml returns something');
+
+    my $entry = XML::Feed::Entry->new($format);
+    isa_ok($entry, 'XML::Feed::Entry::' . $format);
+    $entry->title('Foo Bar');
+    is($entry->title, 'Foo Bar', 'entry title is correct');
+    $entry->link('http://www.example.com/foo/bar.html');
+    is($entry->link, 'http://www.example.com/foo/bar.html', 'entry link is correct');
+    $entry->summary('This is a summary.');
+    isa_ok($entry->summary, 'XML::Feed::Content');
+    is($entry->summary->body, 'This is a summary.', 'entry summary is correct');
+    $entry->content('This is the content.');
+    isa_ok($entry->content, 'XML::Feed::Content');
+    is($entry->content->type, 'text/html', 'entry content type is correct');
+    is($entry->content->body, 'This is the content.', 'entry content body is correct');
+    $entry->content(XML::Feed::Content->new({
+            body => 'This is the content (again).',
+            type => 'text/plain',
+    }));
+    isa_ok($entry->content, 'XML::Feed::Content');
+    is($entry->content->body, 'This is the content (again).', 'setting with XML::Feed::Content works');
+    $entry->category('Television');
+    is($entry->category, 'Television', 'entry category is correct');
+    $entry->author('Foo Baz');
+    is($entry->author, 'Foo Baz', 'entry author is correct');
+    $entry->id('foo:bar-15132');
+    is($entry->id, 'foo:bar-15132', 'entry id is correct');
+    my $dt = DateTime->now;
+    $entry->issued($dt);
+    isa_ok($entry->issued, 'DateTime');
+    is($entry->issued->iso8601, $dt->iso8601, 'entry issued is correct');
+    $entry->modified($dt);
+    isa_ok($entry->modified, 'DateTime');
+    is($entry->modified->iso8601, $dt->iso8601, 'entry modified is correct');
+
+    $feed->add_entry($entry);
+    my @e = $feed->entries;
+    is(scalar @e, 1, 'One post in the feed');
+    is($e[0]->title, 'Foo Bar', 'Correct post');
+}