Multiple enclosure support v0.43
Simon Wistow [Thu, 7 May 2009 00:47:58 +0000 (00:47 +0000)]
Build.PL
Changes
MANIFEST
lib/XML/Feed.pm
lib/XML/Feed/Format/Atom.pm
lib/XML/Feed/Format/RSS.pm
t/14-multi-enclosures.t [new file with mode: 0644]
t/samples/atom-multi-enclosure.xml
t/samples/rss20-multi-enclosure.xml

index a920adb..929c271 100644 (file)
--- a/Build.PL
+++ b/Build.PL
@@ -19,7 +19,7 @@ my $build = Module::Build
                           'URI::Fetch'                 => 0,
                           'XML::Atom'                  => '0.32',
                           'XML::LibXML'                => '1.66',
-                          'XML::RSS'                   => '1.40',
+                          'XML::RSS'                   => '1.44',
                           'Test::More'                 => 0,
                         },
          create_makefile_pl => 'traditional',
diff --git a/Changes b/Changes
index 5224d3e..abe72c3 100644 (file)
--- a/Changes
+++ b/Changes
@@ -2,6 +2,9 @@
 
 Revision history for XML::Feed
 
+0.43  
+    - Add optional multi enclosure support
+
 0.42
     - Fix conversion of multi value fields
       http://rt.cpan.org/Ticket/Display.html?id=41794 
index 2bc5047..df88e12 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -28,6 +28,7 @@ t/12-multi-categories.base
 t/12-multi-subjects-rss.t
 t/13-category-hash-bug.t
 t/14-enclosures.t
+t/14-multi-enclosures.t
 t/pod-coverage.t
 t/pod.t
 t/samples/atom-10-example.xml
index b30672b..13f00bf 100644 (file)
@@ -12,7 +12,8 @@ use Module::Pluggable search_path => "XML::Feed::Format",
                       require     => 1,
                       sub_name    => 'formatters';
 
