feedtools 0.2.18 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -165,14 +165,18 @@ module FeedTools
165
165
  # Warning, this method may be slow if you have a
166
166
  # large number of FeedTools::Feed objects. Can't
167
167
  # use a direct reference to the parent because it plays
168
- # havoc with the garbage collector.
168
+ # havoc with the garbage collector. Could've used
169
+ # a WeakRef object, but really, if there are multiple
170
+ # parent feeds, something is going to go wrong, and the
171
+ # programmer needs to be notified. A WeakRef
172
+ # implementation can't detect this condition.
169
173
  def feed
170
174
  parent_feed = nil
171
175
  ObjectSpace.each_object(FeedTools::Feed) do |feed|
172
- if feed.instance_variable_get("@items").nil?
176
+ if feed.instance_variable_get("@entries").nil?
173
177
  feed.items
174
178
  end
175
- unsorted_items = feed.instance_variable_get("@items")
179
+ unsorted_items = feed.instance_variable_get("@entries")
176
180
  for item in unsorted_items
177
181
  if item.object_id == self.object_id
178
182
  if parent_feed.nil?
@@ -224,13 +228,21 @@ module FeedTools
224
228
  end
225
229
 
226
230
  # Returns the first node within the root_node that matches the xpath query.
227
- def find_node(xpath)
228
- return XPath.first(root_node, xpath)
231
+ def find_node(xpath, select_result_value=false)
232
+ if feed.feed_data_type != :xml
233
+ raise "The feed data type is not xml."
234
+ end
235
+ return try_xpaths(self.root_node, [xpath],
236
+ :select_result_value => select_result_value)
229
237
  end
230
238
 
231
239
  # Returns all nodes within the root_node that match the xpath query.
232
- def find_all_nodes(xpath)
233
- return XPath.match(root_node, xpath)
240
+ def find_all_nodes(xpath, select_result_value=false)
241
+ if feed.feed_data_type != :xml
242
+ raise "The feed data type is not xml."
243
+ end
244
+ return try_xpaths_all(self.root_node, [xpath],
245
+ :select_result_value => select_result_value)
234
246
  end
235
247
 
236
248
  # Returns the root node of the feed item.
@@ -247,13 +259,13 @@ module FeedTools
247
259
  # Returns the feed items's unique id
248
260
  def id
249
261
  if @id.nil?
250
- unless root_node.nil?
251
- @id = XPath.first(root_node, "id/text()").to_s
252
- if @id == ""
253
- @id = XPath.first(root_node, "guid/text()").to_s
254
- end
255
- end
256
- @id = nil if @id == ""
262
+ @id = try_xpaths(self.root_node, [
263
+ "atom10:id/text()",
264
+ "atom03:id/text()",
265
+ "atom:id/text()",
266
+ "id/text()",
267
+ "guid/text()"
268
+ ], :select_result_value => true)
257
269
  end
258
270
  return @id
259
271
  end
@@ -266,37 +278,23 @@ module FeedTools
266
278
  # Returns the feed item title
267
279
  def title
268
280
  if @title.nil?
269
- unless root_node.nil?
270
- repair_entities = false
271
- title_node = XPath.first(root_node, "atom10:title",
272
- FEED_TOOLS_NAMESPACES)
273
- if title_node.nil?
274
- title_node = XPath.first(root_node, "title")
275
- end
276
- if title_node.nil?
277
- title_node = XPath.first(root_node, "atom03:title",
278
- FEED_TOOLS_NAMESPACES)
279
- end
280
- if title_node.nil?
281
- title_node = XPath.first(root_node, "atom:title")
282
- end
283
- if title_node.nil?
284
- title_node = XPath.first(root_node, "dc:title",
285
- FEED_TOOLS_NAMESPACES)
286
- end
287
- if title_node.nil?
288
- title_node = XPath.first(root_node, "dc:title")
289
- end
290
- if title_node.nil?
291
- title_node = XPath.first(root_node, "TITLE")
292
- end
293
- end
281
+ repair_entities = false
282
+ title_node = try_xpaths(self.root_node, [
283
+ "atom10:title",
284
+ "atom03:title",
285
+ "atom:title",
286
+ "title",
287
+ "dc:title"
288
+ ])
294
289
  if title_node.nil?
295
290
  return nil
296
291
  end
297
- title_type = XPath.first(title_node, "@type").to_s
298
- title_mode = XPath.first(title_node, "@mode").to_s
299
- title_encoding = XPath.first(title_node, "@encoding").to_s
292
+ title_type = try_xpaths(title_node, "@type",
293
+ :select_result_value => true)
294
+ title_mode = try_xpaths(title_node, "@mode",
295
+ :select_result_value => true)
296
+ title_encoding = try_xpaths(title_node, "@encoding",
297
+ :select_result_value => true)
300
298
 
301
299
  # Note that we're checking for misuse of type, mode and encoding here
302
300
  if title_type == "base64" || title_mode == "base64" ||
@@ -318,7 +316,7 @@ module FeedTools
318
316
  @title = FeedTools.unescape_entities(@title) if repair_entities
319
317
  @title = FeedTools.tidy_html(@title) unless repair_entities
320
318
  end
321
- if @title != ""
319
+ if !@title.blank? && FeedTools.configurations[:strip_comment_count]
322
320
  # Some blogging tools include the number of comments in a post
323
321
  # in the title... this is supremely ugly, and breaks any
324
322
  # applications which expect the title to be static, so we're
@@ -331,7 +329,7 @@ module FeedTools
331
329
  @title.gsub!(/>\n</, "><")
332
330
  @title.gsub!(/\n/, " ")
333
331
  @title.strip!
334
- @title = nil if @title == ""
332
+ @title = nil if @title.blank?
335
333
  end
336
334
  return @title
337
335
  end
@@ -341,123 +339,90 @@ module FeedTools
341
339
  @title = new_title
342
340
  end
343
341
 
344
- # Returns the feed item description
345
- def description
346
- if @description.nil?
347
- unless root_node.nil?
348
- repair_entities = false
349
- description_node = XPath.first(root_node, "content:encoded")
350
- if description_node.nil?
351
- description_node = XPath.first(root_node, "content:encoded",
352
- FEED_TOOLS_NAMESPACES)
353
- end
354
- if description_node.nil?
355
- description_node = XPath.first(root_node, "encoded")
356
- end
357
- if description_node.nil?
358
- description_node = XPath.first(root_node, "content")
359
- end
360
- if description_node.nil?
361
- description_node = XPath.first(root_node, "fullitem")
362
- end
363
- if description_node.nil?
364
- description_node = XPath.first(root_node, "xhtml:body")
365
- end
366
- if description_node.nil?
367
- description_node = XPath.first(root_node, "xhtml:body",
368
- FEED_TOOLS_NAMESPACES)
369
- end
370
- if description_node.nil?
371
- description_node = XPath.first(root_node, "body")
372
- end
373
- if description_node.nil?
374
- description_node = XPath.first(root_node, "description")
375
- end
376
- if description_node.nil?
377
- description_node = XPath.first(root_node, "tagline")
378
- end
379
- if description_node.nil?
380
- description_node = XPath.first(root_node, "subtitle")
381
- end
382
- if description_node.nil?
383
- description_node = XPath.first(root_node, "summary")
384
- end
385
- if description_node.nil?
386
- description_node = XPath.first(root_node, "abstract")
387
- end
388
- if description_node.nil?
389
- description_node = XPath.first(root_node, "ABSTRACT")
390
- end
391
- if description_node.nil?
392
- description_node = XPath.first(root_node, "blurb")
393
- end
394
- if description_node.nil?
395
- description_node = XPath.first(root_node, "info")
396
- end
397
- end
398
- if description_node.nil?
342
+ # Returns the feed item content
343
+ def content
344
+ if @content.nil?
345
+ repair_entities = false
346
+ content_node = try_xpaths(self.root_node, [
347
+ "content:encoded",
348
+ "content",
349
+ "fullitem",
350
+ "xhtml:body",
351
+ "body",
352
+ "encoded",
353
+ "description",
354
+ "tagline",
355
+ "subtitle",
356
+ "summary",
357
+ "abstract",
358
+ "blurb",
359
+ "info"
360
+ ])
361
+ if content_node.nil?
399
362
  return nil
400
363
  end
401
- description_type = XPath.first(description_node, "@type").to_s
402
- description_mode = XPath.first(description_node, "@mode").to_s
403
- description_encoding = XPath.first(description_node, "@encoding").to_s
364
+ content_type = try_xpaths(content_node, "@type",
365
+ :select_result_value => true)
366
+ content_mode = try_xpaths(content_node, "@mode",
367
+ :select_result_value => true)
368
+ content_encoding = try_xpaths(content_node, "@encoding",
369
+ :select_result_value => true)
404
370
 
405
371
  # Note that we're checking for misuse of type, mode and encoding here
406
- if description_encoding != ""
407
- @description =
372
+ if !content_encoding.blank?
373
+ @content =
408
374
  "[Embedded data objects are not currently supported.]"
409
- elsif description_node.cdatas.size > 0
410
- @description = description_node.cdatas.first.value
411
- elsif description_type == "base64" || description_mode == "base64" ||
412
- description_encoding == "base64"
413
- @description = Base64.decode64(description_node.inner_xml.strip)
414
- elsif description_type == "xhtml" || description_mode == "xhtml" ||
415
- description_type == "xml" || description_mode == "xml" ||
416
- description_type == "application/xhtml+xml"
417
- @description = description_node.inner_xml
418
- elsif description_type == "escaped" || description_mode == "escaped"
419
- @description = FeedTools.unescape_entities(
420
- description_node.inner_xml)
375
+ elsif content_node.cdatas.size > 0
376
+ @content = content_node.cdatas.first.value
377
+ elsif content_type == "base64" || content_mode == "base64" ||
378
+ content_encoding == "base64"
379
+ @content = Base64.decode64(content_node.inner_xml.strip)
380
+ elsif content_type == "xhtml" || content_mode == "xhtml" ||
381
+ content_type == "xml" || content_mode == "xml" ||
382
+ content_type == "application/xhtml+xml"
383
+ @content = content_node.inner_xml
384
+ elsif content_type == "escaped" || content_mode == "escaped"
385
+ @content = FeedTools.unescape_entities(
386
+ content_node.inner_xml)
421
387
  else
422
- @description = description_node.inner_xml
388
+ @content = content_node.inner_xml
423
389
  repair_entities = true
