rets4r 0.8.5 → 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.
Files changed (79) hide show
  1. data/.document +5 -0
  2. data/{test/client/data/1.5/metadata.xml → .gemtest} +0 -0
  3. data/CHANGELOG +611 -66
  4. data/CONTRIBUTORS +6 -2
  5. data/Gemfile +1 -0
  6. data/LICENSE +22 -0
  7. data/MANIFEST +63 -0
  8. data/NEWS +203 -0
  9. data/{README → README.rdoc} +11 -4
  10. data/RUBYS +7 -7
  11. data/Rakefile +48 -0
  12. data/TODO +5 -1
  13. data/examples/client_get_object.rb +31 -42
  14. data/examples/client_login.rb +20 -18
  15. data/examples/client_mapper.rb +17 -0
  16. data/examples/client_metadata.rb +28 -28
  17. data/examples/client_parser.rb +9 -0
  18. data/examples/client_search.rb +25 -27
  19. data/examples/settings.yml +114 -0
  20. data/lib/rets4r.rb +14 -1
  21. data/lib/rets4r/auth.rb +70 -66
  22. data/lib/rets4r/client.rb +470 -650
  23. data/lib/rets4r/client/data.rb +13 -13
  24. data/lib/rets4r/client/dataobject.rb +27 -19
  25. data/lib/rets4r/client/exceptions.rb +116 -0
  26. data/lib/rets4r/client/links.rb +32 -0
  27. data/lib/rets4r/client/metadata.rb +12 -12
  28. data/lib/rets4r/client/parsers/compact.rb +42 -0
  29. data/lib/rets4r/client/parsers/compact_nokogiri.rb +91 -0
  30. data/lib/rets4r/client/parsers/metadata.rb +92 -0
  31. data/lib/rets4r/client/parsers/response_parser.rb +100 -0
  32. data/lib/rets4r/client/requester.rb +143 -0
  33. data/lib/rets4r/client/transaction.rb +30 -33
  34. data/lib/rets4r/core_ext/array/extract_options.rb +15 -0
  35. data/lib/rets4r/core_ext/class/attribute_accessors.rb +58 -0
  36. data/lib/rets4r/core_ext/hash/keys.rb +46 -0
  37. data/lib/rets4r/core_ext/hash/slice.rb +39 -0
  38. data/lib/rets4r/listing_mapper.rb +17 -0
  39. data/lib/rets4r/listing_service.rb +35 -0
  40. data/lib/rets4r/loader.rb +8 -0
  41. data/lib/tasks/annotations.rake +121 -0
  42. data/lib/tasks/coverage.rake +13 -0
  43. data/rets4r.gemspec +24 -0
  44. data/spec/rets4r_compact_data_parser_spec.rb +7 -0
  45. data/test/data/1.5/bad_compact.xml +7 -0
  46. data/test/data/1.5/count_only_compact.xml +3 -0
  47. data/test/{client/data → data}/1.5/error.xml +0 -0
  48. data/test/{client/data → data}/1.5/invalid_compact.xml +0 -0
  49. data/test/{client/data → data}/1.5/login.xml +0 -0
  50. data/test/data/1.5/metadata.xml +0 -0
  51. data/test/{client/data → data}/1.5/search_compact.xml +0 -0
  52. data/test/data/1.5/search_compact_big.xml +136 -0
  53. data/test/{client/data → data}/1.5/search_unescaped_compact.xml +0 -0
  54. data/test/data/listing_service.yml +36 -0
  55. data/test/test_auth.rb +68 -0
  56. data/test/test_client.rb +342 -0
  57. data/test/test_client_links.rb +39 -0
  58. data/test/test_compact_nokogiri.rb +64 -0
  59. data/test/test_helper.rb +12 -0
  60. data/test/test_listing_mapper.rb +112 -0
  61. data/test/test_loader.rb +24 -0
  62. data/test/test_parser.rb +96 -0
  63. data/test/test_quality.rb +57 -0
  64. metadata +168 -53
  65. data/GPL +0 -340
  66. data/examples/metadata.xml +0 -42
  67. data/lib/rets4r/client/metadataindex.rb +0 -82
  68. data/lib/rets4r/client/parser.rb +0 -141
  69. data/lib/rets4r/client/parser/rexml.rb +0 -75
  70. data/lib/rets4r/client/parser/xmlparser.rb +0 -95
  71. data/test/client/parser/tc_rexml.rb +0 -17
  72. data/test/client/parser/tc_xmlparser.rb +0 -21
  73. data/test/client/tc_auth.rb +0 -68
  74. data/test/client/tc_client.rb +0 -320
  75. data/test/client/tc_metadataindex.rb +0 -36
  76. data/test/client/test_parser.rb +0 -128
  77. data/test/client/ts_all.rb +0 -8
  78. data/test/ts_all.rb +0 -1
  79. data/test/ts_client.rb +0 -1
