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.
- data/.document +5 -0
- data/{test/client/data/1.5/metadata.xml → .gemtest} +0 -0
- data/CHANGELOG +611 -66
- data/CONTRIBUTORS +6 -2
- data/Gemfile +1 -0
- data/LICENSE +22 -0
- data/MANIFEST +63 -0
- data/NEWS +203 -0
- data/{README → README.rdoc} +11 -4
- data/RUBYS +7 -7
- data/Rakefile +48 -0
- data/TODO +5 -1
- data/examples/client_get_object.rb +31 -42
- data/examples/client_login.rb +20 -18
- data/examples/client_mapper.rb +17 -0
- data/examples/client_metadata.rb +28 -28
- data/examples/client_parser.rb +9 -0
- data/examples/client_search.rb +25 -27
- data/examples/settings.yml +114 -0
- data/lib/rets4r.rb +14 -1
- data/lib/rets4r/auth.rb +70 -66
- data/lib/rets4r/client.rb +470 -650
- data/lib/rets4r/client/data.rb +13 -13
- data/lib/rets4r/client/dataobject.rb +27 -19
- data/lib/rets4r/client/exceptions.rb +116 -0
- data/lib/rets4r/client/links.rb +32 -0
- data/lib/rets4r/client/metadata.rb +12 -12
- data/lib/rets4r/client/parsers/compact.rb +42 -0
- data/lib/rets4r/client/parsers/compact_nokogiri.rb +91 -0
- data/lib/rets4r/client/parsers/metadata.rb +92 -0
- data/lib/rets4r/client/parsers/response_parser.rb +100 -0
- data/lib/rets4r/client/requester.rb +143 -0
- data/lib/rets4r/client/transaction.rb +30 -33
- data/lib/rets4r/core_ext/array/extract_options.rb +15 -0
- data/lib/rets4r/core_ext/class/attribute_accessors.rb +58 -0
- data/lib/rets4r/core_ext/hash/keys.rb +46 -0
- data/lib/rets4r/core_ext/hash/slice.rb +39 -0
- data/lib/rets4r/listing_mapper.rb +17 -0
- data/lib/rets4r/listing_service.rb +35 -0
- data/lib/rets4r/loader.rb +8 -0
- data/lib/tasks/annotations.rake +121 -0
- data/lib/tasks/coverage.rake +13 -0
- data/rets4r.gemspec +24 -0
- data/spec/rets4r_compact_data_parser_spec.rb +7 -0
- data/test/data/1.5/bad_compact.xml +7 -0
- data/test/data/1.5/count_only_compact.xml +3 -0
- data/test/{client/data → data}/1.5/error.xml +0 -0
- data/test/{client/data → data}/1.5/invalid_compact.xml +0 -0
- data/test/{client/data → data}/1.5/login.xml +0 -0
- data/test/data/1.5/metadata.xml +0 -0
- data/test/{client/data → data}/1.5/search_compact.xml +0 -0
- data/test/data/1.5/search_compact_big.xml +136 -0
- data/test/{client/data → data}/1.5/search_unescaped_compact.xml +0 -0
- data/test/data/listing_service.yml +36 -0
- data/test/test_auth.rb +68 -0
- data/test/test_client.rb +342 -0
- data/test/test_client_links.rb +39 -0
- data/test/test_compact_nokogiri.rb +64 -0
- data/test/test_helper.rb +12 -0
- data/test/test_listing_mapper.rb +112 -0
- data/test/test_loader.rb +24 -0
- data/test/test_parser.rb +96 -0
- data/test/test_quality.rb +57 -0
- metadata +168 -53
- data/GPL +0 -340
- data/examples/metadata.xml +0 -42
- data/lib/rets4r/client/metadataindex.rb +0 -82
- data/lib/rets4r/client/parser.rb +0 -141
- data/lib/rets4r/client/parser/rexml.rb +0 -75
- data/lib/rets4r/client/parser/xmlparser.rb +0 -95
- data/test/client/parser/tc_rexml.rb +0 -17
- data/test/client/parser/tc_xmlparser.rb +0 -21
- data/test/client/tc_auth.rb +0 -68
- data/test/client/tc_client.rb +0 -320
- data/test/client/tc_metadataindex.rb +0 -36
- data/test/client/test_parser.rb +0 -128
- data/test/client/ts_all.rb +0 -8
- data/test/ts_all.rb +0 -1
- data/test/ts_client.rb +0 -1
data/lib/rets4r/client/parser.rb
DELETED
@@ -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
|
data/test/client/tc_auth.rb
DELETED
@@ -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
|
data/test/client/tc_client.rb
DELETED
@@ -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
|