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