-our $VERSION = '0.42';
+our $VERSION = '0.43';
+our $MULTIPLE_ENCLOSURES = 0;
 our @formatters;
 BEGIN {
        @formatters = __PACKAGE__->formatters;
@@ -366,8 +367,21 @@ B<Note:> this will only work for parsing feeds, not creating feeds.
 
 B<Note:> Only C<XML::RSS::LibXML> version 0.3004 is known to work at the moment.
 
+=item C<$XML::Feed::MULTIPLE_ENCLOSURES>
+
+Although the RSS specification states that there can be at most one enclosure per item 
+some feeds break this rule.
+
+If this variable is set then C<XML::Feed> captures all of them and makes them available as a list.
+
+Otherwise it returns the last enclosure parsed.
+
+B<Note:> C<XML::RSS> version 1.44 is needed for this to work.
+
 =back
 
+=cut
+
 =head1 VALID FEEDS
 
 For reference, this cgi script will create valid, albeit nonsensical feeds 
index 5af84bf..90987d5 100644 (file)
@@ -299,15 +299,16 @@ sub enclosure {
 
     if (@_) {
         my $enclosure = shift;
-        # TODO Atom can have multiple enclosures
-        $entry->{entry}->link({ rel => 'enclosure', href => $enclosure->{url},
+        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 $l = first { defined $_->rel && $_->rel eq 'enclosure' } $entry->{entry}->link;
-        return undef unless $l;
-        return XML::Feed::Enclosure->new({ url => $l->href, length => $l->length, type => $l->type });
+        my @links = grep { defined $_->rel && $_->rel eq 'enclosure' } $entry->{entry}->link;
+        return undef unless @links;
+        my @encs = map { XML::Feed::Enclosure->new({ url => $_->href, length => $_->length, type => $_->type }) } @links ;
+        return ($XML::Feed::MULTIPLE_ENCLOSURES)? @encs : $encs[-1];
     }
 }
 
index 7153475..bd5fbd7 100644 (file)
@@ -33,8 +33,12 @@ sub init_string {
     my $feed = shift;
     my($str) = @_;
     $feed->init_empty;
+    my $opts = {
+         hashrefs_instead_of_strings => 1,
+    };
+    $opts->{allow_multiple} = [ 'enclosure' ] if $XML::Feed::MULTIPLE_ENCLOSURES;
     if ($str) {
-        $feed->{rss}->parse($$str, { hashrefs_instead_of_strings => 1 } );
+        $feed->{rss}->parse($$str, $opts );
     }
     $feed;
 }
@@ -334,13 +338,20 @@ sub enclosure {
 
     if (@_) {
         my $enclosure = shift;
-        $entry->{entry}->{enclosure} = {
+        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 {
-        return XML::Feed::Enclosure->new($entry->{entry}->{enclosure});
+        my $tmp  = $entry->{entry}->{enclosure};
+        my @encs = map { XML::Feed::Enclosure->new($_) } (ref $tmp eq 'ARRAY')? @$tmp : ($tmp);
+        return ($XML::Feed::MULTIPLE_ENCLOSURES)? @encs : $encs[-1];
     }
 }
 
diff --git a/t/14-multi-enclosures.t b/t/14-multi-enclosures.t
new file mode 100644 (file)
index 0000000..e2ad7a0
--- /dev/null
@@ -0,0 +1,59 @@
+#!perl -w
+
+use strict;
+use Test::More; 
+use XML::Feed;
+use XML::Feed::Enclosure;
+
+$XML::Feed::MULTIPLE_ENCLOSURES=1;
+
+my @formats = qw(atom rss20);
+plan tests => scalar(@formats)*38;
+
+foreach my $format (@formats) {
+    ok (my $feed      = XML::Feed->parse("t/samples/$format-multi-enclosure.xml"), "Parsed $format");
+    my ($entry)       = $feed->entries;
+    ok (my @enclosures = $entry->enclosure,                                   "Got enclosure");
+    ok ($enclosures[0]->isa("XML::Feed::Enclosure"),                             "Object isa XML::Feed::Enclosure");
+    is ($enclosures[0]->type,   "audio/mpeg",                                    "Got the enclosure mime type");
+    is ($enclosures[0]->length, "2478719",                                       "Got enclosure length");
+    is ($enclosures[0]->url,    "http://example.com/sample_podcast.mp3",         "Got enclosure url");
+    ok ($enclosures[1]->isa("XML::Feed::Enclosure"),                             "Object isa XML::Feed::Enclosure");
+    is ($enclosures[1]->type,   "video/mpeg",                                    "Got the enclosure mime type");
+    is ($enclosures[1]->length, "8888",                                       "Got enclosure length");
+    is ($enclosures[1]->url,    "http://example.com/sample_movie.mpg",         "Got enclosure url");
+
+    ok (my $tmp       = XML::Feed::Enclosure->new({ type => "image/jpeg" }), "Created a new enclosure");
+    is ($tmp->type,         "image/jpeg",                                    "Got type back");
+    ok ($tmp->url("http://example.com/sample_image.jpg"),                    "Set url");
+    ok ($tmp->length("1337"),                                                "Set length");
+    ok ($entry->enclosure($tmp),                                             "Set the enclosure");
+
+    ok (@enclosures    = $entry->enclosure,                                   "Got enclosure again");
+    ok ($enclosures[-1]->isa("XML::Feed::Enclosure"),                             "Object still isa XML::Feed::Enclosure");
+    is ($enclosures[-1]->type,   "image/jpeg",                                    "Got the enclosure mime type");
+    is ($enclosures[-1]->length, "1337",                                          "Got enclosure length again");
+    is ($enclosures[-1]->url,    "http://example.com/sample_image.jpg",           "Got enclosure url again");
+
+    my $xml = $feed->as_xml;
+    ok ($feed         = XML::Feed->parse(\$xml),                             "Parsed xml again");
+    ok (@enclosures    = $entry->enclosure,                                   "Got enclosure again");
+    ok ($enclosures[0]->isa("XML::Feed::Enclosure"),                             "Object isa XML::Feed::Enclosure");
+    is ($enclosures[0]->type,   "audio/mpeg",                                    "Got the enclosure mime type");
+    is ($enclosures[0]->length, "2478719",                                       "Got enclosure length");
+    is ($enclosures[0]->url,    "http://example.com/sample_podcast.mp3",         "Got enclosure url");
+    ok ($enclosures[1]->isa("XML::Feed::Enclosure"),                             "Object isa XML::Feed::Enclosure");
+    is ($enclosures[1]->type,   "video/mpeg",                                    "Got the enclosure mime type");
+    is ($enclosures[1]->length, "8888",                                       "Got enclosure length");
+    is ($enclosures[1]->url,    "http://example.com/sample_movie.mpg",         "Got enclosure url");
+    ok ($enclosures[2]->isa("XML::Feed::Enclosure"),                             "Object still isa XML::Feed::Enclosure");
+    is ($enclosures[2]->type,   "image/jpeg",                                    "Got the enclosure mime type");
+    is ($enclosures[2]->length, "1337",                                          "Got enclosure length again");
+    is ($enclosures[2]->url,    "http://example.com/sample_image.jpg",           "Got enclosure url again");
+    ok ($enclosures[-1]->isa("XML::Feed::Enclosure"),                             "Object still isa XML::Feed::Enclosure");
+    is ($enclosures[-1]->type,   "image/jpeg",                                    "Got the enclosure mime type");
+    is ($enclosures[-1]->length, "1337",                                          "Got enclosure length again");
+    is ($enclosures[-1]->url,    "http://example.com/sample_image.jpg",           "Got enclosure url again");
+
+
+}
index c994c58..37d642c 100644 (file)
@@ -8,7 +8,7 @@
     <updated>2004-11-02T14:44:33</updated>
     <content>description</content>
     <link rel="enclosure" href="http://example.com/sample_podcast.mp3" length="2478719" type="audio/mpeg" />
-    <link rel="enclosure" href="http://example.com/sample_movie.mpg" length="2478719" type="video/mpeg" />
+    <link rel="enclosure" href="http://example.com/sample_movie.mpg" length="8888" type="video/mpeg" />
   </entry>
 </feed>
 
index 45f5138..dd7d09b 100644 (file)
@@ -11,7 +11,7 @@
     <pubDate>Tue, 02 Nov 2004 09:44:33 -0500</pubDate>
     <description>description</description>
     <enclosure url="http://example.com/sample_podcast.mp3" length="2478719" type="audio/mpeg" />
-    <enclosure url="http://example.com/sample_movie.mpg" length="2478719" type="video/mpeg" />
+    <enclosure url="http://example.com/sample_movie.mpg" length="8888" type="video/mpeg" />
   </item>
 </channel>
 </rss>