rasantiago-tiny_xpath_helper 0.1.3 → 0.1.4
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/README.rdoc +41 -5
- data/lib/tiny_xpath_helper.rb +48 -22
- data/tiny_xpath_helper.gemspec +1 -1
- metadata +1 -1
data/README.rdoc
CHANGED
@@ -26,17 +26,53 @@ If you just want the first match, use #first
|
|
26
26
|
=> "one"
|
27
27
|
|
28
28
|
A slightly more elaborate API is available as #find_xpath
|
29
|
-
>> xpath.find_xpath( 'node/@style', :format => :
|
29
|
+
>> xpath.find_xpath( 'node/@style', :format => :rexml, :find => :first )
|
30
30
|
=> style='first'
|
31
31
|
|
32
|
-
Where :format can be :text (returns a string), or :
|
32
|
+
Where :format can be :text (returns a string), or :rexml (returns a REXML element),
|
33
33
|
and :find can be :first or :all
|
34
|
+
>> xpath.find_xpath( 'node/@style', :format => :rexml, :find => :all )
|
35
|
+
=> [style='first']
|
34
36
|
|
35
|
-
|
37
|
+
If you only need to set :format, you may pass just the symbol instead of an options hash
|
38
|
+
This is especially convenient using the [] syntax since a bug in ruby1.8
|
39
|
+
prevents using => inside of []
|
40
|
+
>> xpath[ 'node/@style', :rexml ]
|
36
41
|
=> [style='first']
|
37
42
|
|
38
|
-
|
39
|
-
|
43
|
+
If you want to filter the output, you can pass something that can be to_proc'd as :format
|
44
|
+
(Note that this acts on the string value of the element, not the REXML node)
|
45
|
+
>> xpath[ 'node/@style', :to_i ]
|
46
|
+
=> [0]
|
47
|
+
|
48
|
+
>> xpath[ 'node/@style', :length ]
|
49
|
+
=> [5]
|
50
|
+
|
51
|
+
>> xpath[ 'node', lambda{|x| x.strip.split(//) } ]
|
52
|
+
=> [["o", "n", "e"], ["t", "w", "o"], ["t", "h", "r", "e", "e"]]
|
53
|
+
|
54
|
+
|
55
|
+
You can also pass a block, which will get called on the entire return value, but
|
56
|
+
only if at least one element is found
|
57
|
+
>> xpath.all( 'node' ){ |nodes| nodes.count } # nodes is an array
|
58
|
+
=> 3
|
59
|
+
|
60
|
+
>> xpath.first( 'node' ){ |node| node.reverse } # node is a string
|
61
|
+
=> "eno"
|
62
|
+
|
63
|
+
>> xpath.all( 'node', &:count )
|
64
|
+
=> 3
|
65
|
+
|
66
|
+
>> xpath.all( 'nothing' ){ |nodes| raise "this will not run" }
|
67
|
+
=> []
|
68
|
+
|
69
|
+
There's no shortcut for the case where you want to run something over the array of matching REXML nodes.
|
70
|
+
Just use Array#map for that.
|
71
|
+
>> xpath[ 'node', :rexml ].map(&:name)
|
72
|
+
=> ['node', 'node', 'node']
|
73
|
+
|
74
|
+
The output from :format => :rexml is suitable for passing to another TinyXPathHelper
|
75
|
+
>> node_tree = xpath.find_xpath( 'node/three', :format => :rexml )
|
40
76
|
>> deeper_xpath = TinyXPathHelper.new( node_tree )
|
41
77
|
>> deeper_xpath[ 'b/@beta' ]
|
42
78
|
=> ["true"]
|
data/lib/tiny_xpath_helper.rb
CHANGED
@@ -13,22 +13,35 @@ class TinyXPathHelper
|
|
13
13
|
@options.dup
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
16
|
+
def with_options(*options)
|
17
|
+
r = default_options
|
18
|
+
|
19
|
+
options.each do |option|
|
20
|
+
if not Hash === option
|
21
|
+
option = {:format => option}
|
22
|
+
end
|
23
|
+
r.update(option)
|
24
|
+
end
|
25
|
+
|
26
|
+
return r
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_xpath(xpath_expr, options = {}, &blk)
|
30
|
+
self.class.find_xpath_from( node, xpath_expr, with_options(options), &blk)
|
18
31
|
end
|
19
32
|
|
20
|
-
def first(xpath_expr, options = {})
|
21
|
-
self.find_xpath( xpath_expr,
|
33
|
+
def first(xpath_expr, options = {}, &blk)
|
34
|
+
self.find_xpath( xpath_expr, with_options(options, :find => :first), &blk)
|
22
35
|
end
|
23
36
|
alias at first
|
24
37
|
|
25
|
-
def all(xpath_expr, options = {})
|
26
|
-
self.find_xpath( xpath_expr,
|
38
|
+
def all(xpath_expr, options = {}, &blk)
|
39
|
+
self.find_xpath( xpath_expr, with_options(options, :find => :all), &blk)
|
27
40
|
end
|
28
41
|
alias [] all
|
29
42
|
|
30
43
|
def self.io_stream_classes
|
31
|
-
[ IOStream ] rescue [ IO, StringIO ] # thoughtbot-paperclip fixes the ducktype mess
|
44
|
+
[ IOStream ] rescue [ IO, StringIO ] # thoughtbot-paperclip fixes the ducktype mess in StringIO
|
32
45
|
end
|
33
46
|
|
34
47
|
def self.classes_that_are_xmlish
|
@@ -77,31 +90,44 @@ class TinyXPathHelper
|
|
77
90
|
|
78
91
|
count = options[:find] || :first
|
79
92
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
93
|
+
filter1 = self.method(:xml_node_to_text)
|
94
|
+
filter2 = nil
|
95
|
+
|
96
|
+
if format == :xml or format == :rexml
|
97
|
+
filter1 = nil
|
98
|
+
elsif format == :text
|
99
|
+
filter2 = nil
|
100
|
+
elsif format.respond_to? :to_proc
|
101
|
+
filter2 = format.to_proc
|
84
102
|
else
|
85
103
|
raise "I don't know the format #{format.inspect}"
|
86
104
|
end
|
87
105
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
106
|
+
|
107
|
+
if count == :all
|
108
|
+
elements = REXML::XPath.match(element, path)
|
109
|
+
|
92
110
|
elsif count == :first
|
93
|
-
|
94
|
-
|
95
|
-
)
|
111
|
+
elements = [ REXML::XPath.first(element, path) ].compact # Haskell hacker wishing for the Maybe monad
|
112
|
+
|
96
113
|
else
|
97
114
|
raise "I don't know how to find #{count.inspect}"
|
98
115
|
end
|
99
116
|
|
100
|
-
|
101
|
-
|
102
|
-
|
117
|
+
elements = elements.map(&filter1).map(&filter2)
|
118
|
+
|
119
|
+
if count == :all
|
120
|
+
r = elements
|
121
|
+
elsif count == :first
|
122
|
+
r = elements.first
|
103
123
|
end
|
104
|
-
|
124
|
+
|
125
|
+
if(blk and elements.length > 0)
|
126
|
+
return blk.call( r )
|
127
|
+
end
|
128
|
+
|
129
|
+
return r
|
130
|
+
|
105
131
|
end
|
106
132
|
|
107
133
|
end
|
data/tiny_xpath_helper.gemspec
CHANGED