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 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