feedtools 0.2.24 → 0.2.25

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.
@@ -1273,6 +1273,21 @@ module FeedTools
1273
1273
  ], :select_result_value => true)
1274
1274
  )
1275
1275
  end
1276
+ if @author.name.blank? && !@author.raw.blank? &&
1277
+ !@author.email.blank?
1278
+ name_scan = @author.raw.scan(
1279
+ /"?([^"]*)"? ?[\(<].*#{@author.email}.*[\)>].*/)
1280
+ if name_scan.flatten.size == 1
1281
+ @author.name = name_scan.flatten[0].strip
1282
+ end
1283
+ if @author.name.blank?
1284
+ name_scan = @author.raw.scan(
1285
+ /.*#{@author.email} ?[\(<]"?([^"]*)"?[\)>].*/)
1286
+ if name_scan.flatten.size == 1
1287
+ @author.name = name_scan.flatten[0].strip
1288
+ end
1289
+ end
1290
+ end
1276
1291
  @author.name = nil if @author.name.blank?
1277
1292
  @author.raw = nil if @author.raw.blank?
1278
1293
  @author.email = nil if @author.email.blank?
@@ -1839,11 +1854,15 @@ module FeedTools
1839
1854
  xml_builder.title(FeedTools::HtmlHelper.strip_html_tags(self.title))
1840
1855
  end
1841
1856
  unless self.link.blank?
1842
- xml_builder.link(link)
1857
+ xml_builder.link(self.link)
1843
1858
  end
1844
1859
  unless self.author.nil? || self.author.name.nil?
1845
1860
  xml_builder.tag!("dc:creator", self.author.name)
1846
1861
  end
1862
+ unless self.author.nil? || self.author.email.nil? ||
1863
+ self.author.name.nil?
1864
+ xml_builder.author("#{self.author.email} (#{self.author.name})")
1865
+ end
1847
1866
  unless self.summary.blank?
1848
1867
  xml_builder.description(self.summary)
1849
1868
  end
@@ -132,7 +132,9 @@ module FeedTools
132
132
  options[:form_data] = {} if options[:form_data].blank?
133
133
  request_params << options[:form_data]
134
134
  end
135
+ Thread.pass
135
136
  response = http.send(http_operation, *request_params)
137
+ Thread.pass
136
138
 
137
139
  case response
138
140
  when Net::HTTPSuccess
@@ -167,7 +169,7 @@ module FeedTools
167
169
 
168
170
  redirected_location = response['location']
169
171
  redirected_location = FeedTools::UriHelper.resolve_relative_uri(
170
- redirected_location, [uri.host])
172
+ redirected_location, [uri.to_s])
171
173
 
172
174
  if options[:response_chain].assoc(redirected_location) != nil
173
175
  raise FeedAccessError,
@@ -191,7 +193,7 @@ module FeedTools
191
193
  end
192
194
  rescue SocketError
193
195
  raise FeedAccessError, 'Socket error prevented feed retrieval'
194
- rescue Timeout::Error
196
+ rescue Timeout::Error, Errno::ETIMEDOUT
195
197
  raise FeedAccessError, 'Timeout while attempting to retrieve feed'
196
198
  rescue Errno::ENETUNREACH
197
199
  raise FeedAccessError, 'Network was unreachable'
@@ -23,11 +23,12 @@
23
23
 
24
24
  require 'feed_tools'
25
25
  require 'uri'
26
-
26
+
27
27
  module FeedTools
28
28
  # Generic url processing methods needed in numerous places throughout
29
29
  # FeedTools
30
30
  module UriHelper
31
+
31
32
  # Returns true if the idn module can be used.
32
33
  def self.idn_enabled?
33
34
  # This is an override variable to keep idn from being used even if it
