rets4r 0.8.5 → 1.1.18

Sign up to get free protection for your applications and to get access to all the features.
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