rets 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://secure.travis-ci.org/estately/rets.png?branch=master)](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
|
[![Build Status](https://secure.travis-ci.org/estately/rets.png?branch=master)](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: "[![Build Status](https://secure.travis-ci.org/estately/rets.png?branch=master)](http://travis-ci.org/estately/rets)
|
data/.gemtest
DELETED
File without changes
|