Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #2 from szabgab/master

some cleanup and example
  • Loading branch information...
commit 3c6e943beccfd314423a34e4d0872047446587ef 2 parents 4a25588 + 266eef5
@davorg authored
View
7 .gitignore
@@ -0,0 +1,7 @@
+Build
+MANIFEST.bak
+MYMETA.json
+MYMETA.yml
+_build/
+blib/
+*.swp
View
30 MANIFEST
@@ -1,13 +1,18 @@
Build.PL
+Makefile.PL
Changes
+eg/check_feed.pl
+lib/XML/Feed.pm
lib/XML/Feed/Content.pm
lib/XML/Feed/Enclosure.pm
lib/XML/Feed/Entry.pm
+lib/XML/Feed/Entry/Format/Atom.pm
+lib/XML/Feed/Entry/Format/RSS.pm
lib/XML/Feed/Format/Atom.pm
lib/XML/Feed/Format/RSS.pm
-lib/XML/Feed.pm
-MANIFEST.SKIP
MANIFEST This list of files
+MANIFEST.SKIP
+META.json
META.yml
README
t/00-compile.t
@@ -24,8 +29,8 @@ t/10-mix-and-match.t
t/11-xml-base-atom.t
t/11-xml-base-rss.t
t/12-multi-categories-atom.t
-t/12-multi-categories.base
t/12-multi-categories-rss.t
+t/12-multi-categories.base
t/12-multi-subjects-rss.t
t/13-category-hash-bug.t
t/14-enclosures.t
@@ -35,6 +40,10 @@ t/16-convert-mult-categories.t
t/17-double.t
t/18-double2.t
t/19-double3.t
+t/20-no-enclosures.t
+t/21-rss2-permalinks.t
+t/22-bug73160.t
+t/23-eval.t
t/pod-coverage.t
t/pod.t
t/samples/atom-10-example.xml
@@ -46,6 +55,9 @@ t/samples/atom.xml
t/samples/base_atom.xml
t/samples/base_rss.xml
t/samples/category-bug.xml
+t/samples/rss-multiple-categories.xml
+t/samples/rss-multiple-subjects.xml
+t/samples/rss10-datespaces.xml
t/samples/rss10-double.xml
t/samples/rss10-double2.xml
t/samples/rss10-invalid-date.xml
@@ -55,15 +67,5 @@ t/samples/rss20-double.xml
t/samples/rss20-enclosure.xml
t/samples/rss20-multi-enclosure.xml
t/samples/rss20-no-summary.xml
-t/samples/rss20.xml
-t/samples/rss-multiple-categories.xml
-t/samples/rss-multiple-subjects.xml
-Makefile.PL
-META.json
-t/19-double3.t
-t/samples/rss10-double2.xml
-t/20-no-enclosures.t
-t/21-rss2-permalinks.t
t/samples/rss20-p.xml
-t/22-bug73160.t
-t/samples/rss10-datespaces.xml
+t/samples/rss20.xml
View
4 MANIFEST.SKIP
@@ -3,8 +3,8 @@
\.git
\.bak$
_build
-Build
+Build$
blib
-Makefile
+Makefile$
^MYMETA.yml$
^MYMETA\.json$
View
57 eg/check_feed.pl
@@ -0,0 +1,57 @@
+use strict;
+use warnings;
+use v5.10;
+
+=head1 DESCRIPTION
+
+Given a URL of an Atom or RSS feed or a filename of an already downloaded
+feed, this script will try to parse it and print out what it understands
+from the feed.
+
+=cut
+
+use XML::Feed;
+
+my $src = shift;
+
+die "Usage: $0 FILE|URL\n" if not $src;
+
+my $source = $src;
+if ($src =~ m{^https?://}) {
+ $source = URI->new($src);
+} else {
+ if (not -f $source) {
+ die "'$source' does not look like a URL and it does not exist on the file-system either.\n";
+ }
+}
+
+my $feed = XML::Feed->parse( $source ) or die XML::Feed->errstr;
+say 'Title: ' . ($feed->title // '');
+say 'Tagline: ' . ($feed->tagline // '');
+say 'Format: ' . ($feed->format // '');
+say 'Author: ' . ($feed->author // '');
+say 'Link: ' . ($feed->link // '');
+say 'Base: ' . ($feed->base // '');
+say 'Language: ' . ($feed->language // '');
+say 'Copyright: ' . ($feed->copyright // '');
+say 'Modified: ' . ($feed->modified // ''); # DateTime object
+say 'Generator: ' . ($feed->generator // '');
+
+for my $entry ($feed->entries) {
+ say '';
+ say ' Link: ' . ($entry->link // '');
+ say ' Author: ' . ($entry->author // '');
+ say ' Title: ' . ($entry->title // '');
+ say ' Caregory: ' . ($entry->category // '');
+ say ' Id: ' . ($entry->id // '');
+ say ' Issued: ' . ($entry->issued // ''); # DateTime object
+ say ' Modified: ' . ($entry->modified // ''); # DateTime object
+ say ' Lat: ' . ($entry->lat // '');
+ say ' Long: ' . ($entry->long // '');
+ say ' Format: ' . ($entry->format // '');
+ say ' Tags: ' . ($entry->tags // '');
+ say ' Enclosure: ' . ($entry->enclosure // '');
+ say ' Summary: ' . ($entry->summary->body // '');
+ say ' Content: ' . ($entry->content->body // '');
+}
+
View
13 lib/XML/Feed.pm
@@ -213,10 +213,10 @@ the various syndication formats. The different flavors of RSS and Atom
handle data in different ways: date handling; summaries and content;
escaping and quoting; etc. This module attempts to remove those differences
by providing a wrapper around the formats and the classes implementing
-those formats (I<XML::RSS> and I<XML::Atom::Feed>). For example, dates are
+those formats (L<XML::RSS> and L<XML::Atom::Feed>). For example, dates are
handled differently in each of the above formats. To provide a unified API for
date handling, I<XML::Feed> converts all date formats transparently into
-I<DateTime> objects, which it then returns to the caller.
+L<DateTime> objects, which it then returns to the caller.
=head1 USAGE
@@ -232,7 +232,8 @@ Creates a new empty I<XML::Feed> object using the format I<$format>.
=head2 XML::Feed->parse($stream, $format)
-Parses a syndication feed identified by I<$stream>. I<$stream> can be any
+Parses a syndication feed identified by I<$stream> and returns an
+I<XML::Feed> obhect. I<$stream> can be any
one of the following:
=over 4
@@ -335,15 +336,15 @@ A string.
=head2 $feed->entries
A list of the entries/items in the feed. Returns an array containing
-I<XML::Feed::Entry> objects.
+L<XML::Feed::Entry> objects.
=head2 $feed->items
-A synonym for I<$feed->entries>.
+A synonym (alias) for <$feed-E<gt>entries>.
=head2 $feed->add_entry($entry)
-Adds an entry to the feed. I<$entry> should be an I<XML::Feed::Entry>
+Adds an entry to the feed. I<$entry> should be an L<XML::Feed::Entry>
object in the correct format for the feed.
=head2 $feed->as_xml
View
16 lib/XML/Feed/Entry.pm
@@ -79,7 +79,7 @@ XML::Feed::Entry - Entry/item in a syndication feed
=head1 DESCRIPTION
-I<XML::Feed::Entry> represents an entry/item in an I<XML::Feed> syndication
+I<XML::Feed::Entry> represents an entry/item in an L<XML::Feed> syndication
feed.
=head1 USAGE
@@ -109,26 +109,26 @@ instead to an offsite URI referenced in the entry.
=head2 $entry->content([ $content ])
-Bn I<XML::Feed::Content> object representing the full entry body, or as
+An L<XML::Feed::Content> object representing the full entry body, or as
much as is available in the feed.
In RSS feeds, this method will look first for
-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
+L<http://purl.org/rss/1.0/modules/content/#encoded> and
+L<http://www.w3.org/1999/xhtml#body> elements, then fall back to a
I<E<lt>descriptionE<gt>> element.
=head2 $entry->summary([ $summary ])
-An I<XML::Feed::Content> object representing a short summary of the entry.
+An L<XML::Feed::Content> object representing a short summary of the entry.
Possibly.
Since RSS feeds do not have the idea of a summary separate from the entry
body, this may not always be what you want. If the entry contains both a
I<E<lt>descriptionE<gt>> element B<and> another element typically used for
the full content of the entry--either I<http://www.w3.org/1999/xhtml/body>
-or I<http://purl.org/rss/1.0/modules/content/#encoded>--we treat that as
+or L<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>.
+an L<XML::Feed::Content> object with an empty string in the I<body>.
=head2 $entry->category([ $category ])
@@ -142,7 +142,7 @@ I<add_category> instead.
=head2 $entry->tags([ $tag ])
-A synonym for I<category>;
+A synonym (alias) for I<category>;
=head2 $entry->author([ $author ])
View
202 lib/XML/Feed/Entry/Format/Atom.pm
@@ -0,0 +1,202 @@
+package XML::Feed::Entry::Format::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 init_empty {
+ my $entry = shift;
+ $entry->{entry} = XML::Atom::Entry->new(Version => 1.0);
+ 1;
+}
+
+sub format { 'Atom' }
+
+sub title { shift->{entry}->title(@_) }
+sub source { shift->{entry}->source(@_) }
+sub updated { shift->{entry}->updated(@_) }
+sub base { shift->{entry}->base(@_) }
+
+sub link {
+ my $entry = shift;
+ if (@_) {
+ $entry->{entry}->add_link({ rel => 'alternate', href => $_[0],
+ type => 'text/html', });
+ } else {
+ my $l = first { !defined $_->rel || $_->rel eq 'alternate' } $entry->{entry}->link;
+ $l ? $l->href : undef;
+ }
+}
+
+sub summary {
+ my $entry = shift;
+ if (@_) {
+ my %param;
+ if (ref($_[0]) eq 'XML::Feed::Content') {
+ %param = (Body => $_[0]->body);
+ } else {
+ %param = (Body => $_[0]);
+ }
+ $entry->{entry}->summary(XML::Atom::Content->new(%param, Version => 1.0));
+ } else {
+ my $s = $entry->{entry}->summary;
+ # map Atom types to MIME types
+ my $type = ($s && ref($s) eq 'XML::Feed::Content') ? $s->type : undef;
+ if ($type) {
+ $type = 'text/html' if $type eq 'xhtml' || $type eq 'html';
+ $type = 'text/plain' if $type eq 'text';
+ }
+ my $body = $s;
+ if (defined $s && ref($s) eq 'XML::Feed::Content') {
+ $body = $s->body;
+ }
+ XML::Feed::Content->wrap({ type => $type,
+ body => $body });
+ }
+}
+
+my %types = (
+ 'text/xhtml' => 'xhtml',
+ 'text/html' => 'html',
+ 'text/plain' => 'text',
+);
+
+sub content {
+ my $entry = shift;
+ if (@_) {
+ my %param;
+ my $base;
+ my $orig_body;
+ if (ref($_[0]) eq 'XML::Feed::Content') {
+ $orig_body = $_[0]->body;
+ if (defined $_[0]->type && defined $types{$_[0]->type}) {
+ %param = (Body => $orig_body, Type => $types{$_[0]->type});
+
+ if ($param{'Type'} eq "html") {
+ $param{'Body'} = HTML::Entities::encode_entities($param{'Body'});
+ }
+ } else {
+ }
+ $base = $_[0]->base if defined $_[0]->base;
+ } else {
+ $orig_body = $_[0];
+ }
+ if (!exists($param{Body}))
+ {
+ $param{Body} = $orig_body;
+ }
+ $entry->{entry}->content(XML::Atom::Content->new(%param, Version => 1.0));
+ # Assigning again so the type will be normalized. This seems to be
+ # an XML-Atom do-what-I-don't-meannery.
+ $entry->{entry}->content->body($orig_body);
+ $entry->{entry}->content->base($base) if defined $base;
+ } else {
+ my $c = $entry->{entry}->content;
+
+ # map Atom types to MIME types
+ my $type = $c ? $c->type : undef;
+ if ($type) {
+ $type = 'text/html' if $type eq 'xhtml' || $type eq 'html';
+ $type = 'text/plain' if $type eq 'text';
+ }
+
+ XML::Feed::Content->wrap({ type => $type,
+ base => $c ? $c->base : 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/');
+ if (@_) {
+ $entry->{entry}->add_category({ term => $_ }) for @_;
+ return 1
+ } else {
+
+
+ my @category = ($entry->{entry}->can('categories')) ? $entry->{entry}->categories : $entry->{entry}->category;
+ my @return = @category
+ ? (map { $_->label || $_->term } @category)
+ : $entry->{entry}->getlist($ns, 'subject');
+
+ return wantarray? @return : $return[0];
+ }
+}
+
+sub author {
+ my $entry = shift;
+ if (@_ && $_[0]) {
+ my $person = XML::Atom::Person->new(Version => 1.0);
+ $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(DateTime::Format::W3CDTF->format_datetime($_[0])) if $_[0];
+ } else {
+ $entry->{entry}->issued ? iso2dt($entry->{entry}->issued) : undef;
+ }
+}
+
+sub modified {
+ my $entry = shift;
+ if (@_) {
+ $entry->{entry}->modified(DateTime::Format::W3CDTF->format_datetime($_[0])) if $_[0];
+ } else {
+ return iso2dt($entry->{entry}->modified) if $entry->{entry}->modified;
+ return iso2dt($entry->{entry}->updated) if $entry->{entry}->updated;
+ return undef;
+ }
+}
+
+sub lat {
+ my $entry = shift;
+ if (@_) {
+ $entry->{entry}->lat($_[0]) if $_[0];
+ } else {
+ $entry->{entry}->lat;
+ }
+}
+
+sub long {
+ my $entry = shift;
+ if (@_) {
+ $entry->{entry}->long($_[0]) if $_[0];
+ } else {
+ $entry->{entry}->long;
+ }
+}
+
+
+sub enclosure {
+ my $entry = shift;
+
+ if (@_) {
+ my $enclosure = shift;
+ my $method = ($XML::Feed::MULTIPLE_ENCLOSURES)? 'add_link' : 'link';
+ $entry->{entry}->$method({ rel => 'enclosure', href => $enclosure->{url},
+ length => $enclosure->{length},
+ type => $enclosure->{type} });
+ return 1;
+ } else {
+ my @links = grep { defined $_->rel && $_->rel eq 'enclosure' } $entry->{entry}->link;
+ return unless @links;
+ my @encs = map { XML::Feed::Enclosure->new({ url => $_->href, length => $_->length, type => $_->type }) } @links ;
+ return ($XML::Feed::MULTIPLE_ENCLOSURES)? @encs : $encs[-1];
+ }
+}
+
+
+1;
+
View
219 lib/XML/Feed/Entry/Format/RSS.pm
@@ -0,0 +1,219 @@
+package XML::Feed::Entry::Format::RSS;
+use strict;
+
+sub format { 'RSS ' . $_[0]->{'_version'} }
+
+use XML::Feed::Content;
+
+use base qw( XML::Feed::Entry );
+
+sub init_empty { $_[0]->{entry} = { } }
+
+sub base {
+ my $entry = shift;
+ @_ ? $entry->{entry}->{'xml:base'} = $_[0] : $entry->{entry}->{'xml:base'};
+}
+
+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 {
+ my $link = $entry->{entry}{link} ||
+ $entry->{entry}{permaLink} ||
+ $entry->{entry}{guid};
+ if (defined $link) {
+ $link =~ s/^\s+//;
+ $link =~ s/\s+$//;
+ }
+ return $link;
+ }
+}
+
+sub summary {
+ 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->{content}{encoded} &&
+ !$item->{'http://www.w3.org/1999/xhtml'}{body}) {
+ $item->{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->{content}{encoded} ||
+ $item->{'http://www.w3.org/1999/xhtml'}{body})) {
+ $txt = $item->{description};
+ ## Blogspot's 'short' RSS feeds do this in the Atom namespace
+ ## for no obviously good reason.
+ } elsif ($item->{'http://www.w3.org/2005/Atom'}{summary}) {
+ $txt = $item->{'http://www.w3.org/2005/Atom'}{summary};
+ }
+ XML::Feed::Content->wrap({ type => 'text/plain', body => $txt });
+ }
+}
+
+sub content {
+ my $item = shift->{entry};
+ if (@_) {
+ my $c;
+ if (ref($_[0]) eq 'XML::Feed::Content') {
+ if (defined $_[0]->base) {
+ $c = { 'content' => $_[0]->body, 'xml:base' => $_[0]->base };
+ } else {
+ $c = $_[0]->body;
+ }
+ } else {
+ $c = $_[0];
+ }
+ $item->{content}{encoded} = $c;
+ } else {
+ my $base;
+ my $body =
+ $item->{content}{encoded} ||
+ $item->{'http://www.w3.org/1999/xhtml'}{body} ||
+ $item->{description};
+ if ('HASH' eq ref($body)) {
+ $base = $body->{'xml:base'};
+ $body = $body->{content};
+ }
+ XML::Feed::Content->wrap({ type => 'text/html', body => $body, base => $base });
+ }
+}
+
+sub category {
+ my $entry = shift;
+ my $item = $entry->{entry};
+ if (@_) {
+ my @tmp = ($entry->category, @_);
+ $item->{category} = [@tmp];
+ $item->{dc}{subject} = [@tmp];
+ } else {
+ my $r = $item->{category} || $item->{dc}{subject};
+ my @r = ref($r) eq 'ARRAY' ? @$r : defined $r? ($r) : ();
+ return wantarray? @r : $r[0];
+ }
+}
+
+sub author {
+ 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 {
+ my $item = shift->{entry};
+ if (@_) {
+ $item->{guid} = $_[0];
+ } else {
+ $item->{guid} || $item->{link};
+ }
+}
+
+sub issued {
+ my $item = shift->{entry};
+ if (@_) {
+ $item->{dc}{date} = DateTime::Format::W3CDTF->format_datetime($_[0]);
+ $item->{pubDate} = DateTime::Format::Mail->format_datetime($_[0]);
+ } else {
+ ## Either of these could die if the format is invalid.
+ my $date;
+ eval {
+ if (my $ts = $item->{pubDate}) {
+ my $parser = DateTime::Format::Mail->new;
+ $parser->loose;
+ $ts =~ s/^\s+//;
+ $ts =~ s/\s+$//;
+ $date = $parser->parse_datetime($ts);
+ } elsif ($ts = $item->{dc}{date} or $ts = $item->{dcterms}{date}) {
+ $ts =~ s/^\s+//;
+ $ts =~ s/\s+$//;
+ $date = DateTime::Format::W3CDTF->parse_datetime($ts);
+ }
+ };
+ return $date;
+ }
+}
+
+sub modified {
+ my $item = shift->{entry};
+ if (@_) {
+ $item->{dcterms}{modified} =
+ DateTime::Format::W3CDTF->format_datetime($_[0]);
+ } else {
+ if (my $ts = $item->{dcterms}{modified} ||
+ $item->{'http://www.w3.org/2005/Atom'}{updated}) {
+ $ts =~ s/^\s+//;
+ $ts =~ s/\s+$//;
+ return eval { DateTime::Format::W3CDTF->parse_datetime($ts) } || eval { XML::Atom::Util::iso2dt($ts) };
+ }
+ }
+}
+
+sub lat {
+ my $item = shift->{entry};
+ if (@_) {
+ $item->{geo}{lat} = $_[0];
+ } else {
+ return $item->{geo}{lat};
+ }
+}
+
+sub long {
+ my $item = shift->{entry};
+ if (@_) {
+ $item->{geo}{long} = $_[0];
+ } else {
+ return $item->{geo}{long};
+ }
+}
+
+sub enclosure {
+ my $entry = shift;
+
+ if (@_) {
+ my $enclosure = shift;
+ my $val = {
+ url => $enclosure->{url},
+ type => $enclosure->{type},
+ length => $enclosure->{length}
+ };
+ if ($XML::Feed::MULTIPLE_ENCLOSURES) {
+ push @{$entry->{entry}->{enclosure}}, $val;
+ } else {
+ $entry->{entry}->{enclosure} = $val;
+ }
+ } else {
+ my $tmp = $entry->{entry}->{enclosure};
+ if (defined $tmp) {
+ my @encs = map { XML::Feed::Enclosure->new($_) }
+ (ref $tmp eq 'ARRAY')? @$tmp : ($tmp);
+ return ($XML::Feed::MULTIPLE_ENCLOSURES)? @encs : $encs[-1];
+ }
+ return;
+ }
+}
+
+1;
+
View
201 lib/XML/Feed/Format/Atom.pm
@@ -14,6 +14,7 @@ use XML::Atom::Entry;
XML::Atom::Entry->mk_elem_accessors(qw( lat long ), ['http://www.w3.org/2003/01/geo/wgs84_pos#']);
use XML::Atom::Content;
+use XML::Feed::Entry::Format::Atom;
sub identify {
my $class = shift;
@@ -126,204 +127,4 @@ sub add_entry {
sub as_xml { $_[0]->{atom}->as_xml }
-package XML::Feed::Entry::Format::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 init_empty {
- my $entry = shift;
- $entry->{entry} = XML::Atom::Entry->new(Version => 1.0);
- 1;
-}
-
-sub format { 'Atom' }
-
-sub title { shift->{entry}->title(@_) }
-sub source { shift->{entry}->source(@_) }
-sub updated { shift->{entry}->updated(@_) }
-sub base { shift->{entry}->base(@_) }
-
-sub link {
- my $entry = shift;
- if (@_) {
- $entry->{entry}->add_link({ rel => 'alternate', href => $_[0],
- type => 'text/html', });
- } else {
- my $l = first { !defined $_->rel || $_->rel eq 'alternate' } $entry->{entry}->link;
- $l ? $l->href : undef;
- }
-}
-
-sub summary {
- my $entry = shift;
- if (@_) {
- my %param;
- if (ref($_[0]) eq 'XML::Feed::Content') {
- %param = (Body => $_[0]->body);
- } else {
- %param = (Body => $_[0]);
- }
- $entry->{entry}->summary(XML::Atom::Content->new(%param, Version => 1.0));
- } else {
- my $s = $entry->{entry}->summary;
- # map Atom types to MIME types
- my $type = ($s && ref($s) eq 'XML::Feed::Content') ? $s->type : undef;
- if ($type) {
- $type = 'text/html' if $type eq 'xhtml' || $type eq 'html';
- $type = 'text/plain' if $type eq 'text';
- }
- my $body = $s;
- if (defined $s && ref($s) eq 'XML::Feed::Content') {
- $body = $s->body;
- }
- XML::Feed::Content->wrap({ type => $type,
- body => $body });
- }
-}
-
-my %types = (
- 'text/xhtml' => 'xhtml',
- 'text/html' => 'html',
- 'text/plain' => 'text',
-);
-
-sub content {
- my $entry = shift;
- if (@_) {
- my %param;
- my $base;
- my $orig_body;
- if (ref($_[0]) eq 'XML::Feed::Content') {
- $orig_body = $_[0]->body;
- if (defined $_[0]->type && defined $types{$_[0]->type}) {
- %param = (Body => $orig_body, Type => $types{$_[0]->type});
-
- if ($param{'Type'} eq "html") {
- $param{'Body'} = HTML::Entities::encode_entities($param{'Body'});
- }
- } else {
- }
- $base = $_[0]->base if defined $_[0]->base;
- } else {
- $orig_body = $_[0];
- }
- if (!exists($param{Body}))
- {
- $param{Body} = $orig_body;
- }
- $entry->{entry}->content(XML::Atom::Content->new(%param, Version => 1.0));
- # Assigning again so the type will be normalized. This seems to be
- # an XML-Atom do-what-I-don't-meannery.
- $entry->{entry}->content->body($orig_body);
- $entry->{entry}->content->base($base) if defined $base;
- } else {
- my $c = $entry->{entry}->content;
-
- # map Atom types to MIME types
- my $type = $c ? $c->type : undef;
- if ($type) {
- $type = 'text/html' if $type eq 'xhtml' || $type eq 'html';
- $type = 'text/plain' if $type eq 'text';
- }
-
- XML::Feed::Content->wrap({ type => $type,
- base => $c ? $c->base : 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/');
- if (@_) {
- $entry->{entry}->add_category({ term => $_ }) for @_;
- return 1
- } else {
-
-
- my @category = ($entry->{entry}->can('categories')) ? $entry->{entry}->categories : $entry->{entry}->category;
- my @return = @category
- ? (map { $_->label || $_->term } @category)
- : $entry->{entry}->getlist($ns, 'subject');
-
- return wantarray? @return : $return[0];
- }
-}
-
-sub author {
- my $entry = shift;
- if (@_ && $_[0]) {
- my $person = XML::Atom::Person->new(Version => 1.0);
- $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(DateTime::Format::W3CDTF->format_datetime($_[0])) if $_[0];
- } else {
- $entry->{entry}->issued ? iso2dt($entry->{entry}->issued) : undef;
- }
-}
-
-sub modified {
- my $entry = shift;
- if (@_) {
- $entry->{entry}->modified(DateTime::Format::W3CDTF->format_datetime($_[0])) if $_[0];
- } else {
- return iso2dt($entry->{entry}->modified) if $entry->{entry}->modified;
- return iso2dt($entry->{entry}->updated) if $entry->{entry}->updated;
- return undef;
- }
-}
-
-sub lat {
- my $entry = shift;
- if (@_) {
- $entry->{entry}->lat($_[0]) if $_[0];
- } else {
- $entry->{entry}->lat;
- }
-}
-
-sub long {
- my $entry = shift;
- if (@_) {
- $entry->{entry}->long($_[0]) if $_[0];
- } else {
- $entry->{entry}->long;
- }
-}
-
-
-sub enclosure {
- my $entry = shift;
-
- if (@_) {
- my $enclosure = shift;
- my $method = ($XML::Feed::MULTIPLE_ENCLOSURES)? 'add_link' : 'link';
- $entry->{entry}->$method({ rel => 'enclosure', href => $enclosure->{url},
- length => $enclosure->{length},
- type => $enclosure->{type} });
- return 1;
- } else {
- my @links = grep { defined $_->rel && $_->rel eq 'enclosure' } $entry->{entry}->link;
- return unless @links;
- my @encs = map { XML::Feed::Enclosure->new({ url => $_->href, length => $_->length, type => $_->type }) } @links ;
- return ($XML::Feed::MULTIPLE_ENCLOSURES)? @encs : $encs[-1];
- }
-}
-
-
1;
View
219 lib/XML/Feed/Format/RSS.pm
@@ -8,6 +8,7 @@ use DateTime::Format::Mail;
use DateTime::Format::W3CDTF;
use XML::Atom::Util qw(iso2dt);
use XML::Feed::Enclosure;
+use XML::Feed::Entry::Format::RSS;
our $PREFERRED_PARSER = "XML::RSS";
@@ -172,221 +173,5 @@ sub add_entry {
sub as_xml { $_[0]->{rss}->as_string }
-package XML::Feed::Entry::Format::RSS;
-use strict;
-
-sub format { 'RSS ' . $_[0]->{'_version'} }
-
-use XML::Feed::Content;
-
-use base qw( XML::Feed::Entry );
-
-sub init_empty { $_[0]->{entry} = { } }
-
-sub base {
- my $entry = shift;
- @_ ? $entry->{entry}->{'xml:base'} = $_[0] : $entry->{entry}->{'xml:base'};
-}
-
-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 {
- my $link = $entry->{entry}{link} ||
- $entry->{entry}{permaLink} ||
- $entry->{entry}{guid};
- if (defined $link) {
- $link =~ s/^\s+//;
- $link =~ s/\s+$//;
- }
- return $link;
- }
-}
-
-sub summary {
- 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->{content}{encoded} &&
- !$item->{'http://www.w3.org/1999/xhtml'}{body}) {
- $item->{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->{content}{encoded} ||
- $item->{'http://www.w3.org/1999/xhtml'}{body})) {
- $txt = $item->{description};
- ## Blogspot's 'short' RSS feeds do this in the Atom namespace
- ## for no obviously good reason.
- } elsif ($item->{'http://www.w3.org/2005/Atom'}{summary}) {
- $txt = $item->{'http://www.w3.org/2005/Atom'}{summary};
- }
- XML::Feed::Content->wrap({ type => 'text/plain', body => $txt });
- }
-}
-
-sub content {
- my $item = shift->{entry};
- if (@_) {
- my $c;
- if (ref($_[0]) eq 'XML::Feed::Content') {
- if (defined $_[0]->base) {
- $c = { 'content' => $_[0]->body, 'xml:base' => $_[0]->base };
- } else {
- $c = $_[0]->body;
- }
- } else {
- $c = $_[0];
- }
- $item->{content}{encoded} = $c;
- } else {
- my $base;
- my $body =
- $item->{content}{encoded} ||
- $item->{'http://www.w3.org/1999/xhtml'}{body} ||
- $item->{description};
- if ('HASH' eq ref($body)) {
- $base = $body->{'xml:base'};
- $body = $body->{content};
- }
- XML::Feed::Content->wrap({ type => 'text/html', body => $body, base => $base });
- }
-}
-
-sub category {
- my $entry = shift;
- my $item = $entry->{entry};
- if (@_) {
- my @tmp = ($entry->category, @_);
- $item->{category} = [@tmp];
- $item->{dc}{subject} = [@tmp];
- } else {
- my $r = $item->{category} || $item->{dc}{subject};
- my @r = ref($r) eq 'ARRAY' ? @$r : defined $r? ($r) : ();
- return wantarray? @r : $r[0];
- }
-}
-
-sub author {
- 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 {
- my $item = shift->{entry};
- if (@_) {
- $item->{guid} = $_[0];
- } else {
- $item->{guid} || $item->{link};
- }
-}
-
-sub issued {
- my $item = shift->{entry};
- if (@_) {
- $item->{dc}{date} = DateTime::Format::W3CDTF->format_datetime($_[0]);
- $item->{pubDate} = DateTime::Format::Mail->format_datetime($_[0]);
- } else {
- ## Either of these could die if the format is invalid.
- my $date;
- eval {
- if (my $ts = $item->{pubDate}) {
- my $parser = DateTime::Format::Mail->new;
- $parser->loose;
- $ts =~ s/^\s+//;
- $ts =~ s/\s+$//;
- $date = $parser->parse_datetime($ts);
- } elsif ($ts = $item->{dc}{date} or $ts = $item->{dcterms}{date}) {
- $ts =~ s/^\s+//;
- $ts =~ s/\s+$//;
- $date = DateTime::Format::W3CDTF->parse_datetime($ts);
- }
- };
- return $date;
- }
-}
-
-sub modified {
- my $item = shift->{entry};
- if (@_) {
- $item->{dcterms}{modified} =
- DateTime::Format::W3CDTF->format_datetime($_[0]);
- } else {
- if (my $ts = $item->{dcterms}{modified} ||
- $item->{'http://www.w3.org/2005/Atom'}{updated}) {
- $ts =~ s/^\s+//;
- $ts =~ s/\s+$//;
- return eval { DateTime::Format::W3CDTF->parse_datetime($ts) } || eval { XML::Atom::Util::iso2dt($ts) };
- }
- }
-}
-
-sub lat {
- my $item = shift->{entry};
- if (@_) {
- $item->{geo}{lat} = $_[0];
- } else {
- return $item->{geo}{lat};
- }
-}
-
-sub long {
- my $item = shift->{entry};
- if (@_) {
- $item->{geo}{long} = $_[0];
- } else {
- return $item->{geo}{long};
- }
-}
-
-sub enclosure {
- my $entry = shift;
-
- if (@_) {
- my $enclosure = shift;
- my $val = {
- url => $enclosure->{url},
- type => $enclosure->{type},
- length => $enclosure->{length}
- };
- if ($XML::Feed::MULTIPLE_ENCLOSURES) {
- push @{$entry->{entry}->{enclosure}}, $val;
- } else {
- $entry->{entry}->{enclosure} = $val;
- }
- } else {
- my $tmp = $entry->{entry}->{enclosure};
- if (defined $tmp) {
- my @encs = map { XML::Feed::Enclosure->new($_) }
- (ref $tmp eq 'ARRAY')? @$tmp : ($tmp);
- return ($XML::Feed::MULTIPLE_ENCLOSURES)? @encs : $encs[-1];
- }
- return;
- }
-}
-
1;
+
View
41 t/23-eval.t
@@ -0,0 +1,41 @@
+use strict;
+use warnings;
+use Test::More tests => 8;
+
+use XML::Feed;
+use XML::Feed::Entry;
+
+eval {
+ XML::Feed::Entry->new('Nofeed');
+};
+like $@, qr{Unsupported format Nofeed:}, 'Unsupported format';
+
+{
+ my $rss = XML::Feed::Entry->new('RSS');
+ isa_ok $rss, 'XML::Feed::Entry::Format::RSS';
+
+ my $atom = XML::Feed::Entry->new('Atom');
+ isa_ok $atom, 'XML::Feed::Entry::Format::Atom';
+
+ my $default = XML::Feed::Entry->new();
+ isa_ok $default, 'XML::Feed::Entry::Format::Atom';
+}
+
+
+eval {
+ XML::Feed->new('Nofeed');
+};
+like $@, qr{Unsupported format Nofeed:}, 'Unsupported format';
+
+{
+ my $rss = XML::Feed->new('RSS');
+ isa_ok $rss, 'XML::Feed::Format::RSS';
+
+ my $atom = XML::Feed->new('Atom');
+ isa_ok $atom, 'XML::Feed::Format::Atom';
+
+ my $default = XML::Feed->new();
+ isa_ok $default, 'XML::Feed::Format::Atom';
+}
+
+
Please sign in to comment.
Something went wrong with that request. Please try again.