424
390
  end
425
- if @description == ""
426
- @description = self.itunes_summary
427
- @description = "" if @description.nil?
391
+ if @content.blank?
392
+ @content = self.itunes_summary
428
393
  end
429
- if @description == ""
430
- @description = self.itunes_subtitle
431
- @description = "" if @description.nil?
394
+ if @content.blank?
395
+ @content = self.itunes_subtitle
432
396
  end
433
397
 
434
- unless @description.nil?
435
- @description = FeedTools.sanitize_html(@description, :strip)
436
- @description = FeedTools.unescape_entities(@description) if repair_entities
437
- @description = FeedTools.tidy_html(@description)
398
+ unless @content.blank?
399
+ @content = FeedTools.sanitize_html(@content, :strip)
400
+ @content = FeedTools.unescape_entities(@content) if repair_entities
401
+ @content = FeedTools.tidy_html(@content)
438
402
  end
439
403
 
440
- @description = @description.strip unless @description.nil?
441
- @description = nil if @description == ""
404
+ @content = @content.strip unless @content.nil?
405
+ @content = nil if @content.blank?
442
406
  end
443
- return @description
407
+ return @content
444
408
  end
445
409
 
446
- # Sets the feed item description
447
- def description=(new_description)
448
- @description = new_description
410
+ # Sets the feed item content
411
+ def content=(new_content)
412
+ @content = new_content
449
413
  end
450
414
 
451
415
  # Returns the contents of the itunes:summary element
452
416
  def itunes_summary
453
417
  if @itunes_summary.nil?
454
- @itunes_summary = FeedTools.unescape_entities(XPath.first(root_node,
455
- "itunes:summary/text()").to_s)
456
- if @itunes_summary == ""
457
- @itunes_summary = nil
458
- end
459
- unless @itunes_summary.nil?
418
+ @itunes_summary = try_xpaths(self.root_node, [
419
+ "itunes:summary/text()"
420
+ ])
421
+ unless @itunes_summary.blank?
422
+ @itunes_summary = FeedTools.unescape_entities(@itunes_summary)
460
423
  @itunes_summary = FeedTools.sanitize_html(@itunes_summary)
424
+ else
425
+ @itunes_summary = nil
461
426
  end
462
427
  end
463
428
  return @itunes_summary
@@ -471,13 +436,14 @@ module FeedTools
471
436
  # Returns the contents of the itunes:subtitle element
472
437
  def itunes_subtitle
473
438
  if @itunes_subtitle.nil?
474
- @itunes_subtitle = FeedTools.unescape_entities(XPath.first(root_node,
475
- "itunes:subtitle/text()").to_s)
476
- if @itunes_subtitle == ""
477
- @itunes_subtitle = nil
478
- end
479
- unless @itunes_subtitle.nil?
439
+ @itunes_subtitle = try_xpaths(self.root_node, [
440
+ "itunes:subtitle/text()"
441
+ ])
442
+ unless @itunes_subtitle.blank?
443
+ @itunes_subtitle = FeedTools.unescape_entities(@itunes_subtitle)
480
444
  @itunes_subtitle = FeedTools.sanitize_html(@itunes_subtitle)
445
+ else
446
+ @itunes_subtitle = nil
481
447
  end
482
448
  end
483
449
  return @itunes_subtitle
@@ -511,39 +477,31 @@ module FeedTools
511
477
  # Returns the feed item link
512
478
  def link
513
479
  if @link.nil?
514
- unless root_node.nil?
515
- @link = XPath.first(root_node, "link[@rel='alternate']/@href").to_s
516
- if @link == ""
517
- @link = XPath.first(root_node, "link/@href").to_s
518
- end
519
- if @link == ""
520
- @link = XPath.first(root_node, "link/text()").to_s
521
- end
522
- if @link == ""
523
- @link = XPath.first(root_node, "@rdf:about").to_s
524
- end
525
- if @link == ""
526
- @link = XPath.first(root_node, "guid[@isPermaLink='true']/text()").to_s
527
- end
528
- if @link == ""
529
- @link = XPath.first(root_node, "@href").to_s
530
- end
531
- if @link == ""
532
- @link = XPath.first(root_node, "a/@href").to_s
533
- end
534
- if @link == ""
535
- @link = XPath.first(root_node, "@HREF").to_s
536
- end
537
- if @link == ""
538
- @link = XPath.first(root_node, "A/@HREF").to_s
539
- end
540
- end
541
- if @link == "" || @link.nil?
480
+ @link = try_xpaths(self.root_node, [
481
+ "atom10:link[@type='application/xhtml+xml']/@href",
482
+ "atom10:link[@type='text/html']/@href",
483
+ "atom10:link[@rel='alternate']/@href",
484
+ "atom03:link[@type='application/xhtml+xml']/@href",
485
+ "atom03:link[@type='text/html']/@href",
486
+ "atom03:link[@rel='alternate']/@href",
487
+ "atom:link[@type='application/xhtml+xml']/@href",
488
+ "atom:link[@type='text/html']/@href",
489
+ "atom:link[@rel='alternate']/@href",
490
+ "link[@type='application/xhtml+xml']/@href",
491
+ "link[@type='text/html']/@href",
492
+ "link[@rel='alternate']/@href",
493
+ "link/text()",
494
+ "@rdf:about",
495
+ "guid[@isPermaLink='true']/text()",
496
+ "@href",
497
+ "a/@href"
498
+ ], :select_result_value => true)
499
+ if @link.blank?
542
500
  if FeedTools.is_uri? self.guid
543
501
  @link = self.guid
544
502
  end
545
503
  end
546
- if @link != ""
504
+ if !@link.blank?
547
505
  @link = FeedTools.unescape_entities(@link)
548
506
  end
549
507
  # TODO: Actually implement proper relative url resolving instead of this crap
@@ -556,7 +514,50 @@ module FeedTools
556
514
  # # prepend the base to the link since they seem to have used a relative path
557
515
  # @link = feed.base + @link
558
516
  # end
559
- @link = FeedTools.normalize_url(@link)
517
+ if @link.blank?
518
+ link_node = try_xpaths(self.root_node, [
519
+ "atom10:link",
520
+ "atom03:link",
521
+ "atom:link",
522
+ "link"
523
+ ])
524
+ if link_node != nil
525
+ if link_node.attributes['type'].to_s =~ /^image/ ||
526
+ link_node.attributes['type'].to_s =~ /^application/ ||
527
+ link_node.attributes['type'].to_s =~ /xml/ ||
528
+ link_node.attributes['rel'].to_s =~ /self/
529
+ for child in self.root_node
530
+ if child.class == REXML::Element
531
+ if child.name.downcase == "link"
532
+ if child.attributes['type'].to_s =~ /^image/ ||
533
+ child.attributes['type'].to_s =~ /^application/ ||
534
+ child.attributes['type'].to_s =~ /xml/ ||
535
+ child.attributes['rel'].to_s =~ /self/
536
+ @link = nil
537
+ next
538
+ else
539
+ @link = child.attributes['href'].to_s
540
+ if @link.blank?
541
+ @link = child.inner_xml
542
+ end
543
+ if @link.blank?
544
+ next
545
+ end
546
+ break
547
+ end
548
+ end
549
+ end
550
+ end
551
+ else
552
+ @link = link_node.attributes['href'].to_s
553
+ end
554
+ end
555
+ end
556
+ @link = self.comments if @link.blank?
557
+ @link = nil if @link.blank?
558
+ if FeedTools.configurations[:url_normalization_enabled]
559
+ @link = FeedTools.normalize_url(@link)
560
+ end
560
561
  end
561
562
  return @link
562
563
  end
@@ -570,30 +571,24 @@ module FeedTools
570
571
  def categories
571
572
  if @categories.nil?
572
573
  @categories = []
573
- category_nodes = XPath.match(root_node, "category")
574
- if category_nodes.nil? || category_nodes.empty?
575
- category_nodes = XPath.match(root_node, "dc:subject")
576
- end
577
- unless category_nodes.nil?
578
- for category_node in category_nodes
579
- category = FeedTools::Feed::Category.new
580
- category.term = XPath.first(category_node, "@term").to_s
581
- if category.term == ""
582
- category.term = XPath.first(category_node, "text()").to_s
583
- end
584
- category.term.strip! unless category.term.nil?
585
- category.term = nil if category.term == ""
586
- category.label = XPath.first(category_node, "@label").to_s
587
- category.label.strip! unless category.label.nil?
588
- category.label = nil if category.label == ""
589
- category.scheme = XPath.first(category_node, "@scheme").to_s
590
- if category.scheme == ""
591
- category.scheme = XPath.first(category_node, "@domain").to_s
592
- end
593
- category.scheme.strip! unless category.scheme.nil?
594
- category.scheme = nil if category.scheme == ""
595
- @categories << category
596
- end
574
+ category_nodes = try_xpaths_all(self.root_node, [
575
+ "category",
576
+ "dc:subject"
577
+ ])
578
+ for category_node in category_nodes
579
+ category = FeedTools::Feed::Category.new
580
+ category.term = try_xpaths(category_node, ["@term", "text()"],
581
+ :select_result_value => true)
582
+ category.term.strip! unless category.term.nil?
583
+ category.label = try_xpaths(category_node, ["@label"],
584
+ :select_result_value => true)
585
+ category.label.strip! unless category.label.nil?
586
+ category.scheme = try_xpaths(category_node, [
587
+ "@scheme",
588
+ "@domain"
589
+ ], :select_result_value => true)
590
+ category.scheme.strip! unless category.scheme.nil?
591
+ @categories << category
597
592
  end
598
593
  end
599
594
  return @categories
@@ -603,53 +598,61 @@ module FeedTools
603
598
  def images
604
599
  if @images.nil?
605
600
  @images = []
606
- image_nodes = XPath.match(root_node, "link")
607
- if image_nodes.nil? || image_nodes.empty?
608
- image_nodes = XPath.match(root_node, "logo")
609
- end
610
- if image_nodes.nil? || image_nodes.empty?
611
- image_nodes = XPath.match(root_node, "LOGO")
612
- end
613
- if image_nodes.nil? || image_nodes.empty?
614
- image_nodes = XPath.match(root_node, "image")
615
- end
616
- unless image_nodes.nil?
601
+ image_nodes = try_xpaths_all(self.root_node, [
602
+ "image",
603
+ "logo",
604
+ "atom10:link",
605
+ "atom03:link",
606
+ "atom:link",
607
+ "link"
608
+ ])
609
+ unless image_nodes.blank?
617
610
  for image_node in image_nodes