@@ -0,0 +1,625 @@
1
+ require 'rexml/document'
2
+
3
+ module REXML # :nodoc:
4
+ class LiberalXPathParser < XPathParser # :nodoc:
5
+
6
+ private
7
+
8
+ # Monkey Patch for Ruby 1.8.4
9
+ def expr( path_stack, nodeset, context=nil )
10
+ #puts "#"*15
11
+ #puts "In expr with #{path_stack.inspect}"
12
+ #puts "Returning" if path_stack.length == 0 || nodeset.length == 0
13
+ node_types = ELEMENTS
14
+ return nodeset if path_stack.length == 0 || nodeset.length == 0
15
+ while path_stack.length > 0
16
+ #puts "Path stack = #{path_stack.inspect}"
17
+ #puts "Nodeset is #{nodeset.inspect}"
18
+ case (op = path_stack.shift)
19
+ when :document
20
+ nodeset = [ nodeset[0].root_node ]
21
+ #puts ":document, nodeset = #{nodeset.inspect}"
22
+
23
+ when :qname
24
+ #puts "IN QNAME"
25
+ prefix = path_stack.shift
26
+ name = path_stack.shift
27
+ ns = @namespaces[prefix]
28
+ ns = ns ? ns : ''
29
+ nodeset.delete_if do |node|
30
+ # FIXME: This DOUBLES the time XPath searches take
31
+ ns = node.namespace( prefix ) if node.node_type == :element and ns == ''
32
+ #puts "NS = #{ns.inspect}"
33
+ #puts "node.node_type == :element => #{node.node_type == :element}"
34
+ if node.node_type == :element
35
+ #puts "node.name == #{name} => #{node.name == name}"
36
+ if node.name.downcase == name.downcase
37
+ #puts "node.namespace == #{ns.inspect} => #{node.namespace == ns}"
38
+ end
39
+ end
40
+ !(node.node_type == :element and
41
+ node.name.downcase == name.downcase and
42
+ node.namespace == ns )
43
+ end
44
+ node_types = ELEMENTS
45
+
46
+ when :any
47
+ #puts "ANY 1: nodeset = #{nodeset.inspect}"
48
+ #puts "ANY 1: node_types = #{node_types.inspect}"
49
+ nodeset.delete_if { |node| !node_types.include?(node.node_type) }
50
+ #puts "ANY 2: nodeset = #{nodeset.inspect}"
51
+
52
+ when :self
53
+ # This space left intentionally blank
54
+
55
+ when :processing_instruction
56
+ target = path_stack.shift
57
+ nodeset.delete_if do |node|
58
+ (node.node_type != :processing_instruction) or
59
+ ( target!='' and ( node.target != target ) )
60
+ end
61
+
62
+ when :text
63
+ nodeset.delete_if { |node| node.node_type != :text }
64
+
65
+ when :comment
66
+ nodeset.delete_if { |node| node.node_type != :comment }
67
+
68
+ when :node
69
+ # This space left intentionally blank
70
+ node_types = ALL
71
+
72
+ when :child
73
+ new_nodeset = []
74
+ nt = nil
75
+ for node in nodeset
76
+ nt = node.node_type
77
+ new_nodeset += node.children if nt == :element or nt == :document
78
+ end
79
+ nodeset = new_nodeset
80
+ node_types = ELEMENTS
81
+
82
+ when :literal
83
+ literal = path_stack.shift
84
+ if literal =~ /^\d+(\.\d+)?$/
85
+ return ($1 ? literal.to_f : literal.to_i)
86
+ end
87
+ return literal
88
+
89
+ when :attribute
90
+ new_nodeset = []
91
+ case path_stack.shift
92
+ when :qname
93
+ prefix = path_stack.shift
94
+ name = path_stack.shift
95
+ for element in nodeset
96
+ if element.node_type == :element
97
+ for attribute_name in element.attributes.keys
98
+ if attribute_name.downcase == name.downcase
99
+ attrib = element.attribute( attribute_name,
100
+ @namespaces[prefix] )
101
+ new_nodeset << attrib if attrib
102
+ end
103
+ end
104
+ end
105
+ end
106
+ when :any
107
+ #puts "ANY"
108
+ for element in nodeset
109
+ if element.node_type == :element
110
+ new_nodeset += element.attributes.to_a
111
+ end
112
+ end
113
+ end
114
+ nodeset = new_nodeset
115
+
116
+ when :parent
117
+ #puts "PARENT 1: nodeset = #{nodeset}"
118
+ nodeset = nodeset.collect{|n| n.parent}.compact
119
+ #nodeset = expr(path_stack.dclone, nodeset.collect{|n| n.parent}.compact)
120
+ #puts "PARENT 2: nodeset = #{nodeset.inspect}"
121
+ node_types = ELEMENTS
122
+
123
+ when :ancestor
124
+ new_nodeset = []
125
+ for node in nodeset
126
+ while node.parent
127
+ node = node.parent
128
+ new_nodeset << node unless new_nodeset.include? node
129
+ end
130
+ end
131
+ nodeset = new_nodeset
132
+ node_types = ELEMENTS
133
+
134
+ when :ancestor_or_self
135
+ new_nodeset = []
136
+ for node in nodeset
137
+ if node.node_type == :element
138
+ new_nodeset << node
139
+ while ( node.parent )
140
+ node = node.parent
141
+ new_nodeset << node unless new_nodeset.include? node
142
+ end
143
+ end
144
+ end
145
+ nodeset = new_nodeset
146
+ node_types = ELEMENTS
147
+
148
+ when :predicate
149
+ new_nodeset = []
150
+ subcontext = { :size => nodeset.size }
151
+ pred = path_stack.shift
152
+ nodeset.each_with_index { |node, index|
153
+ subcontext[ :node ] = node
154
+ #puts "PREDICATE SETTING CONTEXT INDEX TO #{index+1}"
155
+ subcontext[ :index ] = index+1
156
+ pc = pred.dclone
157
+ #puts "#{node.hash}) Recursing with #{pred.inspect} and [#{node.inspect}]"
158
+ result = expr( pc, [node], subcontext )
159
+ result = result[0] if result.kind_of? Array and result.length == 1
160
+ #puts "#{node.hash}) Result = #{result.inspect} (#{result.class.name})"
161
+ if result.kind_of? Numeric
162
+ #puts "Adding node #{node.inspect}" if result == (index+1)
163
+ new_nodeset << node if result == (index+1)
164
+ elsif result.instance_of? Array
165
+ #puts "Adding node #{node.inspect}" if result.size > 0
166
+ new_nodeset << node if result.size > 0
167
+ else
168
+ #puts "Adding node #{node.inspect}" if result
169
+ new_nodeset << node if result
170
+ end
171
+ }
172
+ #puts "New nodeset = #{new_nodeset.inspect}"
173
+ #puts "Path_stack = #{path_stack.inspect}"
174
+ nodeset = new_nodeset
175
+ =begin
176
+ predicate = path_stack.shift
177
+ ns = nodeset.clone
178
+ result = expr( predicate, ns )
179
+ #puts "Result = #{result.inspect} (#{result.class.name})"
180
+ #puts "nodeset = #{nodeset.inspect}"
181
+ if result.kind_of? Array
182
+ nodeset = result.zip(ns).collect{|m,n| n if m}.compact
183
+ else
184
+ nodeset = result ? nodeset : []
185
+ end
186
+ #puts "Outgoing NS = #{nodeset.inspect}"
187
+ =end
188
+
189
+ when :descendant_or_self
190
+ rv = descendant_or_self( path_stack, nodeset )
191
+ path_stack.clear
192
+ nodeset = rv
193
+ node_types = ELEMENTS
194
+
195
+ when :descendant
196
+ results = []
197
+ nt = nil
198
+ for node in nodeset
199
+ nt = node.node_type
200
+ results += expr( path_stack.dclone.unshift( :descendant_or_self ),
201
+ node.children ) if nt == :element or nt == :document
202
+ end
203
+ nodeset = results
204
+ node_types = ELEMENTS
205
+
206
+ when :following_sibling
207
+ #puts "FOLLOWING_SIBLING 1: nodeset = #{nodeset}"
208
+ results = []
209
+ for node in nodeset
210
+ all_siblings = node.parent.children
211
+ current_index = all_siblings.index( node )
212
+ following_siblings = all_siblings[ current_index+1 .. -1 ]
213
+ results += expr( path_stack.dclone, following_siblings )
214
+ end
215
+ #puts "FOLLOWING_SIBLING 2: nodeset = #{nodeset}"
216
+ nodeset = results
217
+
218
+ when :preceding_sibling
219
+ results = []
220
+ for node in nodeset
221
+ all_siblings = node.parent.children
222
+ current_index = all_siblings.index( node )
223
+ preceding_siblings = all_siblings[ 0 .. current_index-1 ].reverse
224
+ #results += expr( path_stack.dclone, preceding_siblings )
225
+ end
226
+ nodeset = preceding_siblings
227
+ node_types = ELEMENTS
228
+
229
+ when :preceding
230
+ new_nodeset = []
231
+ for node in nodeset
232
+ new_nodeset += preceding( node )
233
+ end
234
+ #puts "NEW NODESET => #{new_nodeset.inspect}"
235
+ nodeset = new_nodeset
236
+ node_types = ELEMENTS
237
+
238
+ when :following
239
+ new_nodeset = []
240
+ for node in nodeset
241
+ new_nodeset += following( node )
242
+ end
243
+ nodeset = new_nodeset
244
+ node_types = ELEMENTS
245
+
246
+ when :namespace
247
+ new_set = []
248
+ for node in nodeset
249
+ new_nodeset << node.namespace if node.node_type == :element or node.node_type == :attribute
250
+ end
251
+ nodeset = new_nodeset
252
+
253
+ when :variable
254
+ var_name = path_stack.shift
255
+ return @variables[ var_name ]
256
+
257
+ # :and, :or, :eq, :neq, :lt, :lteq, :gt, :gteq
258
+ when :eq, :neq, :lt, :lteq, :gt, :gteq, :and, :or
259
+ left = expr( path_stack.shift, nodeset, context )
260
+ #puts "LEFT => #{left.inspect} (#{left.class.name})"
261
+ right = expr( path_stack.shift, nodeset, context )
262
+ #puts "RIGHT => #{right.inspect} (#{right.class.name})"
263
+ res = equality_relational_compare( left, op, right )
264
+ #puts "RES => #{res.inspect}"
265
+ return res
266
+
267
+ when :div
268
+ left = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
269
+ right = Functions::number(expr(path_stack.shift, nodeset, context)).to_f
270
+ return (left / right)
271
+
272
+ when :mod
273
+ left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
274
+ right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
275
+ return (left % right)
276
+
277
+ when :mult
278
+ left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
279
+ right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
280
+ return (left * right)
281
+
282
+ when :plus
283
+ left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
284
+ right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
285
+ return (left + right)
286
+
287
+ when :minus
288
+ left = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
289
+ right = Functions::number(expr(path_stack.shift, nodeset, context )).to_f
290
+ return (left - right)
291
+
292
+ when :union
293
+ left = expr( path_stack.shift, nodeset, context )
294
+ right = expr( path_stack.shift, nodeset, context )
295
+ return (left | right)
296
+
297
+ when :neg
298
+ res = expr( path_stack, nodeset, context )
299
+ return -(res.to_f)
300
+
301
+ when :not
302
+ when :function
303
+ func_name = path_stack.shift.tr('-','_')
304
+ arguments = path_stack.shift
305
+ #puts "FUNCTION 0: #{func_name}(#{arguments.collect{|a|a.inspect}.join(', ')})"
306
+ subcontext = context ? nil : { :size => nodeset.size }
307
+
308
+ res = []
309
+ cont = context
310
+ nodeset.each_with_index { |n, i|
311
+ if subcontext
312
+ subcontext[:node] = n
313
+ subcontext[:index] = i
314
+ cont = subcontext
315
+ end
316
+ arg_clone = arguments.dclone
317
+ args = arg_clone.collect { |arg|
318
+ #puts "FUNCTION 1: Calling expr( #{arg.inspect}, [#{n.inspect}] )"
319
+ expr( arg, [n], cont )
320
+ }
321
+ #puts "FUNCTION 2: #{func_name}(#{args.collect{|a|a.inspect}.join(', ')})"
322
+ Functions.context = cont
323
+ res << Functions.send( func_name, *args )
324
+ #puts "FUNCTION 3: #{res[-1].inspect}"
325
+ }
326
+ return res
327
+
328
+ end
329
+ end # while
330
+ #puts "EXPR returning #{nodeset.inspect}"
331
+ return nodeset
332
+ end
333
+
334
+ # Monkey Patch for Ruby 1.8.2
335
+ def internal_parse(path_stack, nodeset) # :nodoc:
336
+ return nodeset if nodeset.size == 0 or path_stack.size == 0
337
+ case path_stack.shift
338
+ when :document
339
+ return [ nodeset[0].root.parent ]
340
+
341
+ when :qname
342
+ prefix = path_stack.shift.downcase
343
+ name = path_stack.shift.downcase
344
+ n = nodeset.clone
345
+ ns = @namespaces[prefix]
346
+ ns = ns ? ns : ''
347
+ n.delete_if do |node|
348
+ if node.node_type == :element and ns == ''
349
+ ns = node.namespace( prefix )
350
+ end
351
+ !(node.node_type == :element and
352
+ node.name.downcase == name.downcase and node.namespace == ns )
353
+ end
354
+ return n
355
+
356
+ when :any
357
+ n = nodeset.clone
358
+ n.delete_if { |node| node.node_type != :element }
359
+ return n
360
+
361
+ when :self
362
+ # THIS SPACE LEFT INTENTIONALLY BLANK
363
+
364
+ when :processing_instruction
365
+ target = path_stack.shift
366
+ n = nodeset.clone
367
+ n.delete_if do |node|
368
+ (node.node_type != :processing_instruction) or
369
+ ( !target.nil? and ( node.target != target ) )
370
+ end
371
+ return n
372
+
373
+ when :text
374
+ n = nodeset.clone
375
+ n.delete_if do |node|
376
+ node.node_type != :text
377
+ end
378
+ return n
379
+
380
+ when :comment
381
+ n = nodeset.clone
382
+ n.delete_if do |node|
383
+ node.node_type != :comment
384
+ end
385
+ return n
386
+
387
+ when :node
388
+ return nodeset
389
+
390
+ when :child
391
+ new_nodeset = []
392
+ nt = nil
393
+ for node in nodeset
394
+ nt = node.node_type
395
+ new_nodeset += node.children if nt == :element or nt == :document
396
+ end
397
+ return new_nodeset
398
+
399
+ when :literal
400
+ literal = path_stack.shift
401
+ if literal =~ /^\d+(\.\d+)?$/
402
+ return ($1 ? literal.to_f : literal.to_i)
403
+ end
404
+ return literal
405
+
406
+ when :attribute
407
+ new_nodeset = []
408
+ case path_stack.shift
409
+ when :qname
410
+ prefix = path_stack.shift
411
+ name = path_stack.shift
412
+ for element in nodeset
413
+ if element.node_type == :element
414
+ for attribute_name in element.attributes.keys
415
+ if attribute_name.downcase == name.downcase
416
+ attrib = element.attribute( attribute_name,
417
+ @namespaces[prefix] )
418
+ new_nodeset << attrib if attrib
419
+ end
420
+ end
421
+ end
422
+ end
423
+ when :any
424
+ for element in nodeset
425
+ if element.node_type == :element
426
+ new_nodeset += element.attributes.to_a
427
+ end
428
+ end
429
+ end
430
+ return new_nodeset
431
+
432
+ when :parent
433
+ return internal_parse( path_stack,
434
+ nodeset.collect{|n| n.parent}.compact )
435
+
436
+ when :ancestor
437
+ new_nodeset = []
438
+ for node in nodeset
439
+ while node.parent
440
+ node = node.parent
441
+ new_nodeset << node unless new_nodeset.include? node
442
+ end
443
+ end
444
+ return new_nodeset
445
+
446
+ when :ancestor_or_self
447
+ new_nodeset = []
448
+ for node in nodeset
449
+ if node.node_type == :element
450
+ new_nodeset << node
451
+ while ( node.parent )
452
+ node = node.parent
453
+ new_nodeset << node unless new_nodeset.include? node
454
+ end
455
+ end
456
+ end
457
+ return new_nodeset
458
+
459
+ when :predicate
460
+ predicate = path_stack.shift
461
+ new_nodeset = []
462
+ Functions::size = nodeset.size
463
+ nodeset.size.times do |index|
464
+ node = nodeset[index]
465
+ Functions::node = node
466
+ Functions::index = index+1
467
+ result = Predicate( predicate, node )
468
+ if result.kind_of? Numeric
469
+ new_nodeset << node if result == (index+1)
470
+ elsif result.instance_of? Array
471
+ new_nodeset << node if result.size > 0
472
+ else
473
+ new_nodeset << node if result
474
+ end
475
+ end
476
+ return new_nodeset
477
+
478
+ when :descendant_or_self
479
+ rv = descendant_or_self( path_stack, nodeset )
480
+ path_stack.clear
481
+ return rv
482
+
483
+ when :descendant
484
+ results = []
485
+ nt = nil
486
+ for node in nodeset
487
+ nt = node.node_type
488
+ if nt == :element or nt == :document
489
+ results += internal_parse(
490
+ path_stack.clone.unshift( :descendant_or_self ),
491
+ node.children )
492
+ end
493
+ end
494
+ return results
495
+
496
+ when :following_sibling
497
+ results = []
498
+ for node in nodeset
499
+ all_siblings = node.parent.children
500
+ current_index = all_siblings.index( node )
501
+ following_siblings = all_siblings[ current_index+1 .. -1 ]
502
+ results += internal_parse( path_stack.clone, following_siblings )
503
+ end
504
+ return results
505
+
506
+ when :preceding_sibling
507
+ results = []
508
+ for node in nodeset
509
+ all_siblings = node.parent.children
510
+ current_index = all_siblings.index( node )
511
+ preceding_siblings = all_siblings[ 0 .. current_index-1 ]
512
+ results += internal_parse( path_stack.clone, preceding_siblings )
513
+ end
514
+ return results
515
+
516
+ when :preceding
517
+ new_nodeset = []
518
+ for node in nodeset
519
+ new_nodeset += preceding( node )
520
+ end
521
+ return new_nodeset
522
+
523
+ when :following
524
+ new_nodeset = []
525
+ for node in nodeset
526
+ new_nodeset += following( node )
527
+ end
528
+ return new_nodeset
529
+
530
+ when :namespace
531
+ new_set = []
532
+ for node in nodeset
533
+ if node.node_type == :element or node.node_type == :attribute
534
+ new_nodeset << node.namespace
535
+ end
536
+ end
537
+ return new_nodeset
538
+
539
+ when :variable
540
+ var_name = path_stack.shift
541
+ return @variables[ var_name ]
542
+
543
+ end
544
+ nodeset
545
+ end
546
+ end
547
+
548
+ class XPath # :nodoc:
549
+ def self.liberal_match(element, path=nil, namespaces={},
550
+ variables={}) # :nodoc:
551
+ parser = LiberalXPathParser.new
552
+ parser.namespaces = namespaces
553
+ parser.variables = variables
554
+ path = "*" unless path
555
+ element = [element] unless element.kind_of? Array
556
+ parser.parse(path, element)
557
+ end
558
+
559
+ def self.liberal_first(element, path=nil, namespaces={},
560
+ variables={}) # :nodoc:
561
+ parser = LiberalXPathParser.new
562
+ parser.namespaces = namespaces
563
+ parser.variables = variables
564
+ path = "*" unless path
565
+ element = [element] unless element.kind_of? Array
566
+ parser.parse(path, element)[0]
567
+ end
568
+
569
+ def self.liberal_each(element, path=nil, namespaces={},
570
+ variables={}, &block) # :nodoc:
571
+ parser = LiberalXPathParser.new
572
+ parser.namespaces = namespaces
573
+ parser.variables = variables
574
+ path = "*" unless path
575
+ element = [element] unless element.kind_of? Array
576
+ parser.parse(path, element).each( &block )
577
+ end
578
+ end
579
+
580
+ class Element # :nodoc:
581
+ unless REXML::Element.public_instance_methods.include? :inner_xml
582
+ def inner_xml # :nodoc:
583
+ result = ""
584
+ self.each_child do |child|
585
+ if child.kind_of? REXML::Comment
586
+ result << "<!--" + child.to_s + "-->"
587
+ else
588
+ result << child.to_s
589
+ end
590
+ end
591
+ return result.strip
592
+ end
593
+ else
594
+ warn("inner_xml method already exists.")
595
+ end
596
+
597
+ def base_uri # :nodoc:
598
+ begin
599
+ base_attribute = FeedTools::XmlHelper.try_xpaths(self, [
600
+ '@xml:base'
601
+ ])
602
+ if parent == nil || parent.kind_of?(REXML::Document)
603
+ return nil if base_attribute == nil
604
+ return base_attribute.value
605
+ end
606
+ if base_attribute != nil && parent == nil
607
+ return base_attribute.value
608
+ elsif parent != nil && base_attribute == nil
609
+ return parent.base_uri
610
+ elsif parent != nil && base_attribute != nil
611
+ parent_base_uri = parent.base_uri
612
+ if parent_base_uri != nil
613
+ uri = URI.parse(parent_base_uri)
614
+ return (uri + base_attribute.value).to_s
615
+ else
616
+ return base_attribute.value
617
+ end
618
+ end
619
+ return nil
620
+ rescue
621
+ return nil
622
+ end
623
+ end
624
+ end
625
+ end