feedtools 0.2.18 → 0.2.19
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +28 -0
- data/lib/feed_tools.rb +328 -63
- data/lib/feed_tools/feed.rb +767 -764
- data/lib/feed_tools/feed_item.rb +684 -625
- data/lib/feed_tools/helpers/debug_helper.rb +37 -0
- data/lib/feed_tools/helpers/feed_tools_helper.rb +45 -41
- data/lib/feed_tools/helpers/generic_helper.rb +164 -0
- data/lib/feed_tools/helpers/retrieval_helper.rb +36 -0
- data/rakefile +298 -2
- data/test/unit/amp_test.rb +70 -69
- data/test/unit/atom_test.rb +91 -9
- data/test/unit/cache_test.rb +30 -11
- data/test/unit/cdf_test.rb +6 -4
- data/test/unit/encoding_test.rb +99 -0
- data/test/unit/generation_test.rb +3 -40
- data/test/unit/helper_test.rb +66 -6
- data/test/unit/interface_test.rb +34 -0
- data/test/unit/itunes_test.rb +19 -0
- data/test/unit/nonstandard_test.rb +22 -4
- data/test/unit/rdf_test.rb +19 -0
- data/test/unit/rss_test.rb +137 -43
- metadata +18 -8
- data/lib/feed_tools/vendor/builder.rb +0 -15
- data/lib/feed_tools/vendor/builder/blankslate.rb +0 -55
- data/lib/feed_tools/vendor/builder/xmlbase.rb +0 -144
- data/lib/feed_tools/vendor/builder/xmlevents.rb +0 -65
- data/lib/feed_tools/vendor/builder/xmlmarkup.rb +0 -299
data/CHANGELOG
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
== FeedTools 0.2.19
|
2
|
+
* lousy encoding support (as opposed to none at all)
|
3
|
+
* xml processing instruction now correctly prefixes generated feeds
|
4
|
+
* attributes are escaped properly when generating feeds
|
5
|
+
* uppercase html is no longer sanitized for not being in the whitelist
|
6
|
+
* added alias method for assigning to entries
|
7
|
+
* changed the xpath querying to be much, much more DRY
|
8
|
+
* find_node and find_all_nodes are actually useful now
|
9
|
+
* full case-insensitivity implemented for the xpath helper methods
|
10
|
+
* fixed bug in tests where some assertion failures could affect other tests
|
11
|
+
* fixed bug where the feed item author would sometimes be parsed incorrectly
|
12
|
+
* fixed bug where the convertLineBreaks element would break feed entries
|
13
|
+
* default (i.e. preferred) methods will be Atom-style instead of RSS-style
|
14
|
+
* default feed output format changed to Atom 1.0
|
15
|
+
* itunes namespace corrected
|
16
|
+
* fixed images property when dealing with atom
|
17
|
+
* fixed atom link property
|
18
|
+
* improved timestamp handling
|
19
|
+
* whitespace nodes now ignored by REXML
|
20
|
+
* added option to disable timestamp estimation
|
21
|
+
* added option to limit time-to-live to some upper maximum
|
22
|
+
* enclosures included in feed generation
|
23
|
+
* no longer uses the cache at all for file:/// urls
|
24
|
+
* changed itunes:keywords to use commas
|
25
|
+
* testing now excludes cache testing by default, use "test_all" to include it
|
26
|
+
* more tests
|
1
27
|
== FeedTools 0.2.18
|
2
28
|
* no longer ever polls more often than once every 30 minutes
|
3
29
|
* fixed overlooked improperly refactored enclosure code
|
@@ -6,6 +32,8 @@
|
|
6
32
|
* test cases now implemented using helpers
|
7
33
|
* fixed issue with timeouts
|
8
34
|
* fixed stack overflow while estimating timestamps
|
35
|
+
* fixed some namespace issue with atom
|
36
|
+
* added base64 decoding support
|
9
37
|
== FeedTools 0.2.17
|
10
38
|
* more fixes for timestamping of feed items
|
11
39
|
* fixed nil bug in root_node, feed_type, feed_version, build_xml
|
data/lib/feed_tools.rb
CHANGED
@@ -30,9 +30,9 @@ end
|
|
30
30
|
|
31
31
|
FEED_TOOLS_ENV = ENV['FEED_TOOLS_ENV'] ||
|
32
32
|
ENV['RAILS_ENV'] ||
|
33
|
-
'
|
33
|
+
'development' # :nodoc:
|
34
34
|
|
35
|
-
FEED_TOOLS_VERSION = "0.2.
|
35
|
+
FEED_TOOLS_VERSION = "0.2.19"
|
36
36
|
|
37
37
|
FEED_TOOLS_NAMESPACES = {
|
38
38
|
"admin" => "http://webns.net/mvcb/",
|
@@ -40,8 +40,10 @@ FEED_TOOLS_NAMESPACES = {
|
|
40
40
|
"annotate" => "http://purl.org/rss/1.0/modules/annotate/",
|
41
41
|
"atom10" => "http://www.w3.org/2005/Atom",
|
42
42
|
"atom03" => "http://purl.org/atom/ns#",
|
43
|
+
# "atom-blog" => "http://purl.org/atom-blog/ns#",
|
43
44
|
"audio" => "http://media.tangent.org/rss/1.0/",
|
44
45
|
"blogChannel" => "http://backend.userland.com/blogChannelModule",
|
46
|
+
"blogger" => "http://www.blogger.com/atom/ns#",
|
45
47
|
"cc" => "http://web.resource.org/cc/",
|
46
48
|
"creativeCommons" => "http://backend.userland.com/creativeCommonsRssModule",
|
47
49
|
"co" => "http://purl.org/rss/1.0/modules/company",
|
@@ -56,7 +58,7 @@ FEED_TOOLS_NAMESPACES = {
|
|
56
58
|
"feedburner" => "http://rssnamespace.org/feedburner/ext/1.0",
|
57
59
|
"foaf" => "http://xmlns.com/foaf/0.1/",
|
58
60
|
"fm" => "http://freshmeat.net/rss/fm/",
|
59
|
-
"itunes" => "http://www.itunes.com/
|
61
|
+
"itunes" => "http://www.itunes.com/dtds/podcast-1.0.dtd",
|
60
62
|
"l" => "http://purl.org/rss/1.0/modules/link/",
|
61
63
|
"media" => "http://search.yahoo.com/mrss",
|
62
64
|
"pingback" => "http://madskills.com/public/xml/rss/module/pingback/",
|
@@ -97,12 +99,7 @@ begin
|
|
97
99
|
|
98
100
|
require 'rubygems'
|
99
101
|
|
100
|
-
|
101
|
-
require 'builder'
|
102
|
-
rescue LoadError
|
103
|
-
# RubyGems version is not available, use included Builder
|
104
|
-
require 'feed_tools/vendor/builder'
|
105
|
-
end
|
102
|
+
require_gem('builder', '>= 1.2.4')
|
106
103
|
|
107
104
|
begin
|
108
105
|
require 'tidy'
|
@@ -113,8 +110,10 @@ begin
|
|
113
110
|
require 'feed_tools/vendor/htree'
|
114
111
|
|
115
112
|
require 'net/http'
|
116
|
-
|
117
|
-
|
113
|
+
|
114
|
+
# TODO: Not used yet, don't load since it'll only be a performance hit
|
115
|
+
# require 'net/https'
|
116
|
+
# require 'net/ftp'
|
118
117
|
|
119
118
|
require 'rexml/document'
|
120
119
|
|
@@ -125,7 +124,8 @@ begin
|
|
125
124
|
require 'yaml'
|
126
125
|
require 'base64'
|
127
126
|
|
128
|
-
require_gem('
|
127
|
+
require_gem('activesupport', '>= 1.1.1')
|
128
|
+
require_gem('activerecord', '>= 1.11.1')
|
129
129
|
require_gem('uuidtools', '>= 0.1.2')
|
130
130
|
|
131
131
|
require 'feed_tools/feed'
|
@@ -155,25 +155,55 @@ end
|
|
155
155
|
# slashdot_feed.items.first.find_node("slash:hitparade/text()").value
|
156
156
|
# => "43,37,28,23,11,3,1"
|
157
157
|
module FeedTools
|
158
|
+
@configurations = {}
|
159
|
+
|
160
|
+
def FeedTools.load_configurations
|
161
|
+
if @configurations.blank?
|
162
|
+
config_hash = {}
|
163
|
+
@configurations = {
|
164
|
+
:feed_cache => "FeedTools::DatabaseFeedCache",
|
165
|
+
:user_agent => "FeedTools/#{FEED_TOOLS_VERSION} " +
|
166
|
+
"+http://www.sporkmonger.com/projects/feedtools/",
|
167
|
+
:generator_name => "FeedTools/#{FEED_TOOLS_VERSION}",
|
168
|
+
:generator_href => "http://www.sporkmonger.com/projects/feedtools/",
|
169
|
+
:tidy_enabled => false,
|
170
|
+
:tidy_options => {},
|
171
|
+
:sanitize_with_nofollow => true,
|
172
|
+
:timestamp_estimation_enabled => true,
|
173
|
+
:url_normalization_enabled => true,
|
174
|
+
:strip_comment_count => false,
|
175
|
+
:max_ttl => 3.days.to_s,
|
176
|
+
:output_encoding => "utf-8",
|
177
|
+
:no_content_value => "[no description]"
|
178
|
+
}.merge(config_hash)
|
179
|
+
end
|
180
|
+
return @configurations
|
181
|
+
end
|
158
182
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
183
|
+
# Resets configuration to a clean load
|
184
|
+
def FeedTools.reset_configurations
|
185
|
+
@configurations = nil
|
186
|
+
FeedTools.load_configurations
|
187
|
+
end
|
188
|
+
|
189
|
+
# Returns the configuration hash for FeedTools
|
190
|
+
def FeedTools.configurations
|
191
|
+
if @configurations.blank?
|
192
|
+
FeedTools.load_configurations()
|
193
|
+
end
|
194
|
+
return @configurations
|
195
|
+
end
|
196
|
+
|
197
|
+
# Sets the configuration hash for FeedTools
|
198
|
+
def FeedTools.configurations=(new_configurations)
|
199
|
+
@configurations = new_configurations
|
200
|
+
end
|
165
201
|
|
166
202
|
# Error raised when a feed cannot be retrieved
|
167
203
|
class FeedAccessError < StandardError
|
168
204
|
end
|
169
205
|
|
170
206
|
# Returns the current caching mechanism.
|
171
|
-
def FeedTools.feed_cache
|
172
|
-
return @feed_cache
|
173
|
-
end
|
174
|
-
|
175
|
-
# Sets the current caching mechanism. If set to nil, disables caching.
|
176
|
-
# Default is the DatabaseFeedCache class.
|
177
207
|
#
|
178
208
|
# Objects of this class must accept the following messages:
|
179
209
|
# id
|
@@ -199,12 +229,26 @@ module FeedTools
|
|
199
229
|
# find_by_url
|
200
230
|
# initialize_cache
|
201
231
|
# connected?
|
202
|
-
def FeedTools.feed_cache
|
203
|
-
|
204
|
-
|
205
|
-
@feed_cache
|
232
|
+
def FeedTools.feed_cache
|
233
|
+
return nil if FeedTools.configurations[:feed_cache].blank?
|
234
|
+
class_name = FeedTools.configurations[:feed_cache].to_s
|
235
|
+
if @feed_cache.nil? || @feed_cache.to_s != class_name
|
236
|
+
begin
|
237
|
+
cache_class = eval(class_name)
|
238
|
+
if cache_class.kind_of?(Class)
|
239
|
+
@feed_cache = cache_class
|
240
|
+
return cache_class
|
241
|
+
else
|
242
|
+
return nil
|
243
|
+
end
|
244
|
+
rescue
|
245
|
+
return nil
|
246
|
+
end
|
247
|
+
else
|
248
|
+
return @feed_cache
|
249
|
+
end
|
206
250
|
end
|
207
|
-
|
251
|
+
|
208
252
|
# Returns true if FeedTools.feed_cache is not nil and a connection with
|
209
253
|
# the cache has been successfully established. Also returns false if an
|
210
254
|
# error is raised while trying to determine the status of the cache.
|
@@ -216,28 +260,7 @@ module FeedTools
|
|
216
260
|
return false
|
217
261
|
end
|
218
262
|
end
|
219
|
-
|
220
|
-
# Returns the currently used user agent string.
|
221
|
-
def FeedTools.user_agent
|
222
|
-
return @user_agent
|
223
|
-
end
|
224
|
-
|
225
|
-
# Sets the user agent string to send in the http headers.
|
226
|
-
def FeedTools.user_agent=(new_user_agent)
|
227
|
-
@user_agent = new_user_agent
|
228
|
-
end
|
229
|
-
|
230
|
-
# Returns the currently used no content string.
|
231
|
-
def FeedTools.no_content_string
|
232
|
-
return @no_content_string
|
233
|
-
end
|
234
|
-
|
235
|
-
# Sets the no content string to use when a feed is missing a content element.
|
236
|
-
# Used only for xml output.
|
237
|
-
def FeedTools.no_content_string=(new_no_content_string)
|
238
|
-
@no_content_string = new_no_content_string
|
239
|
-
end
|
240
|
-
|
263
|
+
|
241
264
|
# Returns true if the html tidy module can be used.
|
242
265
|
#
|
243
266
|
# Obviously, you need the tidy gem installed in order to run with html
|
@@ -255,7 +278,7 @@ module FeedTools
|
|
255
278
|
def FeedTools.tidy_enabled?
|
256
279
|
# This is an override variable to keep tidy from being used even if it
|
257
280
|
# is available.
|
258
|
-
if
|
281
|
+
if FeedTools.configurations[:tidy_enabled] == false
|
259
282
|
return false
|
260
283
|
end
|
261
284
|
if @tidy_enabled.nil? || @tidy_enabled == false
|
@@ -337,13 +360,6 @@ module FeedTools
|
|
337
360
|
end
|
338
361
|
return @tidy_enabled
|
339
362
|
end
|
340
|
-
|
341
|
-
# Turns html tidy support on or off. Be aware, that setting this to true
|
342
|
-
# does not mean tidy will be enabled. It simply means that tidy will be
|
343
|
-
# enabled if it is available to be enabled.
|
344
|
-
def FeedTools.tidy_enabled=(new_tidy_enabled)
|
345
|
-
@force_tidy_enabled = new_tidy_enabled
|
346
|
-
end
|
347
363
|
|
348
364
|
# Attempts to ensures that the passed url is valid and sane. Accepts very, very ugly urls
|
349
365
|
# and makes every effort to figure out what it was supposed to be. Also translates from
|
@@ -470,8 +486,8 @@ module FeedTools
|
|
470
486
|
def FeedTools.escape_entities(html)
|
471
487
|
return nil if html.nil?
|
472
488
|
escaped_html = CGI.escapeHTML(html)
|
473
|
-
|
474
|
-
|
489
|
+
escaped_html.gsub!(/'/, "'")
|
490
|
+
escaped_html.gsub!(/"/, """)
|
475
491
|
return escaped_html
|
476
492
|
end
|
477
493
|
|
@@ -540,6 +556,9 @@ module FeedTools
|
|
540
556
|
else
|
541
557
|
tidy_html = html
|
542
558
|
end
|
559
|
+
if tidy_html.blank? && !html.blank?
|
560
|
+
tidy_html = html.strip
|
561
|
+
end
|
543
562
|
return tidy_html
|
544
563
|
end
|
545
564
|
|
@@ -586,7 +605,7 @@ module FeedTools
|
|
586
605
|
if html_node.respond_to? :children
|
587
606
|
for child in html_node.children
|
588
607
|
if child.kind_of? REXML::Element
|
589
|
-
unless acceptable_elements.include? child.name
|
608
|
+
unless acceptable_elements.include? child.name.downcase
|
590
609
|
if mode == :strip
|
591
610
|
html_node.delete_element(child)
|
592
611
|
else
|
@@ -596,7 +615,7 @@ module FeedTools
|
|
596
615
|
end
|
597
616
|
end
|
598
617
|
for attribute in child.attributes.keys
|
599
|
-
unless acceptable_attributes.include? attribute
|
618
|
+
unless acceptable_attributes.include? attribute.downcase
|
600
619
|
child.delete_attribute(attribute)
|
601
620
|
end
|
602
621
|
end
|
@@ -655,6 +674,252 @@ module FeedTools
|
|
655
674
|
end
|
656
675
|
|
657
676
|
module REXML # :nodoc:
|
677
|
+
class LiberalXPathParser < XPathParser # :nodoc:
|
678
|
+
private
|
679
|
+
def internal_parse(path_stack, nodeset) # :nodoc:
|
680
|
+
return nodeset if nodeset.size == 0 or path_stack.size == 0
|
681
|
+
case path_stack.shift
|
682
|
+
when :document
|
683
|
+
return [ nodeset[0].root.parent ]
|
684
|
+
|
685
|
+
when :qname
|
686
|
+
prefix = path_stack.shift.downcase
|
687
|
+
name = path_stack.shift.downcase
|
688
|
+
n = nodeset.clone
|
689
|
+
ns = @namespaces[prefix]
|
690
|
+
ns = ns ? ns : ''
|
691
|
+
n.delete_if do |node|
|
692
|
+
if node.node_type == :element and ns == ''
|
693
|
+
ns = node.namespace( prefix )
|
694
|
+
end
|
695
|
+
!(node.node_type == :element and
|
696
|
+
node.name.downcase == name and node.namespace == ns )
|
697
|
+
end
|
698
|
+
return n
|
699
|
+
|
700
|
+
when :any
|
701
|
+
n = nodeset.clone
|
702
|
+
n.delete_if { |node| node.node_type != :element }
|
703
|
+
return n
|
704
|
+
|
705
|
+
when :self
|
706
|
+
# THIS SPACE LEFT INTENTIONALLY BLANK
|
707
|
+
|
708
|
+
when :processing_instruction
|
709
|
+
target = path_stack.shift
|
710
|
+
n = nodeset.clone
|
711
|
+
n.delete_if do |node|
|
712
|
+
(node.node_type != :processing_instruction) or
|
713
|
+
( !target.nil? and ( node.target != target ) )
|
714
|
+
end
|
715
|
+
return n
|
716
|
+
|
717
|
+
when :text
|
718
|
+
n = nodeset.clone
|
719
|
+
n.delete_if do |node|
|
720
|
+
node.node_type != :text
|
721
|
+
end
|
722
|
+
return n
|
723
|
+
|
724
|
+
when :comment
|
725
|
+
n = nodeset.clone
|
726
|
+
n.delete_if do |node|
|
727
|
+
node.node_type != :comment
|
728
|
+
end
|
729
|
+
return n
|
730
|
+
|
731
|
+
when :node
|
732
|
+
return nodeset
|
733
|
+
|
734
|
+
when :child
|
735
|
+
new_nodeset = []
|
736
|
+
nt = nil
|
737
|
+
for node in nodeset
|
738
|
+
nt = node.node_type
|
739
|
+
new_nodeset += node.children if nt == :element or nt == :document
|
740
|
+
end
|
741
|
+
return new_nodeset
|
742
|
+
|
743
|
+
when :literal
|
744
|
+
literal = path_stack.shift
|
745
|
+
if literal =~ /^\d+(\.\d+)?$/
|
746
|
+
return ($1 ? literal.to_f : literal.to_i)
|
747
|
+
end
|
748
|
+
return literal
|
749
|
+
|
750
|
+
when :attribute
|
751
|
+
new_nodeset = []
|
752
|
+
case path_stack.shift
|
753
|
+
when :qname
|
754
|
+
prefix = path_stack.shift
|
755
|
+
name = path_stack.shift.downcase
|
756
|
+
for element in nodeset
|
757
|
+
if element.node_type == :element
|
758
|
+
for attribute_name in element.attributes.keys
|
759
|
+
if attribute_name.downcase == name
|
760
|
+
attrib = element.attribute( attribute_name,
|
761
|
+
@namespaces[prefix] )
|
762
|
+
new_nodeset << attrib if attrib
|
763
|
+
end
|
764
|
+
end
|
765
|
+
end
|
766
|
+
end
|
767
|
+
when :any
|
768
|
+
for element in nodeset
|
769
|
+
if element.node_type == :element
|
770
|
+
new_nodeset += element.attributes.to_a
|
771
|
+
end
|
772
|
+
end
|
773
|
+
end
|
774
|
+
return new_nodeset
|
775
|
+
|
776
|
+
when :parent
|
777
|
+
return internal_parse( path_stack, nodeset.collect{|n| n.parent}.compact )
|
778
|
+
|
779
|
+
when :ancestor
|
780
|
+
new_nodeset = []
|
781
|
+
for node in nodeset
|
782
|
+
while node.parent
|
783
|
+
node = node.parent
|
784
|
+
new_nodeset << node unless new_nodeset.include? node
|
785
|
+
end
|
786
|
+
end
|
787
|
+
return new_nodeset
|
788
|
+
|
789
|
+
when :ancestor_or_self
|
790
|
+
new_nodeset = []
|
791
|
+
for node in nodeset
|
792
|
+
if node.node_type == :element
|
793
|
+
new_nodeset << node
|
794
|
+
while ( node.parent )
|
795
|
+
node = node.parent
|
796
|
+
new_nodeset << node unless new_nodeset.include? node
|
797
|
+
end
|
798
|
+
end
|
799
|
+
end
|
800
|
+
return new_nodeset
|
801
|
+
|
802
|
+
when :predicate
|
803
|
+
predicate = path_stack.shift
|
804
|
+
new_nodeset = []
|
805
|
+
Functions::size = nodeset.size
|
806
|
+
nodeset.size.times do |index|
|
807
|
+
node = nodeset[index]
|
808
|
+
Functions::node = node
|
809
|
+
Functions::index = index+1
|
810
|
+
result = Predicate( predicate, node )
|
811
|
+
if result.kind_of? Numeric
|
812
|
+
new_nodeset << node if result == (index+1)
|
813
|
+
elsif result.instance_of? Array
|
814
|
+
new_nodeset << node if result.size > 0
|
815
|
+
else
|
816
|
+
new_nodeset << node if result
|
817
|
+
end
|
818
|
+
end
|
819
|
+
return new_nodeset
|
820
|
+
|
821
|
+
when :descendant_or_self
|
822
|
+
rv = descendant_or_self( path_stack, nodeset )
|
823
|
+
path_stack.clear
|
824
|
+
return rv
|
825
|
+
|
826
|
+
when :descendant
|
827
|
+
results = []
|
828
|
+
nt = nil
|
829
|
+
for node in nodeset
|
830
|
+
nt = node.node_type
|
831
|
+
if nt == :element or nt == :document
|
832
|
+
results += internal_parse(
|
833
|
+
path_stack.clone.unshift( :descendant_or_self ),
|
834
|
+
node.children )
|
835
|
+
end
|
836
|
+
end
|
837
|
+
return results
|
838
|
+
|
839
|
+
when :following_sibling
|
840
|
+
results = []
|
841
|
+
for node in nodeset
|
842
|
+
all_siblings = node.parent.children
|
843
|
+
current_index = all_siblings.index( node )
|
844
|
+
following_siblings = all_siblings[ current_index+1 .. -1 ]
|
845
|
+
results += internal_parse( path_stack.clone, following_siblings )
|
846
|
+
end
|
847
|
+
return results
|
848
|
+
|
849
|
+
when :preceding_sibling
|
850
|
+
results = []
|
851
|
+
for node in nodeset
|
852
|
+
all_siblings = node.parent.children
|
853
|
+
current_index = all_siblings.index( node )
|
854
|
+
preceding_siblings = all_siblings[ 0 .. current_index-1 ]
|
855
|
+
results += internal_parse( path_stack.clone, preceding_siblings )
|
856
|
+
end
|
857
|
+
return results
|
858
|
+
|
859
|
+
when :preceding
|
860
|
+
new_nodeset = []
|
861
|
+
for node in nodeset
|
862
|
+
new_nodeset += preceding( node )
|
863
|
+
end
|
864
|
+
return new_nodeset
|
865
|
+
|
866
|
+
when :following
|
867
|
+
new_nodeset = []
|
868
|
+
for node in nodeset
|
869
|
+
new_nodeset += following( node )
|
870
|
+
end
|
871
|
+
return new_nodeset
|
872
|
+
|
873
|
+
when :namespace
|
874
|
+
new_set = []
|
875
|
+
for node in nodeset
|
876
|
+
if node.node_type == :element or node.node_type == :attribute
|
877
|
+
new_nodeset << node.namespace
|
878
|
+
end
|
879
|
+
end
|
880
|
+
return new_nodeset
|
881
|
+
|
882
|
+
when :variable
|
883
|
+
var_name = path_stack.shift
|
884
|
+
return @variables[ var_name ]
|
885
|
+
|
886
|
+
end
|
887
|
+
nodeset
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
class XPath # :nodoc:
|
892
|
+
def self.liberal_match(element, path=nil, namespaces={},
|
893
|
+
variables={}) # :nodoc:
|
894
|
+
parser = LiberalXPathParser.new
|
895
|
+
parser.namespaces = namespaces
|
896
|
+
parser.variables = variables
|
897
|
+
path = "*" unless path
|
898
|
+
element = [element] unless element.kind_of? Array
|
899
|
+
parser.parse(path, element)
|
900
|
+
end
|
901
|
+
|
902
|
+
def self.liberal_first(element, path=nil, namespaces={},
|
903
|
+
variables={}) # :nodoc:
|
904
|
+
parser = LiberalXPathParser.new
|
905
|
+
parser.namespaces = namespaces
|
906
|
+
parser.variables = variables
|
907
|
+
path = "*" unless path
|
908
|
+
element = [element] unless element.kind_of? Array
|
909
|
+
parser.parse(path, element)[0]
|
910
|
+
end
|
911
|
+
|
912
|
+
def self.liberal_each(element, path=nil, namespaces={},
|
913
|
+
variables={}, &block) # :nodoc:
|
914
|
+
parser = LiberalXPathParser.new
|
915
|
+
parser.namespaces = namespaces
|
916
|
+
parser.variables = variables
|
917
|
+
path = "*" unless path
|
918
|
+
element = [element] unless element.kind_of? Array
|
919
|
+
parser.parse(path, element).each( &block )
|
920
|
+
end
|
921
|
+
end
|
922
|
+
|
658
923
|
class Element # :nodoc:
|
659
924
|
unless REXML::Element.public_instance_methods.include? :inner_xml
|
660
925
|
def inner_xml # :nodoc:
|