618
611
  image = FeedTools::Feed::Image.new
619
- image.url = XPath.first(image_node, "url/text()").to_s
620
- if image.url == ""
621
- image.url = XPath.first(image_node, "@rdf:resource").to_s
622
- end
623
- if image.url == "" && (image_node.name == "logo" ||
624
- (image_node.attributes['type'] =~ /^image/) == 0)
625
- image.url = XPath.first(image_node, "@href").to_s
612
+ image.url = try_xpaths(image_node, [
613
+ "url/text()",
614
+ "@rdf:resource"
615
+ ], :select_result_value => true)
616
+ if image.url.blank? && (image_node.name == "logo" ||
617
+ (image_node.attributes['type'].to_s =~ /^image/) == 0)
618
+ image.url = try_xpaths(image_node, [
619
+ "@atom10:href",
620
+ "@atom03:href",
621
+ "@atom:href",
622
+ "@href"
623
+ ], :select_result_value => true)
624
+ if image.url == self.link && image.url != nil
625
+ image.url = nil
626
+ end
626
627
  end
627
- if image.url == "" && image_node.name == "LOGO"
628
- image.url = XPath.first(image_node, "@HREF").to_s
628
+ if image.url.blank? && image_node.name == "LOGO"
629
+ image.url = try_xpaths(image_node, [
630
+ "@href"
631
+ ], :select_result_value => true)
629
632
  end
630
633
  image.url.strip! unless image.url.nil?
631
- image.url = nil if image.url == ""
632
- image.title = XPath.first(image_node, "title/text()").to_s
634
+ image.title = try_xpaths(image_node,
635
+ ["title/text()"], :select_result_value => true)
633
636
  image.title.strip! unless image.title.nil?
634
- image.title = nil if image.title == ""
635
- image.description =
636
- XPath.first(image_node, "description/text()").to_s
637
+ image.description = try_xpaths(image_node,
638
+ ["description/text()"], :select_result_value => true)
637
639
  image.description.strip! unless image.description.nil?
638
- image.description = nil if image.description == ""
639
- image.link = XPath.first(image_node, "link/text()").to_s
640
+ image.link = try_xpaths(image_node,
641
+ ["link/text()"], :select_result_value => true)
640
642
  image.link.strip! unless image.link.nil?
641
- image.link = nil if image.link == ""
642
- image.height = XPath.first(image_node, "height/text()").to_s.to_i
643
+ image.height = try_xpaths(image_node,
644
+ ["height/text()"], :select_result_value => true).to_i
643
645
  image.height = nil if image.height <= 0
644
- image.width = XPath.first(image_node, "width/text()").to_s.to_i
646
+ image.width = try_xpaths(image_node,
647
+ ["width/text()"], :select_result_value => true).to_i
645
648
  image.width = nil if image.width <= 0
646
- image.style = XPath.first(image_node, "@style").to_s.downcase
647
- if image.style == ""
648
- image.style = XPath.first(image_node, "@STYLE").to_s.downcase
649
- end
649
+ image.style = try_xpaths(image_node, [
650
+ "style/text()",
651
+ "@style"
652
+ ], :select_result_value => true)
650
653
  image.style.strip! unless image.style.nil?
651
- image.style = nil if image.style == ""
652
- @images << image
654
+ image.style.downcase! unless image.style.nil?
655
+ @images << image unless image.url.nil?
653
656
  end
654
657
  end
655
658
  end
@@ -657,19 +660,15 @@ module FeedTools
657
660
  end
658
661
 
659
662
  # Returns the feed item itunes image link
660
- #
661
- # If it's not present, falls back to the normal image link.
662
- # Technically, the itunes spec says that the image needs to be
663
- # square and larger than 300x300, but hey, if there's an image
664
- # to be had, it's better than none at all.
665
663
  def itunes_image_link
666
664
  if @itunes_image_link.nil?
667
- # get the feed item itunes image link from the xml document
668
- @itunes_image_link = XPath.first(root_node, "itunes:image/@href").to_s
669
- if @itunes_image_link == ""
670
- @itunes_image_link = XPath.first(root_node, "itunes:link[@rel='image']/@href").to_s
665
+ @itunes_image_link = try_xpaths(self.root_node, [
666
+ "itunes:image/@href",
667
+ "itunes:link[@rel='image']/@href"
668
+ ], :select_result_value => true)
669
+ if FeedTools.configurations[:url_normalization_enabled]
670
+ @itunes_image_link = FeedTools.normalize_url(@itunes_image_link)
671
671
  end
672
- @itunes_image_link = FeedTools.normalize_url(@itunes_image_link)
673
672
  end
674
673
  return @itunes_image_link
675
674
  end
@@ -680,13 +679,14 @@ module FeedTools
680
679
  end
681
680
 
682
681
  # Returns the feed item media thumbnail link
683
- #
684
- # If it's not present, falls back to the normal image link.
685
682
  def media_thumbnail_link
686
683
  if @media_thumbnail_link.nil?
687
- # get the feed item itunes image link from the xml document
688
- @media_thumbnail_link = XPath.first(root_node, "media:thumbnail/@url").to_s
689
- @media_thumbnail_link = FeedTools.normalize_url(@media_thumbnail_link)
684
+ @media_thumbnail_link = try_xpaths(self.root_node, [
685
+ "media:thumbnail/@url"
686
+ ], :select_result_value => true)
687
+ if FeedTools.configurations[:url_normalization_enabled]
688
+ @media_thumbnail_link = FeedTools.normalize_url(@media_thumbnail_link)
689
+ end
690
690
  end
691
691
  return @media_thumbnail_link
692
692
  end
@@ -699,44 +699,28 @@ module FeedTools
699
699
  # Returns the feed item's copyright information
700
700
  def copyright
701
701
  if @copyright.nil?
702
- unless root_node.nil?
703
- repair_entities = false
704
-
705
- copyright_node = XPath.first(root_node, "dc:rights")
706
- if copyright_node.nil?
707
- copyright_node = XPath.first(root_node, "dc:rights",
708
- FEED_TOOLS_NAMESPACES)
709
- end
710
- if copyright_node.nil?
711
- copyright_node = XPath.first(root_node, "rights",
712
- FEED_TOOLS_NAMESPACES)
713
- end
714
- if copyright_node.nil?
715
- copyright_node = XPath.first(root_node, "copyright",
716
- FEED_TOOLS_NAMESPACES)
717
- end
718
- if copyright_node.nil?
719
- copyright_node = XPath.first(root_node, "atom03:copyright",
720
- FEED_TOOLS_NAMESPACES)
721
- end
722
- if copyright_node.nil?
723
- copyright_node = XPath.first(root_node, "atom10:copyright",
724
- FEED_TOOLS_NAMESPACES)
725
- end
726
- if copyright_node.nil?
727
- copyright_node = XPath.first(root_node, "copyrights",
728
- FEED_TOOLS_NAMESPACES)
729
- end
730
- end
702
+ repair_entities = false
703
+ copyright_node = try_xpaths(self.root_node, [
704
+ "atom10:copyright",
705
+ "atom03:copyright",
706
+ "atom:copyright",
707
+ "copyright",
708
+ "copyrights",
709
+ "dc:rights",
710
+ "rights"
711
+ ])
731
712
  if copyright_node.nil?
732
713
  return nil
733
714
  end
734
- copyright_type = XPath.first(copyright_node, "@type").to_s
735
- copyright_mode = XPath.first(copyright_node, "@mode").to_s
736
- copyright_encoding = XPath.first(copyright_node, "@encoding").to_s
715
+ copyright_type = try_xpaths(copyright_node, "@type",
716
+ :select_result_value => true)
717
+ copyright_mode = try_xpaths(copyright_node, "@mode",
718
+ :select_result_value => true)
719
+ copyright_encoding = try_xpaths(copyright_node, "@encoding",
720
+ :select_result_value => true)
737
721
 
738
722
  # Note that we're checking for misuse of type, mode and encoding here
739
- if copyright_encoding != ""
723
+ if !copyright_encoding.blank?
740
724
  @copyright =
741
725
  "[Embedded data objects are not currently supported.]"
742
726
  elsif copyright_node.cdatas.size > 0
@@ -763,7 +747,7 @@ module FeedTools
763
747
  end
764
748
 
765
749
  @copyright = @copyright.strip unless @copyright.nil?
766
- @copyright = nil if @copyright == ""
750
+ @copyright = nil if @copyright.blank?
767
751
  end
768
752
  return @copyright
769
753
  end
@@ -779,16 +763,27 @@ module FeedTools
779
763
  @enclosures = []
780
764
 
781
765
  # First, load up all the different possible sources of enclosures
782
- rss_enclosures = XPath.match(root_node, "enclosure")
783
- atom_enclosures = XPath.match(root_node, "link[@rel='enclosure']")
784
- media_content_enclosures = XPath.match(root_node, "media:content")
785
- media_group_enclosures = XPath.match(root_node, "media:group")
786
-
787
- # Parse RSS-type enclosures. Thanks to a few buggy enclosures implementations,
788
- # sometimes these also manage to show up in atom files.
766
+ rss_enclosures =
767
+ try_xpaths_all(self.root_node, ["enclosure"])
768
+ atom_enclosures =
769
+ try_xpaths_all(self.root_node, [
770
+ "atom10:link[@rel='enclosure']",
771
+ "atom03:link[@rel='enclosure']",
772
+ "atom:link[@rel='enclosure']",
773
+ "link[@rel='enclosure']"
774
+ ])
775
+ media_content_enclosures =
776
+ try_xpaths_all(self.root_node, ["media:content"])
777
+ media_group_enclosures =
778
+ try_xpaths_all(self.root_node, ["media:group"])
779
+
780
+ # Parse RSS-type enclosures. Thanks to a few buggy enclosures
781
+ # implementations, sometimes these also manage to show up in atom
782
+ # files.
789
783
  for enclosure_node in rss_enclosures
790
784
  enclosure = Enclosure.new
791
- enclosure.url = FeedTools.unescape_entities(enclosure_node.attributes["url"].to_s)
785
+ enclosure.url = FeedTools.unescape_entities(
786
+ enclosure_node.attributes["url"].to_s)
792
787
  enclosure.type = enclosure_node.attributes["type"].to_s
