require 'rubygems' require 'rexml/document' module Relax module Parsers ## # Parsers the server's response using the REXML library. # # Benefits: # # * XML Namespace support (parameter :foo, :namespace => 'bar') # # Drawbacks: # # * Case sensitive field names (<Status>..</> != parameter :status) # class REXML < Base FACTORY_NAME = :rexml def initialize(raw, parent) @xml = ::REXML::Document.new(raw) super(raw, parent) end def parse! if parameters parameters.each do |parameter, options| begin element = options[:element] || parameter namespace = options[:namespace] if attribute = options[:attribute] and attribute == true node = attribute(root, element, namespace) elsif attribute node = attribute(element(element), attribute, namespace) elsif options[:collection] node = elements(element, namespace) else node = element(element, namespace) end if options[:collection] value = node.collect do |element| options[:collection].new(element.deep_clone) end else case type = options[:type] when Response value = type.new(node) when :date value = date_value(node) when :time value = time_value(node) when :float value = float_value(node) when :integer value = integer_value(node) when :text else value = text_value(node) end end parent.instance_variable_set("@#{parameter}", value) rescue raise(Relax::MissingParameter) if node.nil? && options[:required] end end end end # Returns the root of the XML document. def root @xml.root end # Checks the name of the root node. def is?(name, namespace = nil) root.name == node_name(name, nil) end # Returns a set of elements matching name. def elements(name, namespace = nil) root.get_elements(node_path(name, namespace)) end # Returns an element of the specified name. def element(name, namespace = nil) root.elements[node_path(name, namespace)] end alias :has? :element # Returns an attribute on an element. def attribute(element, name, namespace = nil) element.attribute(name) end # Gets the value of an element or attribute. def value(value) value.is_a?(::REXML::Element) ? value.text : value.to_s end # Gets a text value. def text_value(value) value(value) end # Gets an integer value. def integer_value(value) value(value).to_i end # Gets a float value. def float_value(value) value(value).to_f end # Gets a date value. def date_value(value) Date.parse(value(value)) end # Gets a time value. def time_value(value) Time.parse(value(value)) end private # Converts a name to a node name. def node_name(name, namespace = nil) "#{namespace.to_s + ':' if namespace}#{name}" end # Gets the XPath expression representing the root node. def root_path(name) "/#{node_name(name)}" end def node_path(name, namespace = nil) "#{node_name(name, namespace)}" end end Factory.register(REXML::FACTORY_NAME, REXML) end end