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.
- data/CHANGELOG +12 -0
- data/lib/feed_tools.rb +2 -295
- data/lib/feed_tools/feed.rb +86 -18
- data/lib/feed_tools/feed_item.rb +20 -1
- data/lib/feed_tools/helpers/retrieval_helper.rb +4 -2
- data/lib/feed_tools/helpers/uri_helper.rb +2 -1
- data/lib/feed_tools/monkey_patch.rb +625 -0
- data/lib/feed_tools/version.rb +1 -1
- data/test/unit/atom_test.rb +46 -0
- metadata +2880 -2878
data/lib/feed_tools/feed_item.rb
CHANGED
@@ -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.
|
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
|