793
788
  enclosure.file_size = enclosure_node.attributes["length"].to_i
794
789
  enclosure.credits = []
@@ -796,10 +791,11 @@ module FeedTools
796
791
  @enclosures << enclosure
797
792
  end
798
793
 
799
- # Parse atom-type enclosures. If there are repeats of the same enclosure object,
800
- # we merge the two together.
794
+ # Parse atom-type enclosures. If there are repeats of the same
795
+ # enclosure object, we merge the two together.
801
796
  for enclosure_node in atom_enclosures
802
- enclosure_url = FeedTools.unescape_entities(enclosure_node.attributes["href"].to_s)
797
+ enclosure_url = FeedTools.unescape_entities(
798
+ enclosure_node.attributes["href"].to_s)
803
799
  enclosure = nil
804
800
  new_enclosure = false
805
801
  for existing_enclosure in @enclosures
@@ -822,13 +818,15 @@ module FeedTools
822
818
  end
823
819
  end
824
820
 
825
- # Creates an anonymous method to parse content objects from the media module. We
826
- # do this to avoid excessive duplication of code since we have to do identical
827
- # processing for content objects within group objects.
821
+ # Creates an anonymous method to parse content objects from the media
822
+ # module. We do this to avoid excessive duplication of code since we
823
+ # have to do identical processing for content objects within group
824
+ # objects.
828
825
  parse_media_content = lambda do |media_content_nodes|
829
826
  affected_enclosures = []
830
827
  for enclosure_node in media_content_nodes
831
- enclosure_url = FeedTools.unescape_entities(enclosure_node.attributes["url"].to_s)
828
+ enclosure_url = FeedTools.unescape_entities(
829
+ enclosure_node.attributes["url"].to_s)
832
830
  enclosure = nil
833
831
  new_enclosure = false
834
832
  for existing_enclosure in @enclosures
@@ -849,73 +847,84 @@ module FeedTools
849
847
  enclosure.width = enclosure_node.attributes["width"].to_i
850
848
  enclosure.bitrate = enclosure_node.attributes["bitrate"].to_i
851
849
  enclosure.framerate = enclosure_node.attributes["framerate"].to_i
852
- enclosure.expression = enclosure_node.attributes["expression"].to_s
850
+ enclosure.expression =
851
+ enclosure_node.attributes["expression"].to_s
853
852
  enclosure.is_default =
854
853
  (enclosure_node.attributes["isDefault"].to_s.downcase == "true")
855
- if XPath.first(enclosure_node, "media:thumbnail/@url").to_s != ""
854
+ enclosure_thumbnail_url = try_xpaths(enclosure_node,
855
+ ["media:thumbnail/@url"], :select_result_value => true)
856
+ if !enclosure_thumbnail_url.blank?
856
857
  enclosure.thumbnail = EnclosureThumbnail.new(
857
- FeedTools.unescape_entities(XPath.first(enclosure_node, "media:thumbnail/@url").to_s),
858
- FeedTools.unescape_entities(XPath.first(enclosure_node, "media:thumbnail/@height").to_s),
859
- FeedTools.unescape_entities(XPath.first(enclosure_node, "media:thumbnail/@width").to_s)
858
+ FeedTools.unescape_entities(enclosure_thumbnail_url),
859
+ FeedTools.unescape_entities(
860
+ try_xpaths(enclosure_node, ["media:thumbnail/@height"],
861
+ :select_result_value => true)),
862
+ FeedTools.unescape_entities(
863
+ try_xpaths(enclosure_node, ["media:thumbnail/@width"],
864
+ :select_result_value => true))
860
865
  )
861
- if enclosure.thumbnail.height == ""
862
- enclosure.thumbnail.height = nil
863
- end
864
- if enclosure.thumbnail.width == ""
865
- enclosure.thumbnail.width = nil
866
- end
867
866
  end
868
867
  enclosure.categories = []
869
- for category in XPath.match(enclosure_node, "media:category")
868
+ for category in try_xpaths_all(enclosure_node, ["media:category"])
870
869
  enclosure.categories << FeedTools::Feed::Category.new
871
870
  enclosure.categories.last.term =
872
- FeedTools.unescape_entities(category.text)
871
+ FeedTools.unescape_entities(category.inner_xml)
873
872
  enclosure.categories.last.scheme =
874
- FeedTools.unescape_entities(category.attributes["scheme"].to_s)
873
+ FeedTools.unescape_entities(
874
+ category.attributes["scheme"].to_s)
875
875
  enclosure.categories.last.label =
876
- FeedTools.unescape_entities(category.attributes["label"].to_s)
877
- if enclosure.categories.last.scheme == ""
876
+ FeedTools.unescape_entities(
877
+ category.attributes["label"].to_s)
878
+ if enclosure.categories.last.scheme.blank?
878
879
  enclosure.categories.last.scheme = nil
879
880
  end
880
- if enclosure.categories.last.label == ""
881
+ if enclosure.categories.last.label.blank?
881
882
  enclosure.categories.last.label = nil
882
883
  end
883
884
  end
884
- if XPath.first(enclosure_node, "media:hash/text()").to_s != ""
885
+ enclosure_media_hash = try_xpaths(enclosure_node,
886
+ ["media:hash/text()"], :select_result_value => true)
887
+ if !enclosure_media_hash.nil?
885
888
  enclosure.hash = EnclosureHash.new(
886
- FeedTools.sanitize_html(FeedTools.unescape_entities(XPath.first(
887
- enclosure_node, "media:hash/text()").to_s), :strip),
889
+ FeedTools.sanitize_html(FeedTools.unescape_entities(
890
+ enclosure_media_hash), :strip),
888
891
  "md5"
889
892
  )
890
893
  end
891
- if XPath.first(enclosure_node, "media:player/@url").to_s != ""
894
+ enclosure_media_player_url = try_xpaths(enclosure_node,
895
+ ["media:player/@url"], :select_result_value => true)
896
+ if !enclosure_media_player_url.blank?
892
897
  enclosure.player = EnclosurePlayer.new(
893
- FeedTools.unescape_entities(XPath.first(enclosure_node, "media:player/@url").to_s),
894
- FeedTools.unescape_entities(XPath.first(enclosure_node, "media:player/@height").to_s),
895
- FeedTools.unescape_entities(XPath.first(enclosure_node, "media:player/@width").to_s)
898
+ FeedTools.unescape_entities(enclosure_media_player_url),
899
+ FeedTools.unescape_entities(
900
+ try_xpaths(enclosure_node,
901
+ ["media:player/@height"], :select_result_value => true)),
902
+ FeedTools.unescape_entities(
903
+ try_xpaths(enclosure_node,
904
+ ["media:player/@width"], :select_result_value => true))
896
905
  )
897
- if enclosure.player.height == ""
898
- enclosure.player.height = nil
899
- end
900
- if enclosure.player.width == ""
901
- enclosure.player.width = nil
902
- end
903
906
  end
904
907
  enclosure.credits = []
905
- for credit in XPath.match(enclosure_node, "media:credit")
908
+ for credit in try_xpaths_all(enclosure_node, ["media:credit"])
906
909
  enclosure.credits << EnclosureCredit.new(
907
- FeedTools.unescape_entities(credit.text),
908
- FeedTools.unescape_entities(credit.attributes["role"].to_s.downcase)
910
+ FeedTools.unescape_entities(credit.inner_xml.to_s.strip),
911
+ FeedTools.unescape_entities(
912
+ credit.attributes["role"].to_s.downcase)
909
913
  )
910
- if enclosure.credits.last.role == ""
914
+ if enclosure.credits.last.name.blank?
915
+ enclosure.credits.last.name = nil
916
+ end
917
+ if enclosure.credits.last.role.blank?
911
918
  enclosure.credits.last.role = nil
912
919
  end
913
920
  end
914
- enclosure.explicit = (XPath.first(enclosure_node,
915
- "media:adult/text()").to_s.downcase == "true")
916
- if XPath.first(enclosure_node, "media:text/text()").to_s != ""
917
- enclosure.text = FeedTools.unescape_entities(XPath.first(enclosure_node,
918
- "media:text/text()").to_s)
921
+ enclosure.explicit = (try_xpaths(enclosure_node,
922
+ ["media:adult/text()"]).to_s.downcase == "true")
923
+ enclosure_media_text =
924
+ try_xpaths(enclosure_node, ["media:text/text()"])
925
+ if !enclosure_media_text.blank?
926
+ enclosure.text = FeedTools.unescape_entities(
927
+ enclosure_media_text)
919
928
  end
920
929
  affected_enclosures << enclosure
921
930
  if new_enclosure
@@ -933,7 +942,7 @@ module FeedTools
933
942
  # Parse the group objects.
934
943
  for media_group in media_group_enclosures
935
944
  group_media_content_enclosures =
936
- XPath.match(media_group, "media:content")
945
+ try_xpaths_all(media_group, ["media:content"])
937
946
 
938
947
  # Parse the content objects within the group objects.
939
948
  affected_enclosures =
@@ -942,82 +951,87 @@ module FeedTools
942
951
  # Now make sure that content objects inherit certain properties from
943
952
  # the group objects.
944
953
  for enclosure in affected_enclosures
945
- if enclosure.thumbnail.nil? &&
946
- XPath.first(media_group, "media:thumbnail/@url").to_s != ""
954
+ media_group_thumbnail = try_xpaths(media_group,
955
+ ["media:thumbnail/@url"], :select_result_value => true)
956
+ if enclosure.thumbnail.nil? && !media_group_thumbnail.blank?
947
957
  enclosure.thumbnail = EnclosureThumbnail.new(
948
958
  FeedTools.unescape_entities(
949
- XPath.first(media_group, "media:thumbnail/@url").to_s),
959
+ media_group_thumbnail),
950
960
  FeedTools.unescape_entities(
951
- XPath.first(media_group, "media:thumbnail/@height").to_s),
961
+ try_xpaths(media_group, ["media:thumbnail/@height"],
962
+ :select_result_value => true)),
952
963
  FeedTools.unescape_entities(
953
- XPath.first(media_group, "media:thumbnail/@width").to_s)
964
+ try_xpaths(media_group, ["media:thumbnail/@width"],
965
+ :select_result_value => true))
954
966
  )
