rets4r 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
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)
@@ -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.
@@ -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 two objects out of the request"
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
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.2
7
- date: 2007-06-27 00:00:00 -07:00
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