TinyXPathHelper
Description
Very light syntax for using XPaths on XML documents or REXML nodes
Examples
TinyXPathHelper.new takes one parameter: an XML document (as a string or IO) or a REXML element
>> xpath = TinyXPathHelper.new( File.open( 'test/xml/sample.xml' ) )
Then, to find the string values of matching nodes, there’s a super-light syntax:
>> xpath[ 'node/' ]
=> ["one", "two", "three\n "]
It works on attributes, too
>> xpath[ 'node/@style' ]
=> ["first"]
If you prefer, you can use #all instead of square brackets
>> xpath.all( 'node/@style' )
=> ["first"]
If you just want the first match, use #first
>> xpath.first( 'node/' )
=> "one"
A slightly more elaborate API is available as #find_xpath
>> xpath.find_xpath( 'node/@style', :format => :rexml, :find => :first )
=> style='first'
Where :format can be :text (returns a string), or :rexml (returns a REXML element), and :find can be :first or :all
>> xpath.find_xpath( 'node/@style', :format => :rexml, :find => :all )
=> [style='first']
If you want to filter the output, you can pass something that can be to_proc’d as :format (Note that this acts on the string value of the element, not the REXML node)
>> xpath[ 'node/@style', :to_i ]
=> [0]
>> xpath[ 'node/@style', :length ]
=> [5]
>> xpath[ 'node', lambda{|x| x.strip.split(//) } ]
=> [["o", "n", "e"], ["t", "w", "o"], ["t", "h", "r", "e", "e"]]
If you only need to set :format, you may pass just the symbol instead of an options hash This is especially convenient using the [] syntax since a bug in ruby1.8 prevents using => inside of []
>> xpath[ 'node/@style', :rexml ]
=> [style='first']
Actually, formats :rexml and :text are aliases for the :auto_text parameter, which takes a bool, and defaults to true.
>> xpath.all( 'node/@style', :auto_text => false )
=> [style='first']
If you find yourself writing iterative xpaths, you may want to use :format => :tiny_xpath_helper
>> node_xpath = xpath.first( 'node', :format => :tiny_xpath_helper)
>> node_xpath.first('@style')
=> style='first'
Option defaults can be passed to TinyXPathHelper#new
>> xpath_with_options = TinyXPathHelper.new( File.open( 'test/xml/sample.xml' ), :format => :to_i, :find => :all )
>> xpath_with_options.find_xpath( 'node' )
=> [0,0,0]
You can access the default options for the TinyXMLHelper Class or any instance with #default_options
>> TinyXPathHelper.default_options
=> {:format => nil, :auto_text => true, :find => :first}
>> xpath_with_options.default_options
=> {:format => :to_i, :auto_text => true, :find => :all}
You can also pass a block, which will get called on the entire return value, but only if at least one element is found
>> xpath.all( 'node' ){ |nodes| nodes.count } # nodes is an array of strings
=> 3
>> xpath.first( 'node' ){ |node| node.reverse } # node is a string
=> "eno"
>> xpath.all( 'node', &:count )
=> 3
>> xpath.all( 'nothing' ){ |nodes| raise "this will not run" }
=> []
If you really want shortcut for the case where you want to run something over the array of matching REXML nodes, set :auto_text to false when calling TinyXpathHelper#new
>> rexml_xpath = TinyXPathHelper.new( File.open( 'test/xml/sample.xml' ), :auto_text => false )
>> rexml_xpath[ 'node', :name ]
=> ['node', 'node', 'node']
The output from :format => :rexml is suitable for passing to another TinyXPathHelper
>> node_tree = xpath.find_xpath( 'node/three', :format => :rexml )
>> deeper_xpath = TinyXPathHelper.new( node_tree )
>> deeper_xpath[ 'b/@beta' ]
=> ["true"]
If you want empty documents to not raise an error
>> empty_xpath = TinyXPathHelper.new( String.new, :allow_empty_document => true )
>> empty_xpath[ 'b/@beta' ]
=> []
If you want to call to_s on the xpath argument
>> xpath_with_indifferent_access = TinyXPathHelper.new( File.open( 'test/xml/sample.xml' ), :with_indifferent_access => true )
>> xpath_with_indifferent_access[ :node ]
=> ["one", "two", "three\n "]
– doctest_require: ‘lib/tiny_xpath_helper.rb’ ++
Copyright © 2009 raSANTIAGO + Associates LLC