955
- if enclosure.thumbnail.height == ""
956
- enclosure.thumbnail.height = nil
957
- end
958
- if enclosure.thumbnail.width == ""
959
- enclosure.thumbnail.width = nil
960
- end
961
967
  end
962
- if (enclosure.categories.nil? || enclosure.categories.size == 0)
968
+ if (enclosure.categories.blank?)
963
969
  enclosure.categories = []
964
- for category in XPath.match(media_group, "media:category")
970
+ for category in try_xpaths_all(media_group, ["media:category"])
965
971
  enclosure.categories << FeedTools::Feed::Category.new
966
972
  enclosure.categories.last.term =
967
- FeedTools.unescape_entities(category.text)
973
+ FeedTools.unescape_entities(category.inner_xml)
968
974
  enclosure.categories.last.scheme =
969
- FeedTools.unescape_entities(category.attributes["scheme"].to_s)
975
+ FeedTools.unescape_entities(
976
+ category.attributes["scheme"].to_s)
970
977
  enclosure.categories.last.label =
971
- FeedTools.unescape_entities(category.attributes["label"].to_s)
972
- if enclosure.categories.last.scheme == ""
978
+ FeedTools.unescape_entities(
979
+ category.attributes["label"].to_s)
980
+ if enclosure.categories.last.scheme.blank?
973
981
  enclosure.categories.last.scheme = nil
974
982
  end
975
- if enclosure.categories.last.label == ""
983
+ if enclosure.categories.last.label.blank?
976
984
  enclosure.categories.last.label = nil
977
985
  end
978
986
  end
979
987
  end
980
- if enclosure.hash.nil? &&
981
- XPath.first(media_group, "media:hash/text()").to_s != ""
988
+ enclosure_media_group_hash = try_xpaths(enclosure_node,
989
+ ["media:hash/text()"], :select_result_value => true)
990
+ if enclosure.hash.nil? && !enclosure_media_group_hash.blank?
982
991
  enclosure.hash = EnclosureHash.new(
983
- FeedTools.unescape_entities(XPath.first(media_group, "media:hash/text()").to_s),
992
+ FeedTools.sanitize_html(FeedTools.unescape_entities(
993
+ enclosure_media_group_hash), :strip),
984
994
  "md5"
985
995
  )
986
996
  end
987
- if enclosure.player.nil? &&
988
- XPath.first(media_group, "media:player/@url").to_s != ""
997
+ enclosure_media_group_url = try_xpaths(media_group,
998
+ "media:player/@url", :select_result_value => true)
999
+ if enclosure.player.nil? && !enclosure_media_group_url.blank?
989
1000
  enclosure.player = EnclosurePlayer.new(
990
- FeedTools.unescape_entities(XPath.first(media_group, "media:player/@url").to_s),
991
- FeedTools.unescape_entities(XPath.first(media_group, "media:player/@height").to_s),
992
- FeedTools.unescape_entities(XPath.first(media_group, "media:player/@width").to_s)
1001
+ FeedTools.unescape_entities(enclosure_media_group_url),
1002
+ FeedTools.unescape_entities(
1003
+ try_xpaths(media_group, ["media:player/@height"],
1004
+ :select_result_value => true)),
1005
+ FeedTools.unescape_entities(
1006
+ try_xpaths(media_group, ["media:player/@width"],
1007
+ :select_result_value => true))
993
1008
  )
994
- if enclosure.player.height == ""
995
- enclosure.player.height = nil
996
- end
997
- if enclosure.player.width == ""
998
- enclosure.player.width = nil
999
- end
1000
1009
  end
1001
1010
  if enclosure.credits.nil? || enclosure.credits.size == 0
1002
1011
  enclosure.credits = []
1003
- for credit in XPath.match(media_group, "media:credit")
1012
+ for credit in try_xpaths_all(media_group, ["media:credit"])
1004
1013
  enclosure.credits << EnclosureCredit.new(
1005
- FeedTools.unescape_entities(credit.text),
1006
- FeedTools.unescape_entities(credit.attributes["role"].to_s.downcase)
1014
+ FeedTools.unescape_entities(credit.inner_xml),
1015
+ FeedTools.unescape_entities(
1016
+ credit.attributes["role"].to_s.downcase)
1007
1017
  )
1008
- if enclosure.credits.last.role == ""
1018
+ if enclosure.credits.last.role.blank?
1009
1019
  enclosure.credits.last.role = nil
1010
1020
  end
1011
1021
  end
1012
1022
  end
1013
1023
  if enclosure.explicit?.nil?
1014
- enclosure.explicit = (XPath.first(media_group,
1015
- "media:adult/text()").to_s.downcase == "true") ? true : false
1024
+ enclosure.explicit = ((try_xpaths(media_group, [
1025
+ "media:adult/text()"
1026
+ ], :select_result_value => true).downcase == "true") ?
1027
+ true : false)
1016
1028
  end
1017
- if enclosure.text.nil? &&
1018
- XPath.first(media_group, "media:text/text()").to_s != ""
1019
- enclosure.text = FeedTools.sanitize_html(FeedTools.unescape_entities(
1020
- XPath.first(media_group, "media:text/text()").to_s), :strip)
1029
+ enclosure_media_group_text = try_xpaths(media_group,
1030
+ ["media:text/text()"], :select_result_value => true)
1031
+ if enclosure.text.nil? && !enclosure_media_group_text.blank?
1032
+ enclosure.text = FeedTools.sanitize_html(
1033
+ FeedTools.unescape_entities(
1034
+ enclosure_media_group_text), :strip)
1021
1035
  end
1022
1036
  end
1023
1037
 
@@ -1033,10 +1047,14 @@ module FeedTools
1033
1047
  end
1034
1048
 
1035
1049
  # Add all the itunes categories
1036
- for itunes_category in XPath.match(root_node, "itunes:category")
1050
+ itunes_categories =
1051
+ try_xpaths_all(self.root_node, ["itunes:category"])
1052
+ for itunes_category in itunes_categories
1037
1053
  genre = "Podcasts"
1038
1054
  category = itunes_category.attributes["text"].to_s
1039
- subcategory = XPath.first(itunes_category, "itunes:category/@text").to_s
1055
+ subcategory =
1056
+ try_xpaths(itunes_category, ["itunes:category/@text"],
1057
+ :select_result_value => true)
1040
1058
  category_path = genre
1041
1059
  if category != ""
1042
1060
  category_path << "/" + category
@@ -1061,7 +1079,7 @@ module FeedTools
1061
1079
  for enclosure in @enclosures
1062
1080
  # Clean up any of those attributes that incorrectly have ""
1063
1081
  # or 0 as their values
1064
- if enclosure.type == ""
1082
+ if enclosure.type.blank?
1065
1083
  enclosure.type = nil
1066
1084
  end
1067
1085
  if enclosure.file_size == 0
@@ -1082,12 +1100,13 @@ module FeedTools
1082
1100
  if enclosure.framerate == 0
1083
1101
  enclosure.framerate = nil
1084
1102
  end
1085
- if enclosure.expression == "" || enclosure.expression.nil?
1103
+ if enclosure.expression.blank?
1086
1104
  enclosure.expression = "full"
1087
1105
  end
1088
1106
 
1089
- # If an enclosure is missing the text field, fall back on the itunes:summary field
1090
- if enclosure.text.nil? || enclosure.text = ""
1107
+ # If an enclosure is missing the text field, fall back on the
1108
+ # itunes:summary field
1109
+ if enclosure.text.blank?
1091
1110
  enclosure.text = self.itunes_summary
1092
1111
  end
1093
1112
 
@@ -1128,8 +1147,8 @@ module FeedTools
1128
1147
  end
1129
1148
  end
1130
1149
 
1131
- # If we have a single enclosure, it's safe to inherit the itunes:duration field
1132
- # if it's missing.
1150
+ # If we have a single enclosure, it's safe to inherit the
1151
+ # itunes:duration field if it's missing.
1133
1152
  if @enclosures.size == 1
1134
1153
  if @enclosures.first.duration.nil? || @enclosures.first.duration == 0
1135
1154
  @enclosures.first.duration = self.itunes_duration
@@ -1147,48 +1166,29 @@ module FeedTools
1147
1166
  def author
1148
1167
  if @author.nil?
1149
1168
  @author = FeedTools::Feed::Author.new
1150
- unless root_node.nil?
1151
- author_node = XPath.first(root_node, "atom10:author",
1152
- FEED_TOOLS_NAMESPACES)
1153
- if author_node.nil?
1154
- author_node = XPath.first(root_node, "atom03:author",
1155
- FEED_TOOLS_NAMESPACES)
1156
- end
1157
- if author_node.nil?
1158
- author_node = XPath.first(root_node, "atom:author")
1159
- end
1160
- if author_node.nil?
1161
- author_node = XPath.first(root_node, "author")
1162
- end
1163
- if author_node.nil?
1164
- author_node = XPath.first(root_node, "managingEditor")
1165
- end
1166
- if author_node.nil?
1167
- author_node = XPath.first(root_node, "dc:author",
1168
- FEED_TOOLS_NAMESPACES)
1169
- end
1170
- if author_node.nil?
1171
- author_node = XPath.first(root_node, "dc:author")
1172
- end
1173
- if author_node.nil?
1174
- author_node = XPath.first(root_node, "dc:creator",
1175
- FEED_TOOLS_NAMESPACES)
1176
- end
1177
- if author_node.nil?
1178
- author_node = XPath.first(root_node, "dc:creator")
1179
- end
1180
- end
1169
+ author_node = try_xpaths(self.root_node, [
1170
+ "atom10:author",
1171
+ "atom03:author",
1172
+ "atom:author",
1173
+ "author",
1174
+ "managingEditor",
1175
+ "dc:author",
1176
+ "dc:creator",
1177
+ "creator"
1178
+ ])
1181
1179
  unless author_node.nil?
1182
1180
  @author.raw = FeedTools.unescape_entities(
1183
- XPath.first(author_node, "text()").to_s)
1184
- @author.raw = nil if @author.raw == ""
1181
+ XPath.first(author_node, "text()").to_s).strip
1182
+ @author.raw = nil if @author.raw.blank?
1185
1183
  unless @author.raw.nil?
