lib/apricoteatsgorilla.rb in smacks-apricoteatsgorilla-0.4.5 vs lib/apricoteatsgorilla.rb in smacks-apricoteatsgorilla-0.4.6

- old
+ new

@@ -1,245 +1,4 @@ -require "rubygems" -require "iconv" -require "hpricot" +$:.unshift(File.join(File.dirname(__FILE__), "apricoteatsgorilla")) -# Apricot eats Gorilla is a SOAP communication helper. -# -# It translates between SOAP messages (XML) and Ruby Hashes while offering some -# helpful methods for working with SOAP webservices. Apricot eats Gorilla was -# initially based on CobraVsMongoose but uses Hpricot instead of REXML. -class ApricotEatsGorilla - class << self # Class methods - - # Flag to enable sorting of Hash keys. - attr_accessor :sort_keys - - # Flag to disable conversion of tag names to lowerCamelCase. - attr_accessor :disable_tag_names_to_lower_camel_case - - # Flag to disable conversion of Hash keys to snake_case. - attr_accessor :disable_hash_keys_to_snake_case - - # Flag to disable conversion of Hash keys to Symbols. - attr_accessor :disable_hash_keys_to_symbol - - # Array of XML nodes to add a namespace to. - attr_accessor :nodes_to_namespace - - # The namespace for nodes set by nodes_to_namespace. - attr_accessor :node_namespace - - # Shortcut method for translating between XML Strings and Ruby Hashes. - # Delegates to xml_to_hash in case +source+ is of type String or delegates - # to hash_to_xml in case +source+ is of type Hash. Returns nil otherwise. - def [](source, root_node = nil) - case source - when String - xml_to_hash(source, root_node) - when Hash - hash_to_xml(source) - else - nil - end - end - - # Yields this class object in case a +block+ was given. Nice way for setting - # multiple options at once. - def setup - yield self if block_given? - end - - # Converts a given +xml+ String into a Ruby Hash. Starts parsing at root - # node by default. The optional +root_node+ parameter can be used to specify - # a custom root node to start parsing at using an XPath (Hpricot search). - # The root node itself won't be included in the Hash. Converts tag names - # from CamelCase/lowerCamelCase to snake_case and into Symbols by default. - # - # ==== Examples - # - # xml = "<apricot><eats>Gorilla</eats></apricot>" - # ApricotEatsGorilla[xml] - # # => { :eats => "Gorilla" } - # - # xml = "<apricot><eats><lotsOf>Gorillas</lotsOf></eats></apricot>" - # ApricotEatsGorilla[xml, "//eats"] - # # => { :lots_of => "Gorillas" } - def xml_to_hash(xml, root_node = nil) - doc = Hpricot.XML remove_whitespace(xml) - root = root_node ? doc.search(root_node) : doc.root - - return nil if root.nil? - return xml_node_to_hash(root) unless root.respond_to? :each - - if root.size == 1 - return root.first.children.to_s if root.first.children.first.kind_of?(Hpricot::Text) - xml_node_to_hash(root.first) - else - root.map do |node| - if node.children.first.kind_of?(Hpricot::Text) - node.children.to_s - else - xml_node_to_hash(node) - end - end - end - end - - # Converts a given Ruby Hash into an XML String. Converts Hash keys from - # snake_case to lowerCamelCase by default. - # - # ==== Examples - # - # hash = { :apricot => { :eats => "Gorilla" } } - # ApricotEatsGorilla[hash] - # # => "<apricot><eats>Gorilla</eats></apricot>" - # - # hash = { :apricot => { :eats => [ "Gorillas", "Snakes" ] } } - # ApricotEatsGorilla[hash] - # # => "<apricot><eats>Gorillas</eats><eats>Snakes</eats></apricot>" - def hash_to_xml(hash) - nested_data_to_xml(hash.keys.first, hash.values.first) - end - - # Builds a SOAP request envelope and includes the content from a given +block+ - # into the envelope body. Accepts a Hash (namespace => namespace_uri) of - # additional +namespaces+ to set. - # - # ==== Examples - # - # ApricotEatsGorilla.soap_envelope { "<authenticate>me</authenticate>" } - # - # # => '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> - # # => <env:Body> - # # => <authenticate>me</authenticate> - # # => </env:Body> - # # => </env:Envelope>' - # - # ApricotEatsGorilla.soap_envelope :wsdl => "http://example.com" do - # "<authenticate>me</authenticate>" - # end - # - # # => '<env:Envelope - # # => xmlns:wsdl="http://example.com" - # # => xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"> - # # => <env:Body> - # # => <authenticate>me</authenticate> - # # => </env:Body> - # # => </env:Envelope>' - def soap_envelope(namespaces = {}) - namespaces["env"] = "http://schemas.xmlsoap.org/soap/envelope/" - tag("env:Envelope", namespaces) do - tag("env:Body") do - yield if block_given? - end - end - end - - # Converts a given +string+ from CamelCase/lowerCamelCase to snake_case. - def to_snake_case(string) - string = string.gsub(/[A-Z]+/, '\1_\0').downcase - string = string[1, string.length-1] if string[0, 1] == "_" - string - end - - # Converts a given +string+ from snake_case to lowerCamelCase. - def to_lower_camel_case(string) - string.to_s.gsub(/_(.)/) { $1.upcase } - end - - private - - # Actual implementation for xml_to_hash. Takes and iterates through a given - # Hpricot +element+ and returns a Ruby Hash equal to the given content. - def xml_node_to_hash(element) - this_node = {} - element.each_child do |child| - # hpricot 0.6.1 returns an empty array, while 0.8 returns nil - if child.children.nil? || child.children.empty? - key, value = child.name, nil - elsif child.children.size == 1 && child.children.first.text? - key, value = child.name, map_to_boolean(child.children.first.to_html) - else - key, value = child.name, xml_node_to_hash(child) - end - - key = remove_namespace(key) - key = to_snake_case(key) unless disable_hash_keys_to_snake_case - key = key.intern unless disable_hash_keys_to_symbol - current = this_node[key] - case current - when Array - this_node[key] << value - when nil - this_node[key] = value - else - this_node[key] = [current.dup, value] - end - end - this_node - end - - # Actual implementation for hash_to_xml. Takes a Hash key +name+ and a - # value +item+ and returns an XML String equal to the given content. - def nested_data_to_xml(name, item) - case item - when Array - item.map { |subitem| nested_data_to_xml(name, subitem) }.join - when Hash - tag(name) do - opt_order(item).map { |tag, value| - case value - when Array - value.map { |subitem| nested_data_to_xml(tag, subitem) }.join - when Hash - nested_data_to_xml(tag, value) - else - tag(tag) { value.to_s } if value.respond_to? :to_s - end - }.join - end - else - tag(name) { item.to_s } if item.respond_to? :to_s - end - end - - # Creates an XML tag. Expects a block for tag content. Defaults to an empty - # element tag in case no block was supplied. - def tag(name, attributes = {}) - name = to_lower_camel_case(name) unless disable_tag_names_to_lower_camel_case - if nodes_to_namespace.kind_of? Array - name = "#{node_namespace}:#{name}" if node_namespace && nodes_to_namespace.include?(name) - end - return "<#{name} />" unless block_given? - - attr = opt_order(attributes).map { |k, v| %Q( xmlns:#{k}="#{v}") }.to_s - body = yield || "" - "<#{name}#{attr}>" << body << "</#{name}>" - end - - # Removes whitespace between tags of a given +xml+ String. - def remove_whitespace(xml) - xml.gsub(/(>)\s*(<)/, '\1\2') - end - - # Removes the namespace from a given XML +tag+. - def remove_namespace(tag) - tag.sub(/.+:(.+)/, '\1') - end - - # Checks to see if a given +string+ matches "true" or "false" and converts - # these values to actual Boolean objects. Returns the original String in - # case it does not match "true" or "false". - def map_to_boolean(string) - return true if string == "true" - return false if string == "false" - Iconv.new("iso-8859-1", "utf-8").iconv(string) - end - - # Returns a sorted version of a given +hash+ if sort_keys is enabled. - def opt_order(hash) - return hash unless sort_keys - hash.keys.sort_by { |s| s.to_s }.map { |key| [key, hash[key]] } - end - - end -end +require "apricoteatsgorilla" +require "xml_node" \ No newline at end of file