Phrogz
1/24/2008 3:56:00 AM
On Jan 23, 5:08 pm, Patrick Callahan <hoc...@youskate.com> wrote:
> Gavin Kistner wrote:
> Wow! That worked on the first try! I knew there had to be a way, but
> I'm trying to whip this script out and I didn't want to spend a week
> studying XML parsing options in Ruby to do it. You've saved me a bunch
> of effort. Now I just need to figure out how to gracefully bail out
> and continue on with the rest of the script if "foo" isn't there at all.
> Uhm... I seem to be having trouble with that piece too. I'm sure it's
> easier than the first problem, but maybe it's just 'cause my brain is
> cooked right now...
The first piece of code I wrote has a potential to be cranky.
doc.each_element( "//item[name='foo']" ){ |item|
puts item.get_elements( "value" ).first.text
}
If no <item>...<name>foo</name> elements can be found, the block will
never be called. However, if it finds such an element, but the <item>
doesn't have a <value> child, then item.get_elements( "value" ) will
return an empty array. Calling .first on an empty array will return
nil, and then you'll have an error when you try to run the 'text'
method of nil.
You could modify the above code to guard for this:
doc.each_element( "//item[name='foo']" ){ |item|
first_value = item.get_elements( "value" ).first
if first_value
puts first_value.text
end
}
However, the second (single XPath query) solution I wrote is both
shorter and also more fault tolerant. If you assume that there's only
one <item> that will match the <name>foo</name> criterion, then you
can change from REXML::XPath.match (which returns an array, possibly
empty) to REXML::XPath.first (which returns either a single value, or
nil).
Something like:
value = REXML::XPath.first( doc, "//item[name='foo']/value/text()" )
if value
# do my stuff here
end
Or, to use your exact case:
# Returns the uninstall command run (if found),
# or nil if the necessary info couldn't be found.
def uninstall_product( xml_string, prod_name )
require 'rexml'
include REXML
uninstall_command = XPath.first(
Document.new( xml_string ),
"//item[product_name='#{prod_name}']/uninstall_string/text()"
)
if uninstall_command
# Do the uninstall; maybe as simple as:
`#{uninstall_command}`
else
warn "Either product '#{prod_name}' doesn't exist, " +
"or it doesn't have an uninstall string."
end
uninstall_command
end