1186
1184
  raw_scan = @author.raw.scan(
1187
1185
  /(.*)\((\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\)/i)
1188
1186
  if raw_scan.nil? || raw_scan.size == 0
1189
1187
  raw_scan = @author.raw.scan(
1190
1188
  /(\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\s*\((.*)\)/i)
1191
- author_raw_pair = raw_scan.first.reverse unless raw_scan.size == 0
1189
+ unless raw_scan.size == 0
1190
+ author_raw_pair = raw_scan.first.reverse
1191
+ end
1192
1192
  else
1193
1193
  author_raw_pair = raw_scan.first
1194
1194
  end
@@ -1205,42 +1205,44 @@ module FeedTools
1205
1205
  else
1206
1206
  unless @author.raw.include?("@")
1207
1207
  # We can be reasonably sure we are looking at something
1208
- # that the creator didn't intend to contain an email address if
1209
- # it got through the preceeding regexes and it doesn't
1208
+ # that the creator didn't intend to contain an email address
1209
+ # if it got through the preceeding regexes and it doesn't
1210
1210
  # contain the tell-tale '@' symbol.
1211
1211
  @author.name = @author.raw
1212
1212
  end
1213
1213
  end
1214
1214
  end
1215
- @author.name = "" if @author.name.nil?
1216
- if @author.name == ""
1215
+ if @author.name.blank?
1217
1216
  @author.name = FeedTools.unescape_entities(
1218
- XPath.first(author_node, "name/text()").to_s)
1217
+ try_xpaths(author_node, [
1218
+ "name/text()",
1219
+ "@name"
1220
+ ], :select_result_value => true)
1221
+ )
1219
1222
  end
1220
- if @author.name == ""
1221
- @author.name = FeedTools.unescape_entities(
1222
- XPath.first(author_node, "@name").to_s)
1223
- end
1224
- if @author.email == ""
1225
- @author.email = FeedTools.unescape_entities(
1226
- XPath.first(author_node, "email/text()").to_s)
1227
- end
1228
- if @author.email == ""
1223
+ if @author.email.blank?
1229
1224
  @author.email = FeedTools.unescape_entities(
1230
- XPath.first(author_node, "@email").to_s)
1231
- end
1232
- if @author.url == ""
1233
- @author.url = FeedTools.unescape_entities(
1234
- XPath.first(author_node, "url/text()").to_s)
1225
+ try_xpaths(author_node, [
1226
+ "email/text()",
1227
+ "@email"
1228
+ ], :select_result_value => true)
1229
+ )
1235
1230
  end
1236
- if @author.url == ""
1231
+ if @author.url.blank?
1237
1232
  @author.url = FeedTools.unescape_entities(
1238
- XPath.first(author_node, "@url").to_s)
1239
- end
1240
- @author.name = nil if @author.name == ""
1241
- @author.raw = nil if @author.raw == ""
1242
- @author.email = nil if @author.email == ""
1243
- @author.url = nil if @author.url == ""
1233
+ try_xpaths(author_node, [
1234
+ "url/text()",
1235
+ "uri/text()",
1236
+ "@url",
1237
+ "@uri",
1238
+ "@href"
1239
+ ], :select_result_value => true)
1240
+ )
1241
+ end
1242
+ @author.name = nil if @author.name.blank?
1243
+ @author.raw = nil if @author.raw.blank?
1244
+ @author.email = nil if @author.email.blank?
1245
+ @author.url = nil if @author.url.blank?
1244
1246
  end
1245
1247
  # Fallback on the itunes module if we didn't find an author name
1246
1248
  begin
@@ -1276,12 +1278,11 @@ module FeedTools
1276
1278
 
1277
1279
  # Set the author name
1278
1280
  @publisher.raw = FeedTools.unescape_entities(
1279
- XPath.first(root_node, "dc:publisher/text()").to_s)
1280
- if @publisher.raw == ""
1281
- @publisher.raw = FeedTools.unescape_entities(
1282
- XPath.first(root_node, "webMaster/text()").to_s)
1283
- end
1284
- unless @publisher.raw == ""
1281
+ try_xpaths(self.root_node, [
1282
+ "dc:publisher/text()",
1283
+ "webMaster/text()"
1284
+ ], :select_result_value => true))
1285
+ unless @publisher.raw.blank?
1285
1286
  raw_scan = @publisher.raw.scan(
1286
1287
  /(.*)\((\b[A-Z0-9._%-\+]+@[A-Z0-9._%-]+\.[A-Z]{2,4}\b)\)/i)
1287
1288
  if raw_scan.nil? || raw_scan.size == 0
@@ -1314,10 +1315,10 @@ module FeedTools
1314
1315
  end
1315
1316
  end
1316
1317
 
1317
- @publisher.name = nil if @publisher.name == ""
1318
- @publisher.raw = nil if @publisher.raw == ""
1319
- @publisher.email = nil if @publisher.email == ""
1320
- @publisher.url = nil if @publisher.url == ""
1318
+ @publisher.name = nil if @publisher.name.blank?
1319
+ @publisher.raw = nil if @publisher.raw.blank?
1320
+ @publisher.email = nil if @publisher.email.blank?
1321
+ @publisher.url = nil if @publisher.url.blank?
1321
1322
  end
1322
1323
  return @publisher
1323
1324
  end
@@ -1345,10 +1346,10 @@ module FeedTools
1345
1346
  # elements. They're actually amazingly common. People don't read specs.
1346
1347
  def itunes_author
1347
1348
  if @itunes_author.nil?
1348
- @itunes_author = FeedTools.unescape_entities(XPath.first(root_node,
1349
- "itunes:author/text()").to_s)
1350
- @itunes_author = feed.itunes_author if @itunes_author == ""
1351
- @itunes_author = nil if @itunes_author == ""
1349
+ @itunes_author = FeedTools.unescape_entities(
1350
+ try_xpaths(self.root_node,
1351
+ ["itunes:author/text()"], :select_result_value => true))
1352
+ @itunes_author = feed.itunes_author if @itunes_author.blank?
1352
1353
  end
1353
1354
  return @itunes_author
1354
1355
  end
@@ -1361,14 +1362,15 @@ module FeedTools
1361
1362
  # Returns the number of seconds that the associated media runs for
1362
1363
  def itunes_duration
1363
1364
  if @itunes_duration.nil?
1364
- raw_duration = FeedTools.unescape_entities(XPath.first(root_node,
1365
- "itunes:duration/text()").to_s)
1366
- if raw_duration != ""
1365
+ raw_duration = FeedTools.unescape_entities(
1366
+ try_xpaths(self.root_node,
1367
+ ["itunes:duration/text()"], :select_result_value => true))
1368
+ if !raw_duration.blank?
1367
1369
  hms = raw_duration.split(":").map { |x| x.to_i }
1368
1370
  if hms.size == 3
1369
- @itunes_duration = hms[0].hour + hms[1].minute + hms[2]
1371
+ @itunes_duration = hms[0].hours + hms[1].minutes + hms[2]
1370
1372
  elsif hms.size == 2
1371
- @itunes_duration = hms[0].minute + hms[1]
1373
+ @itunes_duration = hms[0].minutes + hms[1]
1372
1374
  elsif hms.size == 1
1373
1375
  @itunes_duration = hms[0]
1374
1376
  end
@@ -1388,39 +1390,51 @@ module FeedTools
1388
1390
  options.keys)
1389
1391
  options = { :estimate_timestamp => true }.merge(options)
1390
1392
  if @time.nil?
1391
- unless root_node.nil?
1392
- time_string = XPath.first(root_node, "pubDate/text()").to_s
1393
- if time_string == ""
1394
- time_string = XPath.first(root_node, "dc:date/text()").to_s
1395
- end
1396
- if time_string == ""
1397
- time_string = XPath.first(root_node, "issued/text()").to_s
1398
- end
1399
- if time_string == ""
1400
- time_string = XPath.first(root_node, "updated/text()").to_s
1401
- end
1402
- if time_string == ""
1403
- time_string = XPath.first(root_node, "time/text()").to_s
1404
- end
1405
- end
1393
+ time_string = try_xpaths(self.root_node, [
1394
+ "atom10:updated/text()",
1395
+ "atom03:updated/text()",
1396
+ "atom:updated/text()",
1397
+ "updated/text()",
1398
+ "atom10:modified/text()",
1399
+ "atom03:modified/text()",
1400
+ "atom:modified/text()",
1401
+ "modified/text()",
1402
+ "time/text()",
1403
+ "atom10:issued/text()",
1404
+ "atom03:issued/text()",
1405
+ "atom:issued/text()",
1406
+ "issued/text()",
1407
+ "atom10:published/text()",
1408
+ "atom03:published/text()",
1409
+ "atom:published/text()",
1410
+ "published/text()",
1411
+ "pubDate/text()",
1412
+ "dc:date/text()",
1413
+ "date/text()"
1414
+ ], :select_result_value => true)
1406
1415
  begin
1407
- time_string = "" if time_string.nil?
1408
- if time_string != ""
1416
+ if !time_string.blank?
1409
1417
  @time = Time.parse(time_string).gmtime
1418
+ elsif FeedTools.configurations[:timestamp_estimation_enabled] &&
1419
+ !self.title.nil? &&
1420
+ (Time.parse(self.title) - Time.now).abs > 100
1421
+ @time = Time.parse(self.title).gmtime
1410
1422
  end
1411
1423
  rescue
1412
1424
  end
1413
- if options[:estimate_timestamp]
1414
- if @time.nil?
1415
- begin
1416
- @time = succ_time
1425
+ if FeedTools.configurations[:timestamp_estimation_enabled]
1426
+ if options[:estimate_timestamp]
1427
+ if @time.nil?
1428
+ begin
1429
+ @time = succ_time
1430
+ if @time.nil?
1431
+ @time = prev_time
1432
+ end
1433
+ rescue
1434
+ end
1417
1435
  if @time.nil?
1418
- @time = prev_time
1436
+ @time = Time.now.gmtime
1419
1437
  end
1420
- rescue
1421
- end
1422
- if @time.nil?
1423
- @time = Time.now.gmtime
1424
1438
  end
1425
1439
  end
1426
1440
  end
@@ -1440,10 +1454,10 @@ module FeedTools
1440
1454
  if parent_feed.nil?
1441
1455
  return nil
1442
1456
  end
1443
- if parent_feed.instance_variable_get("@items").nil?
1457
+ if parent_feed.instance_variable_get("@entries").nil?
1444
1458
  parent_feed.items
1445
1459
  end
1446
- unsorted_items = parent_feed.instance_variable_get("@items")
1460
+ unsorted_items = parent_feed.instance_variable_get("@entries")
1447
1461
  item_index = unsorted_items.index(self)
