# This is the generic parser # # Supports responses, data, metadata, reply codes, and reply text. # # Supports the following tags: # RETS, METADATA-.*, MAXROWS, COLUMNS, DATA, COUNT, DELIMITER # # Metadata is built as: # (Metadata 1-> data row # -> data row), # (Metadata 2 -> data row), # etc. # # Data is built as: # Data 1, Data 2, Data N # # # TODO # Add comments/documentation # Handle more tags (if any are unaccounted for) # Handle standard (non-compact) output # Case Insensitivity? # There is still some holdovers from the previous organization of parsers, and they should be cleaned # up at some point. require 'cgi' module RETS4R class Client module Parser attr_accessor :output, :logger def initialize @transaction = Transaction.new @current = [] @output = 2 end def get_transaction @transaction end #### Stream Listener Events def tag_start(name, attrs) @current.push(name) case name when 'RETS' @transaction.reply_code = attrs['ReplyCode'] @transaction.reply_text = attrs['ReplyText'] when /METADATA.*/ @metadata = Metadata.new(name) @metadata.attributes = attrs when 'MAXROWS' @transaction.maxrows = true when 'COUNT' @transaction.count = attrs['Records'].to_i when 'DELIMITER' @transaction.delimiter = attrs['value'].to_i end end def text(text) case @current[-1] when 'COLUMNS' @transaction.header = parse_compact_line(text, @transaction.ascii_delimiter) when 'DATA' if @transaction.header.length > 0 data_type << parse_data(text, @transaction.header) else data_type << parse_compact_line(text, @transaction.ascii_delimiter) end when 'RETS-RESPONSE' @transaction.response = parse_key_value_body(text) end end def tag_end(name) @current.pop @transaction.data << @metadata if name =~ /METADATA.*/ end #### Helper Methods def clean_xml(xml) # This is a hack, and it assumes that we're using compact mode, but it's the easiest way to # strip out those bad "<" and ">" characters that were not properly escaped... xml.gsub!(/(.*)<\/DATA>/i) do |match| "#{CGI::escapeHTML(CGI::unescapeHTML($1))}" end end def data_type if @current[-2].index('METADATA') === 0 return @metadata else return @transaction.data end end def parse_compact_line(data, delim = "\t") begin return data.to_s.split(delim) rescue raise "Error while parsing compact line: #{$!} with data: #{data}" end end def parse_data(data, header) results = Data.new(@current[-2]) parsed_data = parse_compact_line(data, @transaction.ascii_delimiter) header.length.times do |pos| results[header[pos]] = parsed_data[pos] end results end def parse_key_value_body(data) parsed = {} data.each do |line| (key, value) = line.strip.split('=') parsed[key] = value end return parsed end end end end