jschairb-rets4r 1.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/CHANGELOG +566 -0
- data/CONTRIBUTORS +7 -0
- data/Gemfile +10 -0
- data/LICENSE +29 -0
- data/MANIFEST +62 -0
- data/NEWS +186 -0
- data/README.rdoc +43 -0
- data/RUBYS +56 -0
- data/Rakefile +50 -0
- data/TODO +35 -0
- data/examples/client_get_object.rb +49 -0
- data/examples/client_login.rb +39 -0
- data/examples/client_mapper.rb +17 -0
- data/examples/client_metadata.rb +42 -0
- data/examples/client_parser.rb +9 -0
- data/examples/client_search.rb +49 -0
- data/examples/settings.yml +114 -0
- data/lib/rets4r.rb +14 -0
- data/lib/rets4r/auth.rb +73 -0
- data/lib/rets4r/client.rb +487 -0
- data/lib/rets4r/client/data.rb +14 -0
- data/lib/rets4r/client/dataobject.rb +28 -0
- data/lib/rets4r/client/exceptions.rb +116 -0
- data/lib/rets4r/client/links.rb +32 -0
- data/lib/rets4r/client/metadata.rb +15 -0
- data/lib/rets4r/client/parsers/compact.rb +42 -0
- data/lib/rets4r/client/parsers/compact_nokogiri.rb +91 -0
- data/lib/rets4r/client/parsers/metadata.rb +92 -0
- data/lib/rets4r/client/parsers/response_parser.rb +100 -0
- data/lib/rets4r/client/requester.rb +143 -0
- data/lib/rets4r/client/transaction.rb +31 -0
- data/lib/rets4r/core_ext/array/extract_options.rb +15 -0
- data/lib/rets4r/core_ext/class/attribute_accessors.rb +58 -0
- data/lib/rets4r/core_ext/hash/keys.rb +46 -0
- data/lib/rets4r/core_ext/hash/slice.rb +39 -0
- data/lib/rets4r/listing_mapper.rb +17 -0
- data/lib/rets4r/listing_service.rb +35 -0
- data/lib/rets4r/loader.rb +8 -0
- data/lib/tasks/annotations.rake +121 -0
- data/lib/tasks/coverage.rake +13 -0
- data/rets4r.gemspec +24 -0
- data/spec/rets4r_compact_data_parser_spec.rb +7 -0
- data/test/data/1.5/bad_compact.xml +7 -0
- data/test/data/1.5/count_only_compact.xml +3 -0
- data/test/data/1.5/error.xml +1 -0
- data/test/data/1.5/invalid_compact.xml +4 -0
- data/test/data/1.5/login.xml +16 -0
- data/test/data/1.5/metadata.xml +0 -0
- data/test/data/1.5/search_compact.xml +8 -0
- data/test/data/1.5/search_compact_big.xml +136 -0
- data/test/data/1.5/search_unescaped_compact.xml +8 -0
- data/test/data/listing_service.yml +36 -0
- data/test/test_auth.rb +68 -0
- data/test/test_client.rb +342 -0
- data/test/test_client_links.rb +39 -0
- data/test/test_compact_nokogiri.rb +64 -0
- data/test/test_helper.rb +12 -0
- data/test/test_listing_mapper.rb +112 -0
- data/test/test_loader.rb +24 -0
- data/test/test_parser.rb +96 -0
- data/test/test_quality.rb +57 -0
- metadata +211 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
module RETS4R
|
2
|
+
class Client
|
3
|
+
# Represents a row of data. Nothing more than a glorfied Hash with a custom constructor and a
|
4
|
+
# type attribute.
|
5
|
+
class Data < ::Hash
|
6
|
+
attr_accessor :type
|
7
|
+
|
8
|
+
def initialize(type = false)
|
9
|
+
super
|
10
|
+
self.type = type
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RETS4R
|
2
|
+
class Client
|
3
|
+
class ObjectHeader < Hash
|
4
|
+
include Net::HTTPHeader
|
5
|
+
def initialize(raw_header)
|
6
|
+
initialize_http_header( raw_header )
|
7
|
+
end
|
8
|
+
end
|
9
|
+
# Represents a RETS object (as returned by the get_object) transaction.
|
10
|
+
class DataObject
|
11
|
+
|
12
|
+
attr_accessor :header, :data
|
13
|
+
|
14
|
+
alias :type :header
|
15
|
+
alias :info :type
|
16
|
+
|
17
|
+
def initialize(headers, data)
|
18
|
+
@header = ObjectHeader.new(headers)
|
19
|
+
@data = data
|
20
|
+
end
|
21
|
+
|
22
|
+
def success?
|
23
|
+
return true if self.data
|
24
|
+
return false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module RETS4R
|
2
|
+
class Client
|
3
|
+
# This exception should be thrown when a generic client error is encountered.
|
4
|
+
class ClientException < Exception; end
|
5
|
+
|
6
|
+
# This exception should be thrown when there is an error with the parser, which is
|
7
|
+
# considered a subcomponent of the RETS client. It also includes the XML data that
|
8
|
+
# that was being processed at the time of the exception.
|
9
|
+
class ParserException < ClientException
|
10
|
+
attr_accessor :file
|
11
|
+
end
|
12
|
+
|
13
|
+
# The client does not currently support a specified action.
|
14
|
+
class Unsupported < ClientException; end
|
15
|
+
|
16
|
+
# The HTTP response returned by the server indicates that there was an error processing
|
17
|
+
# the request and the client cannot continue on its own without intervention.
|
18
|
+
class HTTPError < ClientException
|
19
|
+
attr_accessor :http_response
|
20
|
+
|
21
|
+
# Takes a HTTPResponse object
|
22
|
+
def initialize(http_response)
|
23
|
+
self.http_response = http_response
|
24
|
+
end
|
25
|
+
|
26
|
+
# Shorthand for calling HTTPResponse#code
|
27
|
+
def code
|
28
|
+
http_response.code
|
29
|
+
end
|
30
|
+
|
31
|
+
# Shorthand for calling HTTPResponse#message
|
32
|
+
def message
|
33
|
+
http_response.message
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the RETS specification message for the HTTP response code
|
37
|
+
def rets_message
|
38
|
+
Client::RETS_HTTP_MESSAGES[code]
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
"#{code} #{message}: #{rets_message}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# A general RETS level exception was encountered. This would include HTTP and RETS
|
47
|
+
# specification level errors as well as informative mishaps such as authentication being
|
48
|
+
# required for access.
|
49
|
+
class RETSException < RuntimeError; end
|
50
|
+
|
51
|
+
# There was a problem with logging into the RETS server.
|
52
|
+
class LoginError < RETSException; end
|
53
|
+
|
54
|
+
# For internal client use only, it is thrown when the a RETS request is made but a password
|
55
|
+
# is prompted for.
|
56
|
+
class AuthRequired < RETSException; end
|
57
|
+
|
58
|
+
# A RETS transaction failed
|
59
|
+
class RETSTransactionException < RETSException; end
|
60
|
+
|
61
|
+
# Search Transaction Exceptions
|
62
|
+
class UnknownQueryFieldException < RETSTransactionException; end
|
63
|
+
class NoRecordsFoundException < RETSTransactionException; end
|
64
|
+
class InvalidSelectException < RETSTransactionException; end
|
65
|
+
class MiscellaneousSearchErrorException < RETSTransactionException; end
|
66
|
+
class InvalidQuerySyntaxException < RETSTransactionException; end
|
67
|
+
class UnauthorizedQueryException < RETSTransactionException; end
|
68
|
+
class MaximumRecordsExceededException < RETSTransactionException; end
|
69
|
+
class TimeoutException < RETSTransactionException; end
|
70
|
+
class TooManyOutstandingQueriesException < RETSTransactionException; end
|
71
|
+
class DTDVersionUnavailableException < RETSTransactionException; end
|
72
|
+
|
73
|
+
# GetObject Exceptions
|
74
|
+
class InvalidResourceException < RETSTransactionException; end
|
75
|
+
class InvalidTypeException < RETSTransactionException; end
|
76
|
+
class InvalidIdentifierException < RETSTransactionException; end
|
77
|
+
class NoObjectFoundException < RETSTransactionException; end
|
78
|
+
class UnsupportedMIMETypeException < RETSTransactionException; end
|
79
|
+
class UnauthorizedRetrievalException < RETSTransactionException; end
|
80
|
+
class ResourceUnavailableException < RETSTransactionException; end
|
81
|
+
class ObjectUnavailableException < RETSTransactionException; end
|
82
|
+
class RequestTooLargeException < RETSTransactionException; end
|
83
|
+
class TimeoutException < RETSTransactionException; end
|
84
|
+
class TooManyOutstandingRequestsException < RETSTransactionException; end
|
85
|
+
class MiscellaneousErrorException < RETSTransactionException; end
|
86
|
+
|
87
|
+
EXCEPTION_TYPES = {
|
88
|
+
# Search Transaction Reply Codes
|
89
|
+
20200 => UnknownQueryFieldException,
|
90
|
+
20201 => NoRecordsFoundException,
|
91
|
+
20202 => InvalidSelectException,
|
92
|
+
20203 => MiscellaneousSearchErrorException,
|
93
|
+
20206 => InvalidQuerySyntaxException,
|
94
|
+
20207 => UnauthorizedQueryException,
|
95
|
+
20208 => MaximumRecordsExceededException,
|
96
|
+
20209 => TimeoutException,
|
97
|
+
20210 => TooManyOutstandingQueriesException,
|
98
|
+
20514 => DTDVersionUnavailableException,
|
99
|
+
|
100
|
+
# GetObject Reply Codes
|
101
|
+
20400 => InvalidResourceException,
|
102
|
+
20401 => InvalidTypeException,
|
103
|
+
20402 => InvalidIdentifierException,
|
104
|
+
20403 => NoObjectFoundException,
|
105
|
+
20406 => UnsupportedMIMETypeException,
|
106
|
+
20407 => UnauthorizedRetrievalException,
|
107
|
+
20408 => ResourceUnavailableException,
|
108
|
+
20409 => ObjectUnavailableException,
|
109
|
+
20410 => RequestTooLargeException,
|
110
|
+
20411 => TimeoutException,
|
111
|
+
20412 => TooManyOutstandingRequestsException,
|
112
|
+
20413 => MiscellaneousErrorException
|
113
|
+
|
114
|
+
}
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module RETS4R #:nodoc:
|
4
|
+
class Client #:nodoc:
|
5
|
+
class Links < Hash
|
6
|
+
attr_accessor :logger
|
7
|
+
def self.from_login_url(login_url)
|
8
|
+
links = self.new
|
9
|
+
links['Login'] = URI.parse(login_url)
|
10
|
+
links
|
11
|
+
end
|
12
|
+
def login
|
13
|
+
self['Login']
|
14
|
+
end
|
15
|
+
def logout
|
16
|
+
self['Logout']
|
17
|
+
end
|
18
|
+
def metadata
|
19
|
+
self['GetMetadata']
|
20
|
+
end
|
21
|
+
def objects
|
22
|
+
self['GetObject']
|
23
|
+
end
|
24
|
+
def search
|
25
|
+
self['Search']
|
26
|
+
end
|
27
|
+
def action
|
28
|
+
self['Action']
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rets4r/client/data'
|
2
|
+
|
3
|
+
module RETS4R
|
4
|
+
class Client
|
5
|
+
# Represents a set of metadata. It is simply an extended Array with type and attributes accessors.
|
6
|
+
class Metadata < Array
|
7
|
+
attr_accessor :type, :attributes
|
8
|
+
|
9
|
+
def initialize(type = false)
|
10
|
+
self.type = type if type
|
11
|
+
self.attributes = {}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Parses XML response containing 'COMPACT' data format.
|
2
|
+
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
module RETS4R
|
6
|
+
class Client
|
7
|
+
class CompactDataParser
|
8
|
+
def parse_results(doc)
|
9
|
+
|
10
|
+
delimiter = doc.get_elements('/RETS/DELIMITER')[0] &&
|
11
|
+
doc.get_elements('/RETS/DELIMITER')[0].attributes['value'].to_i.chr
|
12
|
+
columns = doc.get_elements('/RETS/COLUMNS')[0]
|
13
|
+
rows = doc.get_elements('/RETS/DATA')
|
14
|
+
|
15
|
+
parse_data(columns, rows, delimiter)
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse_data(column_element, row_elements, delimiter = "\t")
|
19
|
+
|
20
|
+
column_names = column_element.to_s.split(delimiter)
|
21
|
+
|
22
|
+
result = []
|
23
|
+
|
24
|
+
data = row_elements.each do |data_row|
|
25
|
+
data_row = data_row.text.split(delimiter)
|
26
|
+
|
27
|
+
row_result = {}
|
28
|
+
|
29
|
+
column_names.each_with_index do |col, x|
|
30
|
+
row_result[col] = data_row[x]
|
31
|
+
end
|
32
|
+
|
33
|
+
row_result.reject! { |k,v| k.nil? || k.empty? }
|
34
|
+
|
35
|
+
result << row_result
|
36
|
+
end
|
37
|
+
|
38
|
+
return result
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
module RETS4R
|
3
|
+
class Client
|
4
|
+
class CompactNokogiriParser
|
5
|
+
include Enumerable
|
6
|
+
def initialize(io)
|
7
|
+
@doc = CompactDocument.new
|
8
|
+
@parser = Nokogiri::XML::SAX::Parser.new(@doc)
|
9
|
+
@io = io
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_a
|
13
|
+
@parser.parse(@io) if @doc.results.empty?
|
14
|
+
@doc.results
|
15
|
+
end
|
16
|
+
|
17
|
+
def each(&block)
|
18
|
+
@doc.proc = block.to_proc
|
19
|
+
@parser.parse(@io)
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
class CompactDocument < Nokogiri::XML::SAX::Document
|
24
|
+
attr_reader :results
|
25
|
+
attr_writer :proc
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@results = []
|
29
|
+
end
|
30
|
+
def start_element name, attrs = []
|
31
|
+
case name
|
32
|
+
when 'DELIMITER'
|
33
|
+
@delimiter = attrs.last.to_i.chr
|
34
|
+
when 'COLUMNS'
|
35
|
+
@columns_element = true
|
36
|
+
@string = ''
|
37
|
+
when 'DATA'
|
38
|
+
@data_element = true
|
39
|
+
@string = ''
|
40
|
+
when 'RETS'
|
41
|
+
handle_body_start attrs
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def end_element name
|
46
|
+
case name
|
47
|
+
when 'COLUMNS'
|
48
|
+
@columns_element = false
|
49
|
+
@columns = @string.split(@delimiter)
|
50
|
+
when 'DATA'
|
51
|
+
@data_element = false
|
52
|
+
handle_row
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def characters string
|
57
|
+
if @columns_element
|
58
|
+
@string << string
|
59
|
+
elsif @data_element
|
60
|
+
@string << string
|
61
|
+
elsif @reply_code
|
62
|
+
throw string
|
63
|
+
@reply_code = false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
def handle_row
|
69
|
+
data = make_hash
|
70
|
+
if @proc
|
71
|
+
@proc.call(data)
|
72
|
+
else
|
73
|
+
@results << data
|
74
|
+
end
|
75
|
+
end
|
76
|
+
def handle_body_start attrs
|
77
|
+
attrs = Hash[*attrs]
|
78
|
+
if exception_class = Client::EXCEPTION_TYPES[attrs['ReplyCode'].to_i]
|
79
|
+
raise exception_class.new(attrs['ReplyText'])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
def make_hash
|
83
|
+
@columns.zip(@string.split(@delimiter)).inject({}) do | h,(k,v) |
|
84
|
+
h[k] = v unless k.empty?
|
85
|
+
next h
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'yaml'
|
3
|
+
require 'rets4r/client/parsers/compact'
|
4
|
+
|
5
|
+
module RETS4R
|
6
|
+
class Client
|
7
|
+
class MetadataParser
|
8
|
+
|
9
|
+
TAGS = [ 'METADATA-RESOURCE',
|
10
|
+
'METADATA-CLASS',
|
11
|
+
'METADATA-TABLE',
|
12
|
+
'METADATA-OBJECT',
|
13
|
+
'METADATA-LOOKUP',
|
14
|
+
'METADATA-LOOKUP_TYPE' ]
|
15
|
+
|
16
|
+
def initialize()
|
17
|
+
@parser = RETS4R::Client::CompactDataParser.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse_file(file = 'metadata.xml')
|
21
|
+
xml = File.read(file)
|
22
|
+
doc = REXML::Document.new(xml)
|
23
|
+
parse(doc)
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse(doc)
|
27
|
+
|
28
|
+
rets_resources = {}
|
29
|
+
|
30
|
+
doc.get_elements('/RETS/*').each do |elem|
|
31
|
+
|
32
|
+
next unless TAGS.include?(elem.name)
|
33
|
+
|
34
|
+
columns = elem.get_elements('COLUMNS')[0]
|
35
|
+
rows = elem.get_elements('DATA')
|
36
|
+
|
37
|
+
data = @parser.parse_data(columns, rows)
|
38
|
+
|
39
|
+
resource_id = elem.attributes['Resource']
|
40
|
+
|
41
|
+
case elem.name
|
42
|
+
when 'METADATA-RESOURCE'
|
43
|
+
data.each do |resource_info|
|
44
|
+
id = resource_info.delete('ResourceID')
|
45
|
+
rets_resources[id] = resource_info
|
46
|
+
end
|
47
|
+
|
48
|
+
when 'METADATA-CLASS'
|
49
|
+
data.each do |class_info|
|
50
|
+
class_name = class_info.delete('ClassName')
|
51
|
+
rets_resources[resource_id][:classes] ||= {}
|
52
|
+
rets_resources[resource_id][:classes][class_name] = class_info
|
53
|
+
end
|
54
|
+
|
55
|
+
when 'METADATA-TABLE'
|
56
|
+
class_name = elem.attributes['Class']
|
57
|
+
data.each do |table_info|
|
58
|
+
system_name = table_info.delete('SystemName')
|
59
|
+
rets_resources[resource_id][:classes][class_name][:tables] ||= {}
|
60
|
+
rets_resources[resource_id][:classes][class_name][:tables][system_name] = table_info
|
61
|
+
end
|
62
|
+
|
63
|
+
when 'METADATA-OBJECT'
|
64
|
+
data.each do |object_info|
|
65
|
+
object_type = object_info.delete('ObjectType')
|
66
|
+
rets_resources[resource_id][:objects] ||= {}
|
67
|
+
rets_resources[resource_id][:objects][object_type] = object_info
|
68
|
+
end
|
69
|
+
|
70
|
+
when 'METADATA-LOOKUP'
|
71
|
+
data.each do |lookup_info|
|
72
|
+
lookup_name = lookup_info.delete('LookupName')
|
73
|
+
rets_resources[resource_id][:lookups] ||= {}
|
74
|
+
rets_resources[resource_id][:lookups][lookup_name] = lookup_info
|
75
|
+
end
|
76
|
+
|
77
|
+
when 'METADATA-LOOKUP_TYPE'
|
78
|
+
lookup = elem.attributes['Lookup']
|
79
|
+
rets_resources[resource_id][:lookup_types] ||= {}
|
80
|
+
rets_resources[resource_id][:lookup_types][lookup] = {}
|
81
|
+
data.each do |lookup_type_info|
|
82
|
+
value = lookup_type_info.delete('Value')
|
83
|
+
rets_resources[resource_id][:lookup_types][lookup][value] = lookup_type_info
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
rets_resources
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'rets4r/client/transaction'
|
2
|
+
require 'rets4r/client/parsers/compact'
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
module RETS4R
|
6
|
+
class Client
|
7
|
+
class ResponseParser
|
8
|
+
def parse_key_value(xml)
|
9
|
+
parse_common(xml) do |doc|
|
10
|
+
parsed = nil
|
11
|
+
first_child = doc.get_elements('/RETS/RETS-RESPONSE')[0] ? doc.get_elements('/RETS/RETS-RESPONSE')[0] : doc.get_elements('/RETS')[0]
|
12
|
+
unless first_child.nil?
|
13
|
+
parsed = {}
|
14
|
+
first_child.text.each do |line|
|
15
|
+
(key, value) = line.strip.split('=')
|
16
|
+
key.strip! if key
|
17
|
+
value.strip! if value
|
18
|
+
parsed[key] = value
|
19
|
+
end
|
20
|
+
else
|
21
|
+
raise 'Response was not a proper RETS XML doc!'
|
22
|
+
end
|
23
|
+
|
24
|
+
if parsed.nil?
|
25
|
+
raise "Response was not valid key/value format"
|
26
|
+
else
|
27
|
+
parsed
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse_results(xml, format)
|
33
|
+
parse_common(xml) do |doc|
|
34
|
+
parser = get_parser_by_name(format)
|
35
|
+
parser.parse_results(doc)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_count(xml)
|
40
|
+
parse_common(xml) do |doc|
|
41
|
+
doc.get_elements('/RETS/COUNT')[0].attributes['Records']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_metadata(xml, format)
|
46
|
+
parse_common(xml) do |doc|
|
47
|
+
return REXML::Document.new(xml)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_object_response(xml)
|
52
|
+
parse_common(xml) do |doc|
|
53
|
+
# XXX
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def parse_common(xml, &block)
|
60
|
+
if xml == ''
|
61
|
+
raise RETSException, 'No transaction body was returned!'
|
62
|
+
end
|
63
|
+
|
64
|
+
doc = REXML::Document.new(xml)
|
65
|
+
|
66
|
+
root = doc.root
|
67
|
+
if root.nil? || root.name != 'RETS'
|
68
|
+
raise "Response had invalid root node. Document was: #{doc.inspect}"
|
69
|
+
end
|
70
|
+
|
71
|
+
transaction = Transaction.new
|
72
|
+
transaction.reply_code = root.attributes['ReplyCode']
|
73
|
+
transaction.reply_text = root.attributes['ReplyText']
|
74
|
+
transaction.maxrows = (doc.get_elements('/RETS/MAXROWS').length > 0)
|
75
|
+
|
76
|
+
|
77
|
+
# XXX: If it turns out we need to parse the response of errors, then this will
|
78
|
+
# need to change.
|
79
|
+
if transaction.reply_code.to_i > 0 && transaction.reply_code.to_i != 20201
|
80
|
+
exception_type = Client::EXCEPTION_TYPES[transaction.reply_code.to_i] || RETSTransactionException
|
81
|
+
raise exception_type, "#{transaction.reply_code} - #{transaction.reply_text}"
|
82
|
+
end
|
83
|
+
|
84
|
+
transaction.response = yield doc
|
85
|
+
return transaction
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_parser_by_name(name)
|
89
|
+
case name
|
90
|
+
when 'COMPACT', 'COMPACT-DECODED'
|
91
|
+
type = RETS4R::Client::CompactDataParser
|
92
|
+
else
|
93
|
+
raise "Invalid format #{name}"
|
94
|
+
end
|
95
|
+
type.new
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|