1448
1462
  if item_index.nil?
1449
1463
  return nil
@@ -1457,7 +1471,7 @@ module FeedTools
1457
1471
  return nil
1458
1472
  end
1459
1473
  end
1460
- #private :succ_time
1474
+ private :succ_time
1461
1475
 
1462
1476
  # Returns 1 second before the succeeding item's time.
1463
1477
  def prev_time #:nodoc:
@@ -1466,10 +1480,10 @@ module FeedTools
1466
1480
  if parent_feed.nil?
1467
1481
  return nil
1468
1482
  end
1469
- if parent_feed.instance_variable_get("@items").nil?
1483
+ if parent_feed.instance_variable_get("@entries").nil?
1470
1484
  parent_feed.items
1471
1485
  end
1472
- unsorted_items = parent_feed.instance_variable_get("@items")
1486
+ unsorted_items = parent_feed.instance_variable_get("@entries")
1473
1487
  item_index = unsorted_items.index(self)
1474
1488
  if item_index.nil?
1475
1489
  return nil
@@ -1483,18 +1497,22 @@ module FeedTools
1483
1497
  return nil
1484
1498
  end
1485
1499
  end
1486
- #private :prev_time
1487
-
1500
+ private :prev_time
1501
+
1488
1502
  # Returns the feed item updated time
1489
1503
  def updated
1490
1504
  if @updated.nil?
1491
- unless root_node.nil?
1492
- updated_string = XPath.first(root_node, "updated/text()").to_s
1493
- if updated_string == ""
1494
- updated_string = XPath.first(root_node, "modified/text()").to_s
1495
- end
1496
- end
1497
- if updated_string != nil && updated_string != ""
1505
+ updated_string = try_xpaths(self.root_node, [
1506
+ "atom10:updated/text()",
1507
+ "atom03:updated/text()",
1508
+ "atom:updated/text()",
1509
+ "updated/text()",
1510
+ "atom10:modified/text()",
1511
+ "atom03:modified/text()",
1512
+ "atom:modified/text()",
1513
+ "modified/text()"
1514
+ ], :select_result_value => true)
1515
+ if !updated_string.blank?
1498
1516
  @updated = Time.parse(updated_string).gmtime rescue nil
1499
1517
  else
1500
1518
  @updated = nil
@@ -1508,23 +1526,24 @@ module FeedTools
1508
1526
  @updated = new_updated
1509
1527
  end
1510
1528
 
1511
- # Returns the feed item issued time
1512
- def issued
1513
- if @issued.nil?
1514
- unless root_node.nil?
1515
- issued_string = XPath.first(root_node, "issued/text()").to_s
1516
- if issued_string == ""
1517
- issued_string = XPath.first(root_node, "published/text()").to_s
1518
- end
1519
- if issued_string == ""
1520
- issued_string = XPath.first(root_node, "pubDate/text()").to_s
1521
- end
1522
- if issued_string == ""
1523
- issued_string = XPath.first(root_node, "dc:date/text()").to_s
1524
- end
1525
- end
1526
- if issued_string != nil && issued_string != ""
1527
- @issued = Time.parse(issued_string).gmtime rescue nil
1529
+ # Returns the feed item published time
1530
+ def published
1531
+ if @published.nil?
1532
+ published_string = try_xpaths(self.root_node, [
1533
+ "atom10:issued/text()",
1534
+ "atom03:issued/text()",
1535
+ "atom:issued/text()",
1536
+ "issued/text()",
1537
+ "atom10:published/text()",
1538
+ "atom03:published/text()",
1539
+ "atom:published/text()",
1540
+ "published/text()",
1541
+ "pubDate/text()",
1542
+ "dc:date/text()",
1543
+ "date/text()"
1544
+ ], :select_result_value => true)
1545
+ if !published_string.blank?
1546
+ @issued = Time.parse(published_string).gmtime rescue nil
1528
1547
  else
1529
1548
  @issued = nil
1530
1549
  end
@@ -1532,17 +1551,19 @@ module FeedTools
1532
1551
  return @issued
1533
1552
  end
1534
1553
 
1535
- # Sets the feed item issued time
1536
- def issued=(new_issued)
1537
- @issued = new_issued
1554
+ # Sets the feed item published time
1555
+ def published=(new_published)
1556
+ @published = new_published
1538
1557
  end
1539
1558
 
1540
1559
  # Returns the url for posting comments
1541
1560
  def comments
1542
1561
  if @comments.nil?
1543
- @comments = FeedTools.normalize_url(
1544
- XPath.first(root_node, "comments/text()").to_s)
1545
- @comments = nil if @comments == ""
1562
+ @comments = try_xpaths(self.root_node, ["comments/text()"],
1563
+ :select_result_value => true)
1564
+ if FeedTools.configurations[:url_normalization_enabled]
1565
+ @comments = FeedTools.normalize_url(@comments)
1566
+ end
1546
1567
  end
1547
1568
  return @comments
1548
1569
  end
@@ -1556,10 +1577,10 @@ module FeedTools
1556
1577
  def source
1557
1578
  if @source.nil?
1558
1579
  @source = FeedTools::Feed::Link.new
1559
- @source.url = XPath.first(root_node, "source/@url").to_s
1560
- @source.url = nil if @source.url == ""
1561
- @source.value = XPath.first(root_node, "source/text()").to_s
1562
- @source.value = nil if @source.value == ""
1580
+ @source.url = try_xpaths(self.root_node, ["source/@url"],
1581
+ :select_result_value => true)
1582
+ @source.value = try_xpaths(self.root_node, ["source/text()"],
1583
+ :select_result_value => true)
1563
1584
  end
1564
1585
  return @source
1565
1586
  end
@@ -1575,26 +1596,26 @@ module FeedTools
1575
1596
  end
1576
1597
  if @tags.nil? || @tags.size == 0
1577
1598
  @tags = []
1578
- tag_list = XPath.match(root_node, "dc:subject/rdf:Bag/rdf:li/text()")
1579
- if tag_list.nil? || tag_list.size == 0
1580
- tag_list = XPath.match(root_node,
1581
- "dc:subject/rdf:Bag/rdf:li/text()", FEED_TOOLS_NAMESPACES)
1582
- end
1583
- if tag_list != nil && tag_list.size > 1
1599
+ tag_list = try_xpaths_all(self.root_node,
1600
+ ["dc:subject/rdf:Bag/rdf:li/text()"],
1601
+ :select_result_value => true)
1602
+ if tag_list != nil && tag_list.size > 0
1584
1603
  for tag in tag_list
1585
- @tags << tag.to_s.downcase.strip
1604
+ @tags << tag.downcase.strip
1586
1605
  end
1587
1606
  end
1588
1607
  end
1589
1608
  if @tags.nil? || @tags.size == 0
1590
1609
  # messy effort to find ourselves some tags, mainly for del.icio.us
1591
1610
  @tags = []
1592
- rdf_bag = XPath.match(root_node, "taxo:topics/rdf:Bag/rdf:li")
1611
+ rdf_bag = try_xpaths_all(self.root_node,
1612
+ ["taxo:topics/rdf:Bag/rdf:li"])
1593
1613
  if rdf_bag != nil && rdf_bag.size > 0
1594
1614
  for tag_node in rdf_bag
1595
1615
  begin
1596
- tag_url = XPath.first(root_node, "@resource").to_s
1597
- tag_match = tag_url.scan(/\/(tag|tags)\/(\w+)/)
1616
+ tag_url = try_xpaths(tag_node, ["@resource"],
1617
+ :select_result_value => true)
1618
+ tag_match = tag_url.scan(/\/(tag|tags)\/(\w+)$/)
1598
1619
  if tag_match.size > 0
1599
1620
  @tags << tag_match.first.last.downcase.strip
1600
1621
  end
@@ -1605,21 +1626,36 @@ module FeedTools
1605
1626
  end
1606
1627
  if @tags.nil? || @tags.size == 0
1607
1628
  @tags = []
1608
- tag_list = XPath.match(root_node, "category/text()")
1629
+ tag_list = try_xpaths_all(self.root_node, ["category/text()"],
1630
+ :select_result_value => true)
1609
1631
  for tag in tag_list
1610
1632
  @tags << tag.to_s.downcase.strip
1611
1633
  end
1612
1634
  end
1613
1635
  if @tags.nil? || @tags.size == 0
1614
1636
  @tags = []
1615
- tag_list = XPath.match(root_node, "dc:subject/text()")
1637
+ tag_list = try_xpaths_all(self.root_node, ["dc:subject/text()"],
1638
+ :select_result_value => true)
1616
1639
  for tag in tag_list
1617
1640
  @tags << tag.to_s.downcase.strip
1618
1641
  end
1619
1642
  end
1620
- if @tags.nil? || @tags.size == 0
1643
+ if @tags.blank?
1621
1644
  begin
1622
- @tags = XPath.first(root_node, "itunes:keywords/text()").to_s.downcase.split(" ")
1645
+ itunes_keywords_string = try_xpaths(self.root_node, [
1646
+ "itunes:keywords/text()"
1647
+ ], :select_result_value => true)
1648
+ unless itunes_keywords_string.blank?
1649
+ @tags = itunes_keywords_string.downcase.split(",")
1650
+ if @tags.size == 1
1651
+ @tags = itunes_keywords_string.downcase.split(" ")
1652
+ @tags = @tags.map { |tag| tag.chomp(",") }
1653
+ end
1654
+ if @tags.size == 1
1655
+ @tags = itunes_keywords_string.downcase.split(",")
1656
+ end
1657
+ @tags = @tags.map { |tag| tag.strip }
1658
+ end
1623
1659
  rescue
1624
1660
  @tags = []
1625
1661
  end
@@ -1642,12 +1678,11 @@ module FeedTools
1642
1678
  # isn't explicitly marked as explicit.
1643
1679
  def explicit?
1644
1680
  if @explicit.nil?
1645
- if XPath.first(root_node,
1646
- "media:adult/text()").to_s.downcase == "true" ||
1647
- XPath.first(root_node,
1648
- "itunes:explicit/text()").to_s.downcase == "yes" ||
1649
- XPath.first(root_node,
1650
- "itunes:explicit/text()").to_s.downcase == "true" ||
1681
+ explicit_string = try_xpaths(self.root_node, [
1682
+ "media:adult/text()",
1683
+ "itunes:explicit/text()"
1684
+ ], :select_result_value => true)
1685
+ if explicit_string == "true" || explicit_string == "yes" ||
1651
1686
  feed.explicit?
