rets4r 0.8.2 → 0.8.3
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/CHANGELOG +11 -0
- data/lib/rets4r/client.rb +93 -3
- data/test/client/tc_client.rb +19 -1
- metadata +15 -15
data/CHANGELOG
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
0.8.3
|
2
|
+
Added example #set_pre_request_block. (Fran�ois Beausoleil)
|
3
|
+
Allow a pre request block to be set for RETS4R::Client.
|
4
|
+
This allows the caller to change the request in any meaningful way, such as adding a
|
5
|
+
RETS-UA-Authorization header when appropriate. (Fran�ois Beausoleil)
|
6
|
+
Better response handling of HTTP errors. Response codes > 300 other than 401 now raise an
|
7
|
+
HTTPError exception that encapsulates the HTTPResponse object.
|
8
|
+
401 errors raise a LoginError exception. (Scott Patterson)
|
9
|
+
Added in RETS specification error messages to reference. (Scott Patterson)
|
10
|
+
New Rakefile. Install the rake gem and do 'rake test' to run the tests. (Fran�ois Beausoleil)
|
11
|
+
|
1
12
|
0.8.2
|
2
13
|
Added missing result parameter to Client#search's yield (Scott Patterson)
|
3
14
|
Added 1.7 to supported list and default version (Fran�ois Beausoleil)
|
data/lib/rets4r/client.rb
CHANGED
@@ -39,7 +39,30 @@ module RETS4R
|
|
39
39
|
SUPPORTED_RETS_VERSIONS = ['1.5', '1.7']
|
40
40
|
CAPABILITY_LIST = ['Action', 'ChangePassword', 'GetObject', 'Login', 'LoginComplete', 'Logout', 'Search', 'GetMetadata', 'Update']
|
41
41
|
SUPPORTED_PARSERS = [] # This will be populated by parsers as they load
|
42
|
-
|
42
|
+
|
43
|
+
# These are the response messages as defined in the RETS 1.5e2 and 1.7d6 specifications.
|
44
|
+
# Provided for convenience and are used by the HTTPError class to provide more useful
|
45
|
+
# messages.
|
46
|
+
RETS_HTTP_MESSAGES = {
|
47
|
+
'200' => 'Operation successful.',
|
48
|
+
'400' => 'The request could not be understood by the server due to malformed syntax.',
|
49
|
+
'401' => 'Either the header did not contain an acceptable Authorization or the username/password was invalid. The server response MUST include a WWW-Authenticate header field.',
|
50
|
+
'402' => 'The requested transaction requires a payment which could not be authorized.',
|
51
|
+
'403' => 'The server understood the request, but is refusing to fulfill it.',
|
52
|
+
'404' => 'The server has not found anything matching the Request-URI.',
|
53
|
+
'405' => 'The method specified in the Request-Line is not allowed for the resource identified by the Request-URI.',
|
54
|
+
'406' => 'The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request.',
|
55
|
+
'408' => 'The client did not produce a request within the time that the server was prepared to wait.',
|
56
|
+
'411' => 'The server refuses to accept the request without a defined Content-Length.',
|
57
|
+
'412' => 'Transaction not permitted at this point in the session.',
|
58
|
+
'413' => 'The server is refusing to process a request because the request entity is larger than the server is willing or able to process.',
|
59
|
+
'414' => 'The server is refusing to service the request because the Request-URI is longer than the server is willing to interpret. This error usually only occurs for a GET method.',
|
60
|
+
'500' => 'The server encountered an unexpected condition which prevented it from fulfilling the request.',
|
61
|
+
'501' => 'The server does not support the functionality required to fulfill the request.',
|
62
|
+
'503' => 'The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.',
|
63
|
+
'505' => 'The server does not support, or refuses to support, the HTTP protocol version that was used in the request message.',
|
64
|
+
}
|
65
|
+
|
43
66
|
attr_accessor :mimemap, :logger
|
44
67
|
|
45
68
|
# We load our parsers here so that they can modify the client class appropriately. Because
|
@@ -82,6 +105,35 @@ module RETS4R
|
|
82
105
|
end
|
83
106
|
end
|
84
107
|
|
108
|
+
# Assigns a block that will be called just before the request is sent.
|
109
|
+
# This block must accept three parameters:
|
110
|
+
# * self
|
111
|
+
# * Net::HTTP instance
|
112
|
+
# * Hash of headers
|
113
|
+
#
|
114
|
+
# The block's return value will be ignored. If you want to prevent the request
|
115
|
+
# to go through, raise an exception.
|
116
|
+
#
|
117
|
+
# == Example
|
118
|
+
#
|
119
|
+
# client = RETS4R::Client.new(...)
|
120
|
+
# # Make a new pre_request_block that calculates the RETS-UA-Authorization header.
|
121
|
+
# client.set_pre_request_block do |rets, http, headers|
|
122
|
+
# a1 = Digest::MD5.hexdigest([headers["User-Agent"], @password].join(":"))
|
123
|
+
# if headers.has_key?("Cookie") then
|
124
|
+
# cookie = headers["Cookie"].split(";").map(&:strip).select {|c| c =~ /rets-session-id/i}
|
125
|
+
# cookie = cookie ? cookie.split("=").last : ""
|
126
|
+
# else
|
127
|
+
# cookie = ""
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# parts = [a1, "", cookie, headers["RETS-Version"]]
|
131
|
+
# headers["RETS-UA-Authorization"] = "Digest " + Digest::MD5.hexdigest(parts.join(":"))
|
132
|
+
# end
|
133
|
+
def set_pre_request_block(&block)
|
134
|
+
@pre_request_block = block
|
135
|
+
end
|
136
|
+
|
85
137
|
# We only allow external read access to URLs because they are internally set based on the
|
86
138
|
# results of various queries.
|
87
139
|
def urls
|
@@ -343,9 +395,8 @@ module RETS4R
|
|
343
395
|
'Content-ID' => response['Content-ID']
|
344
396
|
}
|
345
397
|
|
346
|
-
if response['Content-Length'].to_i > 100
|
398
|
+
if response['Transfer-Encoding'].to_s.downcase == "chunked" || response['Content-Length'].to_i > 100 then
|
347
399
|
data_object = DataObject.new(info, response.body)
|
348
|
-
|
349
400
|
if block_given?
|
350
401
|
yield data_object
|
351
402
|
results += 1
|
@@ -466,6 +517,8 @@ module RETS4R
|
|
466
517
|
headers = @headers
|
467
518
|
headers.merge(header) unless header.empty?
|
468
519
|
|
520
|
+
@pre_request_block.call(self, http, headers) if @pre_request_block
|
521
|
+
|
469
522
|
logger.debug(headers.inspect) if logger
|
470
523
|
|
471
524
|
@semaphore.unlock
|
@@ -477,6 +530,10 @@ module RETS4R
|
|
477
530
|
if response.code == '401'
|
478
531
|
# Authentication is required
|
479
532
|
raise AuthRequired
|
533
|
+
elsif response.code.to_i >= 300
|
534
|
+
# We have a non-successful response that we cannot handle
|
535
|
+
@semaphore.unlock if @semaphore.locked?
|
536
|
+
raise HTTPError.new(response)
|
480
537
|
else
|
481
538
|
cookies = []
|
482
539
|
if set_cookies = response.get_fields('set-cookie') then
|
@@ -494,6 +551,9 @@ module RETS4R
|
|
494
551
|
retry_auth -= 1
|
495
552
|
set_header('Authorization', Auth.authenticate(response, @username, @password, url.path, method, @headers['RETS-Request-ID'], get_user_agent, @nc))
|
496
553
|
retry
|
554
|
+
else
|
555
|
+
@semaphore.unlock if @semaphore.locked?
|
556
|
+
raise LoginError.new(response.message)
|
497
557
|
end
|
498
558
|
end
|
499
559
|
|
@@ -545,6 +605,36 @@ module RETS4R
|
|
545
605
|
class Unsupported < ClientException
|
546
606
|
end
|
547
607
|
|
608
|
+
# The HTTP response returned by the server indicates that there was an error processing
|
609
|
+
# the request and the client cannot continue on its own without intervention.
|
610
|
+
class HTTPError < ClientException
|
611
|
+
attr_accessor :http_response
|
612
|
+
|
613
|
+
# Takes a HTTPResponse object
|
614
|
+
def initialize(http_response)
|
615
|
+
self.http_response = http_response
|
616
|
+
end
|
617
|
+
|
618
|
+
# Shorthand for calling HTTPResponse#code
|
619
|
+
def code
|
620
|
+
http_response.code
|
621
|
+
end
|
622
|
+
|
623
|
+
# Shorthand for calling HTTPResponse#message
|
624
|
+
def message
|
625
|
+
http_response.message
|
626
|
+
end
|
627
|
+
|
628
|
+
# Returns the RETS specification message for the HTTP response code
|
629
|
+
def rets_message
|
630
|
+
Client::RETS_HTTP_MESSAGES[code]
|
631
|
+
end
|
632
|
+
|
633
|
+
def to_s
|
634
|
+
"#{code} #{message}: #{rets_message}"
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|
548
638
|
# A general RETS level exception was encountered. This would include HTTP and RETS
|
549
639
|
# specification level errors as well as informative mishaps such as authentication being
|
550
640
|
# required for access.
|
data/test/client/tc_client.rb
CHANGED
@@ -94,13 +94,30 @@ module RETS4R
|
|
94
94
|
|
95
95
|
def test_returns_single_entity_object_in_a_single_element_array
|
96
96
|
@response.expects(:[]).with('content-type').at_least_once.returns("image/jpeg")
|
97
|
+
@response.expects(:[]).with('Transfer-Encoding').at_least_once.returns("")
|
97
98
|
@response.expects(:[]).with('Content-Length').at_least_once.returns(241)
|
98
99
|
@response.expects(:[]).with('Object-ID').at_least_once.returns("25478")
|
99
100
|
@response.expects(:[]).with('Content-ID').at_least_once.returns("5589")
|
100
101
|
@response.expects(:body).returns("\000"*241)
|
101
102
|
|
102
103
|
results = @rets.get_object("Property", "Photo", "392103:*")
|
103
|
-
assert_equal 1, results.size, "Client parsed
|
104
|
+
assert_equal 1, results.size, "Client parsed one object out of the request"
|
105
|
+
assert_kind_of RETS4R::Client::DataObject, results[0], "First result isn't a DataObject"
|
106
|
+
assert_equal "image/jpeg", results[0].type["Content-Type"], "Content-Type not copied"
|
107
|
+
assert_equal "5589", results[0].type["Content-ID"], "Content-ID not copied"
|
108
|
+
assert_equal "25478", results[0].type["Object-ID"], "Object-ID not copied"
|
109
|
+
assert_equal 241, results[0].data.size, "First object isn't 241 bytes in length"
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_returns_single_entity_object_as_chunked_encoding_in_a_single_element_array
|
113
|
+
@response.expects(:[]).with('content-type').at_least_once.returns("image/jpeg")
|
114
|
+
@response.expects(:[]).with('Transfer-Encoding').at_least_once.returns("chunked")
|
115
|
+
@response.expects(:[]).with('Object-ID').at_least_once.returns("25478")
|
116
|
+
@response.expects(:[]).with('Content-ID').at_least_once.returns("5589")
|
117
|
+
@response.expects(:body).returns("\000"*241)
|
118
|
+
|
119
|
+
results = @rets.get_object("Property", "Photo", "392103:*")
|
120
|
+
assert_equal 1, results.size, "Client parsed one object out of the request"
|
104
121
|
assert_kind_of RETS4R::Client::DataObject, results[0], "First result isn't a DataObject"
|
105
122
|
assert_equal "image/jpeg", results[0].type["Content-Type"], "Content-Type not copied"
|
106
123
|
assert_equal "5589", results[0].type["Content-ID"], "Content-ID not copied"
|
@@ -110,6 +127,7 @@ module RETS4R
|
|
110
127
|
|
111
128
|
def test_yields_data_objects_to_block_and_returns_blocks_value
|
112
129
|
@response.expects(:[]).with('content-type').at_least_once.returns("image/jpeg")
|
130
|
+
@response.expects(:[]).with('Transfer-Encoding').at_least_once.returns("")
|
113
131
|
@response.expects(:[]).with('Content-Length').at_least_once.returns(241)
|
114
132
|
@response.expects(:[]).with('Object-ID').at_least_once.returns("25478")
|
115
133
|
@response.expects(:[]).with('Content-ID').at_least_once.returns("5589")
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.
|
2
|
+
rubygems_version: 0.9.4
|
3
3
|
specification_version: 1
|
4
4
|
name: rets4r
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.8.
|
7
|
-
date:
|
6
|
+
version: 0.8.3
|
7
|
+
date: 2008-01-03 00:00:00 -08:00
|
8
8
|
summary: A native Ruby implementation of RETS (Real Estate Transaction Standard).
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -34,29 +34,21 @@ files:
|
|
34
34
|
- examples/client_metadata.rb
|
35
35
|
- examples/client_search.rb
|
36
36
|
- lib/rets4r
|
37
|
-
- lib/rets4r.rb
|
38
37
|
- lib/rets4r/auth.rb
|
39
38
|
- lib/rets4r/client
|
40
|
-
- lib/rets4r/client.rb
|
41
39
|
- lib/rets4r/client/data.rb
|
42
40
|
- lib/rets4r/client/dataobject.rb
|
43
41
|
- lib/rets4r/client/metadata.rb
|
44
42
|
- lib/rets4r/client/metadataindex.rb
|
45
43
|
- lib/rets4r/client/parser
|
46
|
-
- lib/rets4r/client/parser.rb
|
47
|
-
- lib/rets4r/client/transaction.rb
|
48
44
|
- lib/rets4r/client/parser/rexml.rb
|
49
45
|
- lib/rets4r/client/parser/xmlparser.rb
|
46
|
+
- lib/rets4r/client/parser.rb
|
47
|
+
- lib/rets4r/client/transaction.rb
|
48
|
+
- lib/rets4r/client.rb
|
49
|
+
- lib/rets4r.rb
|
50
50
|
- test/client
|
51
|
-
- test/ts_all.rb
|
52
|
-
- test/ts_client.rb
|
53
51
|
- test/client/data
|
54
|
-
- test/client/parser
|
55
|
-
- test/client/tc_auth.rb
|
56
|
-
- test/client/tc_client.rb
|
57
|
-
- test/client/tc_metadataindex.rb
|
58
|
-
- test/client/test_parser.rb
|
59
|
-
- test/client/ts_all.rb
|
60
52
|
- test/client/data/1.5
|
61
53
|
- test/client/data/1.5/error.xml
|
62
54
|
- test/client/data/1.5/invalid_compact.xml
|
@@ -64,8 +56,16 @@ files:
|
|
64
56
|
- test/client/data/1.5/metadata.xml
|
65
57
|
- test/client/data/1.5/search_compact.xml
|
66
58
|
- test/client/data/1.5/search_unescaped_compact.xml
|
59
|
+
- test/client/parser
|
67
60
|
- test/client/parser/tc_rexml.rb
|
68
61
|
- test/client/parser/tc_xmlparser.rb
|
62
|
+
- test/client/tc_auth.rb
|
63
|
+
- test/client/tc_client.rb
|
64
|
+
- test/client/tc_metadataindex.rb
|
65
|
+
- test/client/test_parser.rb
|
66
|
+
- test/client/ts_all.rb
|
67
|
+
- test/ts_all.rb
|
68
|
+
- test/ts_client.rb
|
69
69
|
- CONTRIBUTORS
|
70
70
|
- README
|
71
71
|
- LICENSE
|