werein/prestashop

View on GitHub
lib/prestashop/api/converter.rb

Summary

Maintainability
D
2 days
Test Coverage
require 'nokogiri'

module Prestashop
  module Api
    module Converter

      # Build XML from given params and hash. This XML is compatible with Prestashop WebService
      #
      #   Converter.build :customers, :customer, { name: 'Steve' }  # => <prestashop><customers><customer>....</customer></customers></prestashop>
      #
      def self.build resource, model, hash
        Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |ml|
          ml.prestashop("xmlns:xlink" => "http://www.w3.org/1999/xlink") {
            ml.send(model.to_s) {
              build_nodes ml, hash
            }
          }
        end.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML | Nokogiri::XML::Node::SaveOptions::NO_DECLARATION).strip
      end

      # Parse XML (response from WebService call) to Hash, skipping first <prestashop> node.
      #
      #   Converter.parse('<prestashop><customers><customer><name>Steve</customer></customers></prestashop>')
      #   # => {customers: [ customer: { name: 'Steve' } ]}
      #
      def self.parse xml
        result = Nokogiri::XML xml

        raise ParserError unless result.root

        xml_node_to_hash result.root
      end

      # Parse XML to Hash, call parse and skip everything to error message
      #
      def self.parse_error response
        parsed = parse(response)
        if parsed[:errors] and parsed[:errors][:error] and parsed[:errors][:error][:message]
          parsed[:errors][:error][:message]
        else
          raise ParserError
        end
      end

      # Build XML nodes based from given source parameter, skipping +attr+ or +val+ keys if source is Hash
      #
      # ==== Parameters:
      # * +ml+ - Current XML
      # * +source+ - Source, which will be converted into the XML
      #
      def self.build_nodes ml, source, mkey = nil
        unless source[:attr] and source[:val]
          source.each do |key, value| 
            if value.kind_of? Hash
              if value[:attr]
                ml.send(key, value[:attr]){
                  cdata ml, value[:val]
                  build_nodes ml, value
                }
              elsif key != :attr
                ml.send(key){
                  unless value.kind_of? Hash
                    cdata ml, value
                  end
                  build_nodes ml, value
                }
              end
            elsif value.kind_of? Array
              value.each do |item|
                hash = {}
                hash[key] = item
                build_nodes ml, hash
              end
            elsif key != :val
              ml.send(key){
                cdata ml, value
              }
            end
          end if source.kind_of? Hash
        end
      end

      # Create CDATA tag into XML, when is not empty
      #
      def self.cdata ml, value
        if value and value != ''
          ml.cdata value
        end
      end

      # Parse XML node and covert it to hash
      #
      #   Converter.xml_node_to_hash '<customer id_lang="1">Steve</customer>'
      #   # => { attr: { id_lang: 1 }, val: 'Steve'  }
      #
      def self.xml_node_to_hash node 
        # If we are at the root of the document, start the hash
        if node.element?
          result_hash = {}
          if node.attributes != {}
             if node.attributes
              node.attributes.each do |key, value|
                unless value.name == 'href'
                  result_hash[:attr] = {} unless result_hash[:attr]
                  result_hash[:attr][value.name.to_sym] = prepare(value.value)
                end
              end
            end
          end
          if node.children.size > 0
            node.children.each do |child|
              result = xml_node_to_hash(child)
              if child.name == "text"
                unless child.next_sibling || child.previous_sibling
                  if !result_hash.empty?
                    result_hash[:val] = prepare(result)
                    return result_hash
                  end
                  return prepare(result)
                end
              elsif child.name == "#cdata-section"
                if !result_hash.empty?
                  result_hash[:val] = prepare(result)
                  return result_hash
                end
                return prepare(result)
              elsif result_hash[child.name.to_sym]
                if result_hash[child.name.to_sym].is_a?(Object::Array)
                  result_hash[child.name.to_sym] << prepare(result)
                else
                  result_hash[child.name.to_sym] = [result_hash[child.name.to_sym]] << prepare(result)
                end
              else 
                result_hash[child.name.to_sym] = prepare(result)
              end
            end
   
            return result_hash
          else
            return result_hash
          end 
        else 
          return prepare(node.content.to_s) 
        end 
      end

      def self.prepare data
        (data.class == String && data.to_i.to_s == data) ? data.to_i : data
      end
    end
  end
end