1652
1687
  @explicit = true
1653
1688
  else
@@ -1669,8 +1704,9 @@ module FeedTools
1669
1704
  end
1670
1705
 
1671
1706
  # Generates xml based on the content of the feed item
1672
- def build_xml(feed_type=(self.feed.feed_type or "rss"), version=nil,
1673
- xml_builder=Builder::XmlMarkup.new(:indent => 2))
1707
+ def build_xml(feed_type=(self.feed.feed_type or "atom"), version=nil,
1708
+ xml_builder=Builder::XmlMarkup.new(
1709
+ :indent => 2, :escape_attrs => false))
1674
1710
  if feed_type == "rss" && (version == nil || version == 0.0)
1675
1711
  version = 1.0
1676
1712
  elsif feed_type == "atom" && (version == nil || version == 0.0)
@@ -1681,7 +1717,8 @@ module FeedTools
1681
1717
  if link.nil?
1682
1718
  raise "Cannot generate an rdf-based feed item with a nil link field."
1683
1719
  end
1684
- return xml_builder.item("rdf:about" => CGI.escapeHTML(link)) do
1720
+ return xml_builder.item("rdf:about" =>
1721
+ FeedTools.escape_entities(link)) do
1685
1722
  unless title.nil? || title == ""
1686
1723
  xml_builder.title(title)
1687
1724
  else
@@ -1708,24 +1745,37 @@ module FeedTools
1708
1745
  end
1709
1746
  end
1710
1747
  end
1711
- xml_builder.tag!("itunes:keywords", tags.join(" "))
1748
+ if self.feed.podcast?
1749
+ xml_builder.tag!("itunes:keywords", tags.join(", "))
1750
+ end
1712
1751
  end
1713
1752
  build_xml_hook(feed_type, version, xml_builder)
1714
1753
  end
1715
1754
  elsif feed_type == "rss"
1716
1755
  # normal rss format
1717
1756
  return xml_builder.item do
1718
- unless title.nil? || title == ""
1719
- xml_builder.title(title)
1757
+ unless self.title.blank?
1758
+ xml_builder.title(self.title)
1720
1759
  end
1721
- unless link.nil? || link == ""
1760
+ unless self.link.blank?
1722
1761
  xml_builder.link(link)
1723
1762
  end
1724
- unless description.nil? || description == ""
1725
- xml_builder.description(description)
1763
+ unless self.description.blank?
1764
+ xml_builder.description(self.description)
1726
1765
  end
1727
- unless time.nil?
1728
- xml_builder.pubDate(time.rfc822)
1766
+ unless self.time.nil?
1767
+ xml_builder.pubDate(self.time.rfc822)
1768
+ end
1769
+ unless self.guid.blank?
1770
+ if FeedTools.is_uri?(self.guid)
1771
+ xml_builder.guid(self.guid, "isPermaLink" => "true")
1772
+ else
1773
+ xml_builder.guid(self.guid, "isPermaLink" => "false")
1774
+ end
1775
+ else
1776
+ unless self.link.blank?
1777
+ xml_builder.guid(self.link, "isPermaLink" => "true")
1778
+ end
1729
1779
  end
1730
1780
  unless tags.nil? || tags.size == 0
1731
1781
  xml_builder.tag!("taxo:topics") do
@@ -1735,53 +1785,51 @@ module FeedTools
1735
1785
  end
1736
1786
  end
1737
1787
  end
1738
- xml_builder.tag!("itunes:keywords", tags.join(" "))
1739
- end
1740
- build_xml_hook(feed_type, version, xml_builder)
1741
- end
1742
- elsif feed_type == "atom" && version == 0.3
1743
- # normal atom format
1744
- return xml_builder.entry("xmlns" =>
1745
- FEED_TOOLS_NAMESPACES['atom03']) do
1746
- unless title.nil? || title == ""
1747
- xml_builder.title(title,
1748
- "mode" => "escaped",
1749
- "type" => "text/html")
1750
- end
1751
- xml_builder.author do
1752
- unless self.author.nil? || self.author.name.nil?
1753
- xml_builder.name(self.author.name)
1754
- else
1755
- xml_builder.name("n/a")
1756
- end
1757
- unless self.author.nil? || self.author.email.nil?
1758
- xml_builder.email(self.author.email)
1759
- end
1760
- unless self.author.nil? || self.author.url.nil?
1761
- xml_builder.url(self.author.url)
1788
+ if self.feed.podcast?
1789
+ xml_builder.tag!("itunes:keywords", tags.join(", "))
1762
1790
  end
1763
1791
  end
1764
- unless link.nil? || link == ""
1765
- xml_builder.link("href" => link,
1766
- "rel" => "alternate",
1767
- "type" => "text/html",
1768
- "title" => title)
1769
- end
1770
- unless description.nil? || description == ""
1771
- xml_builder.content(description,
1772
- "mode" => "escaped",
1773
- "type" => "text/html")
1774
- end
1775
- unless time.nil?
1776
- xml_builder.issued(time.iso8601)
1777
- end
1778
- unless tags.nil? || tags.size == 0
1779
- for tag in tags
1780
- xml_builder.category(tag)
1792
+ unless self.enclosures.blank? || self.enclosures.size == 0
1793
+ for enclosure in self.enclosures
1794
+ attribute_hash = {}
1795
+ next if enclosure.url.blank?
1796
+ begin
1797
+ if enclosure.file_size.blank? || enclosure.file_size.to_i == 0
1798
+ # We can't use this enclosure because it's missing the
1799
+ # required file size. Check alternate versions for
1800
+ # file_size.
1801
+ if !enclosure.versions.blank? && enclosure.versions.size > 0
1802
+ for alternate in enclosure.versions
1803
+ if alternate.file_size != nil &&
1804
+ alternate.file_size.to_i > 0
1805
+ enclosure = alternate
1806
+ break
1807
+ end
1808
+ end
1809
+ end
1810
+ end
1811
+ rescue
1812
+ end
1813
+ attribute_hash["url"] = FeedTools.normalize_url(enclosure.url)
1814
+ if enclosure.type != nil
1815
+ attribute_hash["type"] = enclosure.type
1816
+ end
1817
+ if enclosure.file_size != nil && enclosure.file_size.to_i > 0
1818
+ attribute_hash["length"] = enclosure.file_size.to_s
1819
+ else
1820
+ # We couldn't find an alternate and the problem is still
1821
+ # there. Give up and go on.
1822
+ xml_builder.comment!(
1823
+ "*** Enclosure failed to include file size. Ignoring. ***")
1824
+ next
1825
+ end
1826
+ xml_builder.enclosure(attribute_hash)
1781
1827
  end
1782
1828
  end
1783
1829
  build_xml_hook(feed_type, version, xml_builder)
1784
1830
  end
1831
+ elsif feed_type == "atom" && version == 0.3
1832
+ raise "Atom 0.3 is obsolete."
1785
1833
  elsif feed_type == "atom" && version == 1.0
1786
1834
  # normal atom format
1787
1835
  return xml_builder.entry("xmlns" =>
@@ -1800,20 +1848,20 @@ module FeedTools
1800
1848
  xml_builder.email(self.author.email)
1801
1849
  end
1802
1850
  unless self.author.nil? || self.author.url.nil?
1803
- xml_builder.url(self.author.url)
1851
+ xml_builder.uri(self.author.url)
1804
1852
  end
1805
1853
  end
1806
1854
  unless link.nil? || link == ""
1807
- xml_builder.link("href" => link,
1855
+ xml_builder.link("href" => FeedTools.escape_entities(self.link),
1808
1856
  "rel" => "alternate",
1809
1857
  "type" => "text/html",
1810
- "title" => title)
1858
+ "title" => FeedTools.escape_entities(title))
1811
1859
  end
1812
- unless description.nil? || description == ""
1860
+ if !description.blank?
1813
1861
  xml_builder.content(description,
1814
1862
  "type" => "html")
1815
- else
1816
- xml_builder.content(FeedTools.no_content_string,
1863
+ elsif !FeedTools.configurations[:no_content_string].blank?
1864
+ xml_builder.content(FeedTools.configurations[:no_content_string],
1817
1865
  "type" => "html")
1818
1866
  end
1819
1867
  if self.updated != nil
@@ -1851,25 +1899,36 @@ module FeedTools
1851
1899
  xml_builder.category("term" => tag)
1852
1900
  end
1853
1901
  end
1902
+ unless self.enclosures.blank? || self.enclosures.size == 0
1903
+ for enclosure in self.enclosures
1904
+ attribute_hash = {}
1905
+ next if enclosure.url.blank?
1906
+ attribute_hash["rel"] = "enclosure"
1907
+ attribute_hash["href"] = FeedTools.normalize_url(enclosure.url)
1908
+ if enclosure.type != nil
1909
+ attribute_hash["type"] = enclosure.type
1910
+ end
1911
+ if enclosure.file_size != nil && enclosure.file_size.to_i > 0
1912
+ attribute_hash["length"] = enclosure.file_size.to_s
1913
+ end
1914
+ xml_builder.link(attribute_hash)
1915
+ end
1916
+ end
1854
1917
  build_xml_hook(feed_type, version, xml_builder)
1855
1918
  end
1919
+ else
1920
+ raise "Unsupported feed format/version."
1856
1921
  end
1857
1922
  end
1858
1923
 
1859
- alias_method :tagline, :description
1860
- alias_method :tagline=, :description=
1861
- alias_method :subtitle, :description
1862
- alias_method :subtitle=, :description=
1863
- alias_method :summary, :description
1864
- alias_method :summary=, :description=
1865
- alias_method :abstract, :description
1866
- alias_method :abstract=, :description=
1867
- alias_method :content, :description
1868
- alias_method :content=, :description=
1924
+ alias_method :summary, :content
1925
+ alias_method :summary=, :content=
1926
+ alias_method :abstract, :content
1927
+ alias_method :abstract=, :content=
1928
+ alias_method :description, :content
1929
+ alias_method :description=, :content=
1869
1930
  alias_method :guid, :id
1870
1931
  alias_method :guid=, :id=
1871
- alias_method :published, :issued
1872
- alias_method :published=, :issued=
1873
1932
 
1874
1933
  # Returns a simple representation of the feed item object's state.
1875
1934
  def inspect