rets 0.10.0 → 0.11.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +2 -0
- data/Rakefile +1 -1
- data/lib/rets.rb +1 -1
- data/lib/rets/client.rb +28 -7
- data/lib/rets/client_progress_reporter.rb +2 -2
- data/lib/rets/metadata/lookup_table.rb +2 -2
- data/lib/rets/metadata/lookup_type.rb +2 -2
- data/lib/rets/metadata/multi_lookup_table.rb +1 -1
- data/lib/rets/metadata/resource.rb +7 -2
- data/lib/rets/metadata/rets_class.rb +1 -1
- data/lib/rets/metadata/rets_object.rb +17 -8
- data/lib/rets/metadata/table.rb +1 -1
- data/lib/rets/parser/compact.rb +20 -1
- data/test/fixtures.rb +11 -2
- data/test/test_client.rb +12 -16
- data/test/test_metadata.rb +1 -1
- data/test/test_metadata_lookup_type.rb +9 -0
- data/test/test_metadata_object.rb +17 -4
- data/test/test_metadata_resource.rb +9 -1
- data/test/test_parser_compact.rb +21 -0
- metadata +9 -8
- data/.gemtest +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 966f5afd3517f512f4a54b235b7e52aa5437c7b7
|
4
|
+
data.tar.gz: 5b29080c3a5c2a6c48c2b4e8c5d32cd51b90add9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb19f04bc071b36c147cc697b4b0c616be5c47a2e0919f9289d6fac7f4d112f356a0fbe25ca5a0fb56dbac62c8c3aa0d11438411ccafe43c2d5f16972f3f4990
|
7
|
+
data.tar.gz: 9d64e1063bbd640985bc0820c58e403b1391d62838bd92f353719caa7805de98e1188e196a96af5f5e92c55691ebeb8c1ee25eb54e26640c6e6aaf01c89be40b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
### 0.11.0 / NOT RELEASED YET
|
2
|
+
|
3
|
+
* fix: fix retry logging
|
4
|
+
* feature: allow retries to be configured for all query types in client settings
|
5
|
+
* feature: allow configrable wait time between retries
|
6
|
+
* feature: detect errors as error messages in a response body delivered with HTTP 200
|
7
|
+
|
8
|
+
### 0.10.1 / 2016-05-04
|
9
|
+
|
10
|
+
* fix: handle invalid codepoints in character references
|
11
|
+
|
1
12
|
### 0.10.0 / 2016-02-29
|
2
13
|
|
3
14
|
* fix: ensure cookie store exists #133
|
data/README.md
CHANGED
@@ -7,6 +7,8 @@
|
|
7
7
|
[](http://travis-ci.org/estately/rets)
|
8
8
|
A pure-ruby library for fetching data from [RETS] servers.
|
9
9
|
|
10
|
+
If you're looking for a slick CLI interface check out [retscli](https://github.com/summera/retscli), which is an awesome tool for exploring metadata or learning about RETS.
|
11
|
+
|
10
12
|
[RETS]: http://www.rets.org
|
11
13
|
|
12
14
|
## REQUIREMENTS:
|
data/Rakefile
CHANGED
@@ -13,7 +13,7 @@ Hoe.spec 'rets' do
|
|
13
13
|
extra_deps << [ "http-cookie", "~> 1.0.0" ]
|
14
14
|
extra_deps << [ "nokogiri", "~> 1.5" ]
|
15
15
|
|
16
|
-
extra_dev_deps << [ "mocha", "~> 0
|
16
|
+
extra_dev_deps << [ "mocha", "~> 1.1.0" ]
|
17
17
|
extra_dev_deps << [ "vcr", "~> 2.2" ]
|
18
18
|
extra_dev_deps << [ "webmock", "~> 1.8" ]
|
19
19
|
|
data/lib/rets.rb
CHANGED
data/lib/rets/client.rb
CHANGED
@@ -13,6 +13,9 @@ module Rets
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def clean_setup
|
16
|
+
if options.fetch(:login_after_error, true)
|
17
|
+
@capabilities = nil
|
18
|
+
end
|
16
19
|
@metadata = nil
|
17
20
|
@tries = nil
|
18
21
|
@login_url = options[:login_url]
|
@@ -102,9 +105,11 @@ module Rets
|
|
102
105
|
end
|
103
106
|
|
104
107
|
def handle_find_failure(retries, opts, e)
|
105
|
-
|
108
|
+
max_retries = fetch_max_retries(opts)
|
109
|
+
if retries < max_retries
|
106
110
|
retries += 1
|
107
|
-
|
111
|
+
wait_before_next_request
|
112
|
+
client_progress.find_with_retries_failed_a_retry(e, retries, max_retries)
|
108
113
|
clean_setup
|
109
114
|
find_with_given_retry(retries, opts)
|
110
115
|
else
|
@@ -113,6 +118,18 @@ module Rets
|
|
113
118
|
end
|
114
119
|
end
|
115
120
|
|
121
|
+
def fetch_max_retries(hash)
|
122
|
+
hash[:max_retries] || options.fetch(:max_retries, 3)
|
123
|
+
end
|
124
|
+
|
125
|
+
def wait_before_next_request
|
126
|
+
sleep_time = Float(options.fetch(:recoverable_error_wait_secs, 0))
|
127
|
+
if sleep_time > 0
|
128
|
+
logger.info "Waiting #{sleep_time} seconds before next attempt"
|
129
|
+
sleep sleep_time
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
116
133
|
def find_every(opts)
|
117
134
|
raise ArgumentError.new("missing option :search_type (provide the name of a RETS resource)") unless opts[:search_type]
|
118
135
|
raise ArgumentError.new("missing option :class (provide the name of a RETS class)") unless opts[:class]
|
@@ -120,7 +137,6 @@ module Rets
|
|
120
137
|
params = {
|
121
138
|
"SearchType" => opts.fetch(:search_type),
|
122
139
|
"Class" => opts.fetch(:class),
|
123
|
-
|
124
140
|
"Count" => opts[:count],
|
125
141
|
"Format" => opts.fetch(:format, "COMPACT"),
|
126
142
|
"Limit" => opts[:limit],
|
@@ -132,13 +148,13 @@ module Rets
|
|
132
148
|
"Query" => opts[:query],
|
133
149
|
"QueryType" => opts.fetch(:query_type, "DMQL2"),
|
134
150
|
}.reject { |k,v| v.nil? }
|
135
|
-
res = http_post(capability_url("Search"), params)
|
151
|
+
res = clean_response(http_post(capability_url("Search"), params))
|
136
152
|
|
137
153
|
if opts[:count] == COUNT.only
|
138
154
|
Parser::Compact.get_count(res.body)
|
139
155
|
else
|
140
156
|
results = Parser::Compact.parse_document(
|
141
|
-
res.body
|
157
|
+
res.body
|
142
158
|
)
|
143
159
|
if opts[:resolve]
|
144
160
|
rets_class = find_rets_class(opts[:search_type], opts[:class])
|
@@ -269,7 +285,7 @@ module Rets
|
|
269
285
|
"Type" => "METADATA-#{type}",
|
270
286
|
"ID" => "0"
|
271
287
|
})
|
272
|
-
res.body
|
288
|
+
clean_response(res).body
|
273
289
|
end
|
274
290
|
|
275
291
|
# The capabilies as provided by the RETS server during login.
|
@@ -330,7 +346,12 @@ module Rets
|
|
330
346
|
end
|
331
347
|
|
332
348
|
def http_get(url, params=nil, extra_headers={})
|
333
|
-
@http_client.http_get(url, params, extra_headers)
|
349
|
+
clean_response(@http_client.http_get(url, params, extra_headers))
|
350
|
+
end
|
351
|
+
|
352
|
+
def clean_response(res)
|
353
|
+
res.body.encode!("UTF-8", res.body.encoding, :invalid => :replace, :undef => :replace)
|
354
|
+
res
|
334
355
|
end
|
335
356
|
|
336
357
|
def http_post(url, params, extra_headers = {})
|
@@ -18,10 +18,10 @@ module Rets
|
|
18
18
|
@stats_prefix = stats_prefix
|
19
19
|
end
|
20
20
|
|
21
|
-
def find_with_retries_failed_a_retry(exception, retries)
|
21
|
+
def find_with_retries_failed_a_retry(exception, retries, max_retries)
|
22
22
|
@stats.count("#{@stats_prefix}find_with_retries_failed_retry")
|
23
23
|
@logger.warn("Rets::Client: Failed with message: #{exception.message}")
|
24
|
-
@logger.info("Rets::Client: Retry #{retries}
|
24
|
+
@logger.info("Rets::Client: Retry #{retries}/#{max_retries}")
|
25
25
|
end
|
26
26
|
|
27
27
|
def find_with_retries_exceeded_retry_count(exception)
|
@@ -22,7 +22,7 @@ module Rets
|
|
22
22
|
#
|
23
23
|
# [out] The file to print to. Defaults to $stdout.
|
24
24
|
def print_tree(out = $stdout)
|
25
|
-
out.puts "
|
25
|
+
out.puts "### LookupTable: #{name}"
|
26
26
|
out.puts " Resource: #{resource_id}"
|
27
27
|
out.puts " Required: #{table_fragment['Required']}"
|
28
28
|
out.puts " Searchable: #{ table_fragment["Searchable"] }"
|
@@ -30,7 +30,7 @@ module Rets
|
|
30
30
|
out.puts " ShortName: #{ table_fragment["ShortName"] }"
|
31
31
|
out.puts " LongName: #{ long_name }"
|
32
32
|
out.puts " StandardName: #{ table_fragment["StandardName"] }"
|
33
|
-
out.puts "
|
33
|
+
out.puts "#### Types:"
|
34
34
|
|
35
35
|
lookup_types.each do |lookup_type|
|
36
36
|
lookup_type.print_tree(out)
|
@@ -4,8 +4,8 @@ module Rets
|
|
4
4
|
attr_reader :long_value, :value
|
5
5
|
|
6
6
|
def initialize(lookup_type_fragment)
|
7
|
-
@value = lookup_type_fragment["Value"]
|
8
|
-
@long_value = lookup_type_fragment["LongValue"]
|
7
|
+
@value = lookup_type_fragment["Value"].strip
|
8
|
+
@long_value = lookup_type_fragment["LongValue"].strip
|
9
9
|
end
|
10
10
|
|
11
11
|
# Print the tree to a file
|
@@ -22,7 +22,7 @@ module Rets
|
|
22
22
|
#
|
23
23
|
# [out] The file to print to. Defaults to $stdout.
|
24
24
|
def print_tree(out = $stdout)
|
25
|
-
out.puts "
|
25
|
+
out.puts "### MultiLookupTable: #{name}"
|
26
26
|
out.puts " Resource: #{resource_id}"
|
27
27
|
out.puts " Required: #{table_fragment['Required']}"
|
28
28
|
out.puts " Searchable: #{ table_fragment["Searchable"] }"
|
@@ -29,7 +29,12 @@ module Rets
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.find_rets_objects(metadata, resource_id)
|
32
|
-
metadata[:object]
|
32
|
+
objects = metadata[:object]
|
33
|
+
if objects
|
34
|
+
objects.select { |object| object.resource == resource_id }.map(&:objects).flatten
|
35
|
+
else
|
36
|
+
[]
|
37
|
+
end
|
33
38
|
end
|
34
39
|
|
35
40
|
def self.build_lookup_tree(resource_id, metadata)
|
@@ -80,7 +85,7 @@ module Rets
|
|
80
85
|
#
|
81
86
|
# [out] The file to print to. Defaults to $stdout.
|
82
87
|
def print_tree(out = $stdout)
|
83
|
-
out.puts "Resource: #{id} (Key Field: #{key_field})"
|
88
|
+
out.puts "# Resource: #{id} (Key Field: #{key_field})"
|
84
89
|
rets_classes.each do |rets_class|
|
85
90
|
rets_class.print_tree(out)
|
86
91
|
end
|
@@ -41,7 +41,7 @@ module Rets
|
|
41
41
|
#
|
42
42
|
# [out] The file to print to. Defaults to $stdout.
|
43
43
|
def print_tree(out = $stdout)
|
44
|
-
out.puts "
|
44
|
+
out.puts "## Class: #{name}"
|
45
45
|
out.puts " Visible Name: #{visible_name}"
|
46
46
|
out.puts " Description : #{description}"
|
47
47
|
tables.each do |table|
|
@@ -1,24 +1,28 @@
|
|
1
1
|
module Rets
|
2
2
|
module Metadata
|
3
3
|
class RetsObject
|
4
|
-
attr_reader :name, :mime_type, :description
|
4
|
+
attr_reader :name, :mime_type, :description, :type
|
5
5
|
|
6
|
-
def initialize(name, mime_type, description)
|
6
|
+
def initialize(type, name, mime_type, description)
|
7
7
|
@name = name
|
8
8
|
@mime_type = mime_type
|
9
9
|
@description = description
|
10
|
+
@type = type
|
10
11
|
end
|
11
12
|
|
12
13
|
def self.build(rets_object_fragment)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
rets_object_fragment = downcase_hash_keys(rets_object_fragment)
|
15
|
+
name = rets_object_fragment["visiblename"]
|
16
|
+
mime_type = rets_object_fragment["mimetype"]
|
17
|
+
description = rets_object_fragment["description"]
|
18
|
+
type = rets_object_fragment['objecttype']
|
19
|
+
new(type, name, mime_type, description)
|
17
20
|
end
|
18
21
|
|
19
22
|
def print_tree(out = $stdout)
|
20
|
-
out.puts " Object: #{
|
21
|
-
out.puts "
|
23
|
+
out.puts " Object: #{type}"
|
24
|
+
out.puts " Visible Name: #{name}"
|
25
|
+
out.puts " Mime Type: #{mime_type}"
|
22
26
|
out.puts " Description: #{description}"
|
23
27
|
end
|
24
28
|
|
@@ -27,6 +31,11 @@ module Rets
|
|
27
31
|
mime_type == other.mime_type &&
|
28
32
|
description == other.description
|
29
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def self.downcase_hash_keys(hash)
|
37
|
+
Hash[hash.map { |k, v| [k.downcase, v] }]
|
38
|
+
end
|
30
39
|
end
|
31
40
|
end
|
32
41
|
end
|
data/lib/rets/metadata/table.rb
CHANGED
@@ -15,7 +15,7 @@ module Rets
|
|
15
15
|
#
|
16
16
|
# [out] The file to print to. Defaults to $stdout.
|
17
17
|
def print_tree(out = $stdout)
|
18
|
-
out.puts "
|
18
|
+
out.puts "### Table: #{name}"
|
19
19
|
out.puts " Resource: #{resource_id}"
|
20
20
|
out.puts " ShortName: #{ table_fragment["ShortName"] }"
|
21
21
|
out.puts " LongName: #{ long_name }"
|
data/lib/rets/parser/compact.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
require 'cgi'
|
3
|
+
|
3
4
|
module Rets
|
4
5
|
module Parser
|
5
6
|
class Compact
|
@@ -76,7 +77,10 @@ module Rets
|
|
76
77
|
end
|
77
78
|
|
78
79
|
column_names = columns.split(delimiter)
|
79
|
-
data_values = data.split(delimiter, INCLUDE_NULL_FIELDS).map
|
80
|
+
data_values = data.split(delimiter, INCLUDE_NULL_FIELDS).map do |x|
|
81
|
+
safely_decode_character_references!(x)
|
82
|
+
CGI.unescape_html(x)
|
83
|
+
end
|
80
84
|
|
81
85
|
zipped_key_values = column_names.zip(data_values).map { |k, v| [k.freeze, v.to_s] }
|
82
86
|
|
@@ -84,6 +88,21 @@ module Rets
|
|
84
88
|
hash.reject { |key, value| key.empty? && value.to_s.empty? }
|
85
89
|
end
|
86
90
|
|
91
|
+
def self.safely_decode_character_references!(string)
|
92
|
+
string.gsub!(/&#(x)?([\h]+);/) do
|
93
|
+
if $2
|
94
|
+
base = $1 == "x" ? 16 : 10
|
95
|
+
int = Integer($2, base)
|
96
|
+
begin
|
97
|
+
int.chr(Encoding::UTF_8)
|
98
|
+
rescue RangeError
|
99
|
+
""
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
string
|
104
|
+
end
|
105
|
+
|
87
106
|
def self.get_count(xml)
|
88
107
|
doc = Nokogiri.parse(xml.to_s)
|
89
108
|
if node = doc.at("//COUNT")
|
data/test/fixtures.rb
CHANGED
@@ -266,6 +266,14 @@ SAMPLE_COMPACT_WITH_SPECIAL_CHARS_2 = <<EOF
|
|
266
266
|
</RETS>
|
267
267
|
EOF
|
268
268
|
|
269
|
+
SAMPLE_COMPACT_WITH_DOUBLY_ENCODED_BAD_CHARACTER_REFERENCES = <<EOF
|
270
|
+
<RETS ReplyCode=\"0\" ReplyText=\"Operation Success.\">
|
271
|
+
<DELIMITER value=\"09\" />
|
272
|
+
<COLUMNS> PublicRemarksNew WindowCoverings YearBuilt Zoning ZoningCompatibleYN </COLUMNS>
|
273
|
+
<DATA> foo &#56324; bar 1999 00 </DATA>
|
274
|
+
</RETS>
|
275
|
+
EOF
|
276
|
+
|
269
277
|
SAMPLE_PROPERTY_WITH_LOTS_OF_COLUMNS = <<EOF
|
270
278
|
<RETS ReplyCode=\"0\" ReplyText=\"Operation Success.\">
|
271
279
|
<DELIMITER value=\"09\" />
|
@@ -309,7 +317,8 @@ Resource: Properties (Key Field: matrix_unique_key)
|
|
309
317
|
Types:
|
310
318
|
Quarterly -> Q
|
311
319
|
Annually -> A
|
312
|
-
Object:
|
313
|
-
|
320
|
+
Object: HiRes
|
321
|
+
Visible Name: Photo
|
322
|
+
Mime Type: photo/jpg
|
314
323
|
Description: photo description
|
315
324
|
EOF
|
data/test/test_client.rb
CHANGED
@@ -97,7 +97,7 @@ class TestClient < MiniTest::Test
|
|
97
97
|
logger.debug "foo"
|
98
98
|
end
|
99
99
|
|
100
|
-
def
|
100
|
+
def test_find_every_raises_on_missing_required_arguments
|
101
101
|
assert_raises ArgumentError do
|
102
102
|
@client.find_every({})
|
103
103
|
end
|
@@ -129,38 +129,27 @@ class TestClient < MiniTest::Test
|
|
129
129
|
|
130
130
|
def test_response_text_encoding_from_ascii
|
131
131
|
@client.stubs(:capability_url).with("Search").returns("search_url")
|
132
|
-
|
133
132
|
response = mock
|
134
|
-
response.stubs(:body).returns(
|
135
|
-
@client.stubs(:http_post).with("search_url", anything).returns(response)
|
133
|
+
response.stubs(:body).returns("An ascii string".encode("binary", "UTF-8"))
|
136
134
|
|
137
|
-
|
138
|
-
|
139
|
-
@client.find_every(:search_type => "Foo", :class => "Bar")
|
135
|
+
assert_equal @client.clean_response(response).body, "An ascii string"
|
140
136
|
end
|
141
137
|
|
142
138
|
def test_response_text_encoding_from_utf_8
|
143
139
|
@client.stubs(:capability_url).with("Search").returns("search_url")
|
144
|
-
|
145
140
|
response = mock
|
146
141
|
response.stubs(:body).returns("Some string with non-ascii characters \u0119")
|
147
|
-
@client.stubs(:http_post).with("search_url", anything).returns(response)
|
148
142
|
|
149
|
-
|
143
|
+
assert_equal @client.clean_response(response).body, "Some string with non-ascii characters \u0119"
|
150
144
|
|
151
|
-
@client.find_every(:search_type => "Foo", :class => "Bar")
|
152
145
|
end
|
153
146
|
|
154
147
|
def test_response_text_encoding_from_utf_16
|
155
148
|
@client.stubs(:capability_url).with("Search").returns("search_url")
|
156
|
-
|
157
149
|
response = mock
|
158
150
|
response.stubs(:body).returns("Some string with non-utf-8 characters \xC2")
|
159
|
-
@client.stubs(:http_post).with("search_url", anything).returns(response)
|
160
|
-
|
161
|
-
Rets::Parser::Compact.expects(:parse_document).with("Some string with non-utf-8 characters \uFFFD")
|
162
151
|
|
163
|
-
@client.
|
152
|
+
assert_equal @client.clean_response(response).body, "Some string with non-utf-8 characters \uFFFD"
|
164
153
|
end
|
165
154
|
|
166
155
|
def test_find_retries_when_receiving_no_records_found
|
@@ -187,6 +176,13 @@ class TestClient < MiniTest::Test
|
|
187
176
|
@client.find(:all, :foo => :bar)
|
188
177
|
end
|
189
178
|
|
179
|
+
def test_find_waits_configured_time_before_next_request
|
180
|
+
@client.options[:recoverable_error_wait_secs] = 3.14
|
181
|
+
@client.expects(:sleep).with(3.14).times(3)
|
182
|
+
@client.stubs(:find_every).raises(Rets::MiscellaneousSearchError.new(0, 'Foo'))
|
183
|
+
@client.find(:all, :foo => :bar) rescue nil
|
184
|
+
end
|
185
|
+
|
190
186
|
def test_find_eventually_reraises_errors
|
191
187
|
@client.stubs(:find_every).raises(Rets::AuthorizationFailure.new(401, 'Not Authorized'))
|
192
188
|
@client.stubs(:login)
|
data/test/test_metadata.rb
CHANGED
@@ -58,7 +58,7 @@ class TestMetadata < MiniTest::Test
|
|
58
58
|
]
|
59
59
|
|
60
60
|
rets_objects = [
|
61
|
-
Rets::Metadata::RetsObject.new("Photo", "photo/jpg", "photo description")
|
61
|
+
Rets::Metadata::RetsObject.new("HiRes", "Photo", "photo/jpg", "photo description")
|
62
62
|
]
|
63
63
|
|
64
64
|
resource = Rets::Metadata::Resource.new(resource_id, "matrix_unique_key", rets_classes, rets_objects)
|
@@ -9,4 +9,13 @@ class TestMetadataLookupType < MiniTest::Test
|
|
9
9
|
assert_equal('a', lookup_type.value)
|
10
10
|
assert_equal('c', lookup_type.long_value)
|
11
11
|
end
|
12
|
+
|
13
|
+
def test_lookup_type_ignores_trailing_whitespace
|
14
|
+
fragment = { "Value" => 'a ', "LongValue" => 'c ' }
|
15
|
+
|
16
|
+
lookup_type = Rets::Metadata::LookupType.new(fragment)
|
17
|
+
|
18
|
+
assert_equal('a', lookup_type.value)
|
19
|
+
assert_equal('c', lookup_type.long_value)
|
20
|
+
end
|
12
21
|
end
|
@@ -5,16 +5,29 @@ class TestMetadataObject < MiniTest::Test
|
|
5
5
|
name = "Name"
|
6
6
|
mime_type = "mimetype"
|
7
7
|
description = "description"
|
8
|
+
object_type = "type"
|
8
9
|
|
9
10
|
object_fragment = {
|
10
|
-
|
11
|
-
|
12
|
-
|
11
|
+
"ObjectType" => object_type,
|
12
|
+
"VisibleName" => name,
|
13
|
+
"MIMEType" => mime_type,
|
14
|
+
"Description" => description,
|
13
15
|
}
|
14
16
|
|
15
17
|
assert_equal(
|
16
18
|
Rets::Metadata::RetsObject.build(object_fragment),
|
17
|
-
Rets::Metadata::RetsObject.new(name, mime_type, description)
|
19
|
+
Rets::Metadata::RetsObject.new(object_type, name, mime_type, description)
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_rets_object_building_not_case_dependent
|
24
|
+
object_fragment = {
|
25
|
+
"MiMeTyPe" => "image/jpeg"
|
26
|
+
}
|
27
|
+
|
28
|
+
assert_equal(
|
29
|
+
Rets::Metadata::RetsObject.build(object_fragment).mime_type,
|
30
|
+
"image/jpeg"
|
18
31
|
)
|
19
32
|
end
|
20
33
|
end
|
@@ -53,12 +53,20 @@ class TestMetadataResource < MiniTest::Test
|
|
53
53
|
assert_equal([rets_object], objects)
|
54
54
|
end
|
55
55
|
|
56
|
+
def test_resource_build_objects_when_objects_were_not_loaded
|
57
|
+
resource_id = "id"
|
58
|
+
metadata = {} # doesn't contain metadata for :object key
|
59
|
+
|
60
|
+
objects = Rets::Metadata::Resource.build_objects(resource_id, metadata)
|
61
|
+
assert_equal [], objects
|
62
|
+
end
|
63
|
+
|
56
64
|
def test_resource_build
|
57
65
|
fragment = { "ResourceID" => "test" }
|
58
66
|
|
59
67
|
lookup_types = stub(:lookup_types)
|
60
68
|
classes = stub(:classes)
|
61
|
-
objects = stub(:
|
69
|
+
objects = stub(:objects)
|
62
70
|
metadata = stub(:metadata)
|
63
71
|
|
64
72
|
Rets::Metadata::Resource.stubs(:build_lookup_tree => lookup_types)
|
data/test/test_parser_compact.rb
CHANGED
@@ -86,9 +86,30 @@ class TestParserCompact < MiniTest::Test
|
|
86
86
|
assert_equal "text with <tag>", rows.last["PublicRemarksNew"]
|
87
87
|
end
|
88
88
|
|
89
|
+
def test_parse_doubly_encoded_bad_character_references
|
90
|
+
rows = Rets::Parser::Compact.parse_document(SAMPLE_COMPACT_WITH_DOUBLY_ENCODED_BAD_CHARACTER_REFERENCES)
|
91
|
+
assert_equal "foo bar", rows.last["PublicRemarksNew"]
|
92
|
+
end
|
93
|
+
|
89
94
|
def test_parse_property_with_lots_of_columns
|
90
95
|
row = Rets::Parser::Compact.parse_document(SAMPLE_PROPERTY_WITH_LOTS_OF_COLUMNS).first
|
91
96
|
assert_equal 800, row.keys.size
|
92
97
|
assert_equal 800.times.map { |x| "K%03d" % x }, row.keys
|
93
98
|
end
|
99
|
+
|
100
|
+
def test_safely_decode_character_references!
|
101
|
+
assert_decoded "a", "a"
|
102
|
+
assert_decoded "a", "a"
|
103
|
+
assert_decoded "a", "a"
|
104
|
+
assert_decoded "a", "a"
|
105
|
+
assert_decoded "", "�"
|
106
|
+
assert_decoded "", "�"
|
107
|
+
assert_decoded "", "�"
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
def assert_decoded(a, b)
|
112
|
+
recoded = Rets::Parser::Compact.safely_decode_character_references!(b)
|
113
|
+
assert_equal a, recoded
|
114
|
+
end
|
94
115
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Estately, Inc. Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httpclient
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 1.1.0
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 1.1.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: vcr
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,18 +114,20 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '3.
|
117
|
+
version: '3.15'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '3.
|
124
|
+
version: '3.15'
|
125
125
|
description: |-
|
126
126
|
[](http://travis-ci.org/estately/rets)
|
127
127
|
A pure-ruby library for fetching data from [RETS] servers.
|
128
128
|
|
129
|
+
If you're looking for a slick CLI interface check out [retscli](https://github.com/summera/retscli), which is an awesome tool for exploring metadata or learning about RETS.
|
130
|
+
|
129
131
|
[RETS]: http://www.rets.org
|
130
132
|
email:
|
131
133
|
- opensource@estately.com
|
@@ -137,7 +139,6 @@ extra_rdoc_files:
|
|
137
139
|
- Manifest.txt
|
138
140
|
- README.md
|
139
141
|
files:
|
140
|
-
- ".gemtest"
|
141
142
|
- CHANGELOG.md
|
142
143
|
- Manifest.txt
|
143
144
|
- README.md
|
@@ -218,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
218
219
|
version: '0'
|
219
220
|
requirements: []
|
220
221
|
rubyforge_project:
|
221
|
-
rubygems_version: 2.
|
222
|
+
rubygems_version: 2.5.1
|
222
223
|
signing_key:
|
223
224
|
specification_version: 4
|
224
225
|
summary: "[](http://travis-ci.org/estately/rets)
|
data/.gemtest
DELETED
File without changes
|