@@ -1,141 +0,0 @@
1
- # This is the generic parser
2
- #
3
- # Supports responses, data, metadata, reply codes, and reply text.
4
- #
5
- # Supports the following tags:
6
- # RETS, METADATA-.*, MAXROWS, COLUMNS, DATA, COUNT, DELIMITER
7
- #
8
- # Metadata is built as:
9
- # (Metadata 1-> data row
10
- # -> data row),
11
- # (Metadata 2 -> data row),
12
- # etc.
13
- #
14
- # Data is built as:
15
- # Data 1, Data 2, Data N
16
- #
17
- #
18
- # TODO
19
- # Add comments/documentation
20
- # Handle more tags (if any are unaccounted for)
21
- # Handle standard (non-compact) output
22
- # Case Insensitivity?
23
- # There is still some holdovers from the previous organization of parsers, and they should be cleaned
24
- # up at some point.
25
-
26
- require 'cgi'
27
-
28
- module RETS4R
29
- class Client
30
- module Parser
31
- attr_accessor :output, :logger
32
-
33
- def initialize
34
- @transaction = Transaction.new
35
- @current = []
36
- @output = 2
37
- end
38
-
39
- def get_transaction
40
- @transaction
41
- end
42
-
43
- #### Stream Listener Events
44
- def tag_start(name, attrs)
45
- @current.push(name)
46
-
47
- case name
48
- when 'RETS'
49
- @transaction.reply_code = attrs['ReplyCode']
50
- @transaction.reply_text = attrs['ReplyText']
51
- when /METADATA.*/
52
- @metadata = Metadata.new(name)
53
- @metadata.attributes = attrs
54
- when 'MAXROWS'
55
- @transaction.maxrows = true
56
- when 'COUNT'
57
- @transaction.count = attrs['Records'].to_i
58
- when 'DELIMITER'
59
- @transaction.delimiter = attrs['value'].to_i
60
- end
61
- end
62
-
63
- def text(text)
64
- case @current[-1]
65
- when 'COLUMNS'
66
- @transaction.header = parse_compact_line(text, @transaction.ascii_delimiter)
67
-
68
- when 'DATA'
69
- if @transaction.header.length > 0
70
- data_type << parse_data(text, @transaction.header)
71
- else
72
- data_type << parse_compact_line(text, @transaction.ascii_delimiter)
73
- end
74
-
75
- when 'RETS-RESPONSE'
76
- @transaction.response = parse_key_value_body(text)
77
- end
78
- end
79
-
80
- def tag_end(name)
81
- @current.pop
82
-
83
- @transaction.data << @metadata if name =~ /METADATA.*/
84
- end
85
-
86
- #### Helper Methods
87
- def clean_xml(xml)
88
- # This is a hack, and it assumes that we're using compact mode, but it's the easiest way to
89
- # strip out those bad "<" and ">" characters that were not properly escaped...
90
- xml.gsub!(/<DATA>(.*)<\/DATA>/i) do |match|
91
- "<DATA>#{CGI::escapeHTML(CGI::unescapeHTML($1))}</DATA>"
92
- end
93
- end
94
-
95
- def data_type
96
- if @current[-2].index('METADATA') === 0
97
- return @metadata
98
- else
99
- return @transaction.data
100
- end
101
- end
102
-
103
- def parse_compact_line(data, delim = "\t")
104
- begin
105
- # We need to remove the beginning and ending delimiters prior to splitting
106
- string_data = data.to_s
107
- return string_data[1, string_data.length - 2].split(delim, -1)
108
- rescue
109
- raise "Error while parsing compact line: #{$!} with data: #{data}"
110
- end
111
- end
112
-
113
- def parse_data(data, header)
114
- results = Data.new(@current[-2])
115
-
116
- parsed_data = parse_compact_line(data, @transaction.ascii_delimiter)
117
-
118
- header.length.times do |pos|
119
- # The removal of delimiters in #parse_compact_line prevents blank fields in newer
120
- # version of Ruby, but on older versions (specifically 1.8.5 from 2006) a blank
121
- # field would still manage to sneak in, so we now explicitly prevent them from going
122
- # to the results.
123
- results[header[pos]] = parsed_data[pos] unless header[pos].nil? || header[pos].strip == ""
124
- end
125
-
126
- results
127
- end
128
-
129
- def parse_key_value_body(data)
130
- parsed = {}
131
-
132
- data.each do |line|
133
- (key, value) = line.strip.split('=')
134
- parsed[key] = value
135
- end
136
-
137
- return parsed
138
- end
139
- end
140
- end
141
- end
@@ -1,75 +0,0 @@
1
- require 'rets4r/client/parser'
2
- require 'rexml/parsers/baseparser'
3
- require 'rexml/parsers/streamparser'
4
- require 'rexml/streamlistener'
5
- require 'rets4r/client/transaction'
6
- require 'rets4r/client/data'
7
- require 'rets4r/client/metadata'
8
-
9
- module RETS4R
10
- class Client
11
- module Parser
12
- class REXML
13
- include Parser
14
-
15
- SUPPORTED_PARSERS << self
16
-
17
- attr_accessor :logger
18
-
19
- def initialize
20
- @transaction = Transaction.new
21
- @current = []
22
- end
23
-
24
- def parse(xml, output = false, do_retry = true)
25
- output = self.output unless output # Allow per-parse output changes
26
-
27
- return xml if output == OUTPUT_RAW
28
-
29
- # This is an legacy option that is not currently supported by XMLParser, but it is left in
30
- # here for reference or "undocumented usage."
31
- if output == OUTPUT_DOM
32
- return ::REXML::Document.new(xml)
33
- end
34
-
35
- # If we're here, then we need to output a RETS::Data object
36
- listener = StreamListener.new
37
-
38
- begin
39
- stream = ::REXML::Parsers::StreamParser.new(xml, listener)
40
- stream.parse
41
- rescue ::REXML::ParseException, Exception
42
- # We should get fancier here and actually check the type of error, but, err, oh well.
43
- if do_retry
44
- logger.info("Unable to parse XML on first try due to '#{$!}'. Now retrying.") if logger
45
-
46
- return parse(clean_xml(xml), output, false)
47
- else
48
- ex = ParserException.new($!)
49
- ex.file = xml
50
-
51
- logger.error("REXML parser was unable to parse XML: #{$!}") if logger
52
- logger.error("Unparsable XML was:\n#{xml}") if logger
53
-
54
- raise ex
55
- end
56
- end
57
-
58
- return listener.get_transaction
59
- end
60
-
61
- class StreamListener
62
- include ::REXML::StreamListener
63
- include Parser
64
-
65
- def initialize(logger = nil)
66
- self.logger = logger
67
- @transaction = Transaction.new
68
- @current = []
69
- @output = 2
70
- end
71
- end
72
- end
73
- end
74
- end
75
- end
@@ -1,95 +0,0 @@
1
- # Because XMLParser may not be present on this system, we attempt to require it and if it
2
- # succeeds, we create the XMLParser and add it to the supported parsers. The user of the client
3
- # can still switch to REXML if desired.
4
- begin
5
- require 'xml/parser'
6
- require 'rets4r/client/parser'
7
- require 'rets4r/client/transaction'
8
- require 'rets4r/client/data'
9
- require 'rets4r/client/metadata'
10
-
11
- module RETS4R
12
- class Client
13
- module Parser
14
- class XMLParser < XML::Parser
15
- include Parser
16
-
17
- SUPPORTED_PARSERS << self
18
-
19
- attr_accessor :logger
20
-
21
- def initialize
22
- @transaction = Transaction.new
23
- @current = []
24
- @text = ''
25
- end
26
-
27
- def parse(xml, output = false, do_retry = true)
28
- begin
29
- super(xml)
30
- rescue XMLParserError
31
- line = self.line
32
-
33
- # We should get fancier here and actually check the type of error, but, err, oh well.
34
- if do_retry
35
- # We probably received this error because somebody forgot to escape XML entities...
36
- # so we attempt to escape them ourselves...
37
- do_retry = false
38
-
39
- begin
40
- cleaned_xml = self.clean_xml(xml)
41
-
42
- # Because a cparser can only be used once per instantiation...
43
- retry_xml = self.class.new
44
- retry_xml.parse(cleaned_xml, output, do_retry)
45
-
46
- @transaction = retry_xml.get_transaction
47
-
48
- rescue
49
- ex = ParserException.new($!)
50
- ex.file = xml
51
-
52
- raise ex
53
- end
54
- else
55
- # We should never get here! But if we do...
56
- raise "You really shouldn't be seeing this error message! This means that there was an unexpected error: #{$!} (#{$!.class})"
57
- end
58
- end
59
-
60
- @transaction
61
- end
62
-
63
- def get_transaction
64
- @transaction
65
- end
66
-
67
- private
68
-
69
- #### Stream Listener Events
70
- def startElement(name, attrs)
71
- tag_start(name, attrs)
72
- end
73
-
74
- def character(text)
75
- @text += text
76
- end
77
-
78
- def processText()
79
- text(@text)
80
-
81
- @text = ''
82
- end
83
-
84
- def endElement(name)
85
- processText()
86
-
87
- tag_end(name)
88
- end
89
- end
90
- end
91
- end
92
- end
93
- rescue LoadError
94
- # CParser is not available because we could not load the XMLParser library
95
- end
@@ -1,17 +0,0 @@
1
- $:.unshift File.join(File.dirname(__FILE__), "../..", "lib")
2
-
3
- require 'test/unit'
4
- require 'rets4r/client/parser/rexml'
5
- require 'test/client/test_parser'
6
-
7
- module RETS4R
8
- class Client
9
- class TestRParser < Test::Unit::TestCase
10
- include TestParser
11
-
12
- def setup
13
- @parser = Parser::REXML.new
14
- end
15
- end
16
- end
17
- end
@@ -1,21 +0,0 @@
1
- $:.unshift File.join(File.dirname(__FILE__), "../..", "lib")
2
-
3
- require 'test/unit'
4
- require 'rets4r/client/parser/xmlparser'
5
- require 'test/client/test_parser'
6
-
7
- module RETS4R
8
- class Client
9
- if Module.constants.include?('XMLParser') && SUPPORTED_PARSERS.include?(Parser::XMLParser)
10
- class TestCParser < Test::Unit::TestCase
11
- include TestParser
12
-
13
- def setup
14
- @parser = Parser::XMLParser.new
15
- end
16
- end
17
- else
18
- puts "Skipping RETS4R XMLParser testing because XMLParser is not available."
19
- end
20
- end
21
- end
@@ -1,68 +0,0 @@
1
- $:.unshift File.join(File.dirname(__FILE__), "../..", "lib")
2
-
3
- require 'rets4r/auth'
4
- require 'test/unit'
5
-
6
- module RETS4R
7
- class TestAuth < Test::Unit::TestCase
8
- def setup
9
- @useragent = 'TestAgent/0.00'
10
- @username = 'username'
11
- @password = 'password'
12
- @realm = 'REALM'
13
- @nonce = '2006-03-03T17:37:10'
14
- end
15
-
16
- def test_authenticate
17
- response = {
18
- 'www-authenticate' => 'Digest qop="auth",realm="'+ @realm +'",nonce="'+ @nonce +'",opaque="",stale="false",domain="\my\test\domain"'
19
- }
20
-
21
- Auth.authenticate(response, @username, @password, '/my/rets/url', 'GET', Auth.request_id, @useragent)
22
- end
23
-
24
- # test without spacing
25
- def test_parse_auth_header_without_spacing
26
- header = 'Digest qop="auth",realm="'+ @realm +'",nonce="'+ @nonce +'",opaque="",stale="false",domain="\my\test\domain"'
27
- check_header(header)
28
- end
29
-
30
- # test with spacing between each item
31
- def test_parse_auth_header_with_spacing
32
- header = 'Digest qop="auth", realm="'+ @realm +'", nonce="'+ @nonce +'", opaque="", stale="false", domain="\my\test\domain"'
33
- check_header(header)
34
- end
35
-
36
- # used to check the that the header was processed properly.
37
- def check_header(header)
38
- results = Auth.parse_header(header)
39
-
40
- assert_equal('auth', results['qop'])
41
- assert_equal('REALM', results['realm'])
42
- assert_equal('2006-03-03T17:37:10', results['nonce'])
43
- assert_equal('', results['opaque'])
44
- assert_equal('false', results['stale'])
45
- assert_equal('\my\test\domain', results['domain'])
46
- end
47
-
48
- def test_calculate_digest
49
- # with qop
50
- assert_equal('c5f9ef280f0ca78ed7a488158fc2f4cc', Auth.calculate_digest(@username, \
51
- @password, @realm, 'test', 'GET', '/my/rets/url', true, 'test'))
52
-
53
- # without qop
54
- assert_equal('bceafa34467a3519c2f6295d4800f4ea', Auth.calculate_digest(@username, \
55
- @password, @realm, 'test', 'GET', '/my/rets/url', false))
56
- end
57
-
58
- def test_request_id
59
- assert_not_nil(true, Auth.request_id)
60
- end
61
-
62
- def test_cnonce
63
- # We call cnonce with a static request ID so that we have a consistent result with which
64
- # to test against
65
- assert_equal('d5cdfa1acffde590d263689fb40cf53c', Auth.cnonce(@useragent, @password, 'requestId', @nonce))
66
- end
67
- end
68
- end
@@ -1,320 +0,0 @@
1
- $:.unshift File.join(File.dirname(__FILE__), "../..", "lib")
2
-
3
- require 'rets4r/client'
4
- require 'test/unit'
5
- require 'stringio'
6
- require 'logger'
7
- require 'rubygems' rescue nil
8
- require 'mocha'
9
-
10
- module RETS4R
11
- class Client
12
- public :process_content_type
13
- end
14
-
15
- class TestClientGetMetadata < Test::Unit::TestCase
16
- RETS_PORT = '9080'
17
- RETS_URL = "http://localhost:#{RETS_PORT}"
18
- RETS_LOGIN = 'login'
19
- RETS_PASSWORD = 'password'
20
-
21
- class CustomError < StandardError; end
22
-
23
- def setup
24
- @logfile = StringIO.open
25
- @rets = RETS4R::Client.new(RETS_URL)
26
- @rets.logger = Logger.new(@logfile)
27
- @rets.logger.level = Logger::DEBUG
28
-
29
- @rets.stubs(:request).returns(@response = mock("response"))
30
- @response.stubs(:body).returns(:body)
31
- @rets.stubs(:parse).returns(@results = mock("results"))
32
- end
33
-
34
- def teardown
35
- @logfile.close
36
- end
37
-
38
- def test_get_metadata_yields_the_results_if_given_a_block
39
- @rets.expects(:parse).returns(@results = mock("results"))
40
-
41
- in_block = false
42
- @rets.get_metadata do |results|
43
- in_block = true
44
- assert_equal @results, results
45
- end
46
-
47
- assert in_block, "Block was never yielded to"
48
- end
49
-
50
- def test_get_metadata_returns_the_metadata_when_no_block_given
51
- rval = @rets.get_metadata
52
-
53
- assert_equal @results, rval
54
- end
55
-
56
- def test_get_metadata_returns_the_blocks_value_when_given_a_block
57
- rval = @rets.get_metadata do |results|
58
- :block_value
59
- end
60
-
61
- assert_equal :block_value, rval
62
- end
63
- end
64
-
65
- class TestClientGetObject < Test::Unit::TestCase
66
- RETS_PORT = '9080'
67
- RETS_URL = "http://localhost:#{RETS_PORT}"
68
- RETS_LOGIN = 'login'
69
- RETS_PASSWORD = 'password'
70
-
71
- class CustomError < StandardError; end
72
-
73
- def setup
74
- @logfile = StringIO.open
75
- @rets = RETS4R::Client.new(RETS_URL)
76
- @rets.logger = Logger.new(@logfile)
77
- @rets.logger.level = Logger::DEBUG
78
-
79
- @rets.stubs(:request).returns(@response = mock("response"))
80
- end
81
-
82
- def test_returns_multipart_parallel_objects_in_a_single_array
83
- @response.expects(:[]).with('content-type').at_least_once.returns("multipart/parallel; boundary=1231")
84
- @response.expects(:body).returns("\r\n--1231\r\nContent-ID: 392103\r\nObject-ID: 1\r\nContent-Type: image/jpeg\r\n\r\n" + "\000"*120 + "\r\n--1231\r\nContent-ID: 392103\r\nObject-ID: 2\r\nContent-Type: image/gif\r\n\r\n" + "\000"*140 + "\r\n--1231--")
85
- results = @rets.get_object("Property", "Photo", "392103:*")
86
- assert_equal 2, results.size, "Client parsed two objects out of the request"
87
- assert_kind_of RETS4R::Client::DataObject, results[0], "First result isn't a DataObject"
88
- assert_kind_of RETS4R::Client::DataObject, results[1], "Second result isn't a DataObject"
89
- assert_equal "image/jpeg", results[0].type["Content-Type"], "First object isn't an image/jpeg"
90
- assert_equal 120, results[0].data.size, "First object isn't 120 bytes in length"
91
- assert_equal "image/gif", results[1].type["Content-Type"], "Second object isn't an image/gif"
92
- assert_equal 140, results[1].data.size, "Second object isn't 140 bytes in length"
93
- end
94
-
95
- def test_returns_single_entity_object_in_a_single_element_array
96
- @response.expects(:[]).with('content-type').at_least_once.returns("image/jpeg")
97
- @response.expects(:[]).with('Transfer-Encoding').at_least_once.returns("")
98
- @response.expects(:[]).with('Content-Length').at_least_once.returns(241)
99
- @response.expects(:[]).with('Object-ID').at_least_once.returns("25478")
100
- @response.expects(:[]).with('Content-ID').at_least_once.returns("5589")
101
- @response.expects(:body).returns("\000"*241)
102
-
103
- results = @rets.get_object("Property", "Photo", "392103:*")
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"
121
- assert_kind_of RETS4R::Client::DataObject, results[0], "First result isn't a DataObject"
122
- assert_equal "image/jpeg", results[0].type["Content-Type"], "Content-Type not copied"
123
- assert_equal "5589", results[0].type["Content-ID"], "Content-ID not copied"
124
- assert_equal "25478", results[0].type["Object-ID"], "Object-ID not copied"
125
- assert_equal 241, results[0].data.size, "First object isn't 241 bytes in length"
126
- end
127
-
128
- def test_yields_data_objects_to_block_and_returns_blocks_value
129
- @response.expects(:[]).with('content-type').at_least_once.returns("image/jpeg")
130
- @response.expects(:[]).with('Transfer-Encoding').at_least_once.returns("")
131
- @response.expects(:[]).with('Content-Length').at_least_once.returns(241)
132
- @response.expects(:[]).with('Object-ID').at_least_once.returns("25478")
133
- @response.expects(:[]).with('Content-ID').at_least_once.returns("5589")
134
- @response.expects(:body).returns("\000"*241)
135
-
136
- yielded_count = 0
137
-
138
- value = @rets.get_object("Property", "VRImage", "912:0") do |result|
139
- assert_kind_of RETS4R::Client::DataObject, result
140
- yielded_count += 1
141
- :return_value
142
- end
143
-
144
- assert_equal yielded_count, value
145
- end
146
- end
147
-
148
- class TestClientLogin < Test::Unit::TestCase
149
- RETS_PORT = '9080'
150
- RETS_URL = "http://localhost:#{RETS_PORT}"
151
- RETS_LOGIN = 'login'
152
- RETS_PASSWORD = 'password'
153
-
154
- class CustomError < StandardError; end
155
-
156
- def setup
157
- @logfile = StringIO.open
158
- @rets = RETS4R::Client.new(RETS_URL)
159
- @rets.logger = Logger.new(@logfile)
160
- @rets.logger.level = Logger::DEBUG
161
-
162
- @rets.stubs(:request).returns(@response = mock("response"))
163
- @response.stubs(:body).returns(:body)
164
- @rets.stubs(:parse).returns(@results = mock("results"))
165
- @results.stubs(:success?).returns(true)
166
- @results.stubs(:response).returns(Hash.new)
167
- @results.stubs(:secondary_response=)
168
- end
169
-
170
- def teardown
171
- @logfile.close
172
- end
173
-
174
- def test_successful_login_yields_the_results_to_the_block
175
- @rets.expects(:request).with {|arg| arg.kind_of?(URI)}.returns(@response)
176
- @rets.expects(:parse).with(:body, RETS4R::Client::OUTPUT_RUBY).returns(@results)
177
- @results.expects(:success?).returns(true)
178
- @rets.expects(:logout)
179
-
180
- in_block = false
181
- @rets.login("user", "pass") do |results|
182
- assert_equal @results, results
183
- in_block = true
184
- end
185
-
186
- assert in_block, "Block was never yielded to"
187
- end
188
-
189
- def test_logout_called_after_block_execution_if_block_raises
190
- assert_raises(CustomError) do
191
- @rets.expects(:logout)
192
- @rets.login("user", "pass") do |results|
193
- raise CustomError
194
- end
195
- end
196
- end
197
-
198
- def test_login_returns_the_blocks_value
199
- rval = @rets.login("user", "pass") do |results|
200
- :value
201
- end
202
-
203
- assert_equal :value, rval
204
- end
205
-
206
- def test_login_without_a_block_returns_the_results
207
- results = @rets.login("user", "pass")
208
- assert_equal @results, results
209
- end
210
- end
211
-
212
- class TestClient < Test::Unit::TestCase
213
- RETS_PORT = '9080'
214
- RETS_URL = "http://localhost:#{RETS_PORT}"
215
- RETS_LOGIN = 'login'
216
- RETS_PASSWORD = 'password'
217
-
218
- def setup
219
- @logfile = StringIO.open
220
- @rets = RETS4R::Client.new(RETS_URL)
221
- @rets.logger = Logger.new(@logfile)
222
- @rets.logger.level = Logger::DEBUG
223
- end
224
-
225
- def teardown
226
- @logfile.close
227
- end
228
-
229
- def test_setup
230
- assert_nothing_raised() { @rets.set_user_agent('ACK/2.1') }
231
- assert_equal('ACK/2.1', @rets.user_agent)
232
-
233
- assert_nothing_raised() { @rets.user_agent = 'SPRETS/0.1' }
234
- assert_nothing_raised() { @rets.set_request_method('GET') }
235
-
236
- assert_raise(RETS4R::Client::Unsupported) { @rets.set_rets_version('1.4.0') }
237
- assert_nothing_raised() { @rets.set_rets_version('1.5') }
238
- assert_equal("1.5", @rets.rets_version)
239
- assert_equal("RETS/1.5", @rets.get_header("RETS-Version"))
240
- assert_nothing_raised() { @rets.rets_version = '1.7' }
241
- assert_equal("RETS/1.7", @rets.get_header("RETS-Version"))
242
-
243
- assert_equal('SPRETS/0.1', @rets.get_user_agent)
244
- assert_equal('GET', @rets.get_request_method)
245
- assert_equal('1.7', @rets.get_rets_version)
246
-
247
- assert_nothing_raised() { @rets.request_method = 'POST' }
248
-
249
- assert_equal('POST', @rets.request_method)
250
-
251
- assert_nothing_raised() { @rets.set_parser_class(Client::Parser::REXML) }
252
- assert_raise(Client::Unsupported) { @rets.parser_class = MockParser }
253
- assert_nothing_raised() { @rets.set_parser_class(MockParser, true) }
254
- assert_equal(MockParser, @rets.parser_class)
255
-
256
- assert_nothing_raised() { @rets.set_output(RETS4R::Client::OUTPUT_RAW) }
257
- assert_equal(RETS4R::Client::OUTPUT_RAW, @rets.output)
258
- assert_nothing_raised() { @rets.output = RETS4R::Client::OUTPUT_RUBY }
259
- assert_equal(RETS4R::Client::OUTPUT_RUBY, @rets.get_output)
260
-
261
- # Check that our changes were logged when in debug mode
262
- assert @logfile.length > 0
263
- end
264
-
265
- # Just to make sure that we're okay when we don't have a logger, we set it to nil and
266
- # make a change that would trigger a debug mode log.
267
- def test_without_logger
268
- @rets.logger = nil
269
-
270
- assert_nothing_raised() { @rets.set_request_method('GET') }
271
- end
272
-
273
- def test_content_type_parsing
274
- ct = 'multipart/parallel; boundary=cc2631bb.0165.3b32.8a7d.a8453f662101; charset=utf-8'
275
-
276
- results = @rets.process_content_type(ct)
277
-
278
- assert_equal('cc2631bb.0165.3b32.8a7d.a8453f662101', results['boundary'])
279
- assert_equal('multipart/parallel', results['content-type'])
280
- assert_equal('utf-8', results['charset'])
281
- end
282
-
283
- def test_performs_get_request
284
- assert_nothing_raised() {@rets.request_method = 'GET'}
285
- assert_equal('GET', @rets.request_method)
286
-
287
- http = mock('http')
288
- response = mock('response')
289
- response.stubs(:to_hash).returns({})
290
- response.stubs(:code).returns('500')
291
- response.stubs(:message).returns('Move along, nothing to see here.')
292
-
293
- http.expects(:get).with('', {'RETS-Session-ID' => '0', 'User-Agent' => @rets.user_agent, 'RETS-Version' => "RETS/#{@rets.rets_version}", 'Accept' => '*/*'}).at_least_once.returns(response)
294
- http.expects(:post).never
295
- Net::HTTP.any_instance.expects(:start).at_least_once.yields(http)
296
-
297
- assert_raises(RETS4R::Client::HTTPError) {@rets.login('user', 'pass')}
298
- end
299
-
300
- def test_performs_post_request
301
- assert_nothing_raised() {@rets.request_method = 'POST'}
302
- assert_equal('POST', @rets.request_method)
303
-
304
- http = mock('http')
305
- response = mock('response')
306
- response.stubs(:to_hash).returns({})
307
- response.stubs(:code).returns('500')
308
- response.stubs(:message).returns('Move along, nothing to see here.')
309
-
310
- http.expects(:post).with('', '', {'RETS-Session-ID' => '0', 'User-Agent' => @rets.user_agent, 'RETS-Version' => "RETS/#{@rets.rets_version}", 'Accept' => '*/*'}).at_least_once.returns(response)
311
- http.expects(:get).never
312
- Net::HTTP.any_instance.expects(:start).at_least_once.yields(http)
313
-
314
- assert_raises(RETS4R::Client::HTTPError) {@rets.login('user', 'pass')}
315
- end
316
-
317
- class MockParser
318
- end
319
- end
320
- end