rets 0.5.1 → 0.6.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 +21 -0
- data/Manifest.txt +1 -0
- data/README.md +4 -0
- data/lib/rets.rb +2 -2
- data/lib/rets/client.rb +36 -5
- data/lib/rets/http_client.rb +29 -13
- data/lib/rets/metadata/lookup_type.rb +1 -1
- data/lib/rets/metadata/resource.rb +11 -2
- data/lib/rets/metadata/rets_class.rb +6 -0
- data/lib/rets/metadata/root.rb +15 -22
- data/lib/rets/metadata/table.rb +8 -0
- data/lib/rets/parser/compact.rb +7 -1
- data/test/fixtures.rb +7 -0
- data/test/test_client.rb +34 -9
- data/test/test_metadata.rb +31 -29
- data/test/test_parser_compact.rb +5 -0
- data/test/vcr_cassettes/unauthorized_response.yml +262 -0
- metadata +30 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2ed1e7653c004159b6f9042dc917f9c8a30be926
|
4
|
+
data.tar.gz: 4b2275a656b7240b5d94a4d0f9c0ad5a62a18eeb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3853c2929ab43105aba05ceebe185e4c3b86271bdc99b8ec42e9578921ee9d2740c98cfea2b31a13e22de6e3d5e4fa608f61399133593c43dae14f3c63ffc772
|
7
|
+
data.tar.gz: 7d177e112e61b54dc347811bdfa6a44aa2dd472ce45b061dbf180e636b4e5f2c4318e6d8ea08dc5c1013615118eefc4da401fa4b7bd3da629317d7022221f30b
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
### 0.6.0 / 2013-10-30
|
2
|
+
|
3
|
+
* fix: fix spelling error that created misleading exceptions
|
4
|
+
* feature: track stats for http requests sent
|
5
|
+
* feature: raise an exception if the login action doesn't return an http 200 status
|
6
|
+
* feature: add better class description and more fields to print tree
|
7
|
+
* feature: support http proxies
|
8
|
+
* feature: customizable http timeouts
|
9
|
+
* feature: add logging http headers when in debug mode
|
10
|
+
* feature: strip invalid utf8 from responses before parsing
|
11
|
+
* fix: don't raise an exception on a 401 after logout
|
12
|
+
* fix: treat no matching records status without a count node as a zero count
|
13
|
+
* feature: add an option for loading custom ca_certs
|
14
|
+
* feature: remove invalid resource types from metadata
|
15
|
+
* feature: special case http 412
|
16
|
+
* feature: add max_retries option
|
17
|
+
|
18
|
+
### 0.5.1 / 2013-10-30
|
19
|
+
|
20
|
+
* fix: 0.5.0 was broken, fix gem Manifest to fix gem
|
21
|
+
|
1
22
|
### 0.5.0 / 2013-09-05
|
2
23
|
|
3
24
|
* feature: Allow client.count to get integer count
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -17,6 +17,10 @@ A pure-ruby library for fetching data from [RETS] servers.
|
|
17
17
|
[net-http-persistent]: http://seattlerb.rubyforge.org/net-http-persistent/
|
18
18
|
[nokogiri]: http://nokogiri.org
|
19
19
|
|
20
|
+
## EXAMPLE USAGE:
|
21
|
+
|
22
|
+
We need work in this area! There are currently a few guideline examples in the `example` folder on connecting, fetching a property's data, and fetching a property's photos.
|
23
|
+
|
20
24
|
## LICENSE:
|
21
25
|
|
22
26
|
(The MIT License)
|
data/lib/rets.rb
CHANGED
@@ -3,7 +3,7 @@ require 'digest/md5'
|
|
3
3
|
require 'nokogiri'
|
4
4
|
|
5
5
|
module Rets
|
6
|
-
VERSION = '0.
|
6
|
+
VERSION = '0.6.0'
|
7
7
|
|
8
8
|
MalformedResponse = Class.new(ArgumentError)
|
9
9
|
UnknownResponse = Class.new(ArgumentError)
|
@@ -31,7 +31,7 @@ module Rets
|
|
31
31
|
attr_reader :capability_name
|
32
32
|
def initialize(capability_name)
|
33
33
|
@capability_name = capability_name
|
34
|
-
super("unknown
|
34
|
+
super("unknown capability #{capability_name}")
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
data/lib/rets/client.rb
CHANGED
@@ -31,7 +31,20 @@ module Rets
|
|
31
31
|
self.logger = @options[:logger] || FakeLogger.new
|
32
32
|
@client_progress = ClientProgressReporter.new(self.logger, options[:stats_collector], options[:stats_prefix])
|
33
33
|
@cached_metadata = @options[:metadata]
|
34
|
-
@
|
34
|
+
if @options[:http_proxy]
|
35
|
+
@http = HTTPClient.new(options.fetch(:http_proxy))
|
36
|
+
|
37
|
+
if @options[:proxy_username]
|
38
|
+
@http.set_proxy_auth(options.fetch(:proxy_username), options.fetch(:proxy_password))
|
39
|
+
end
|
40
|
+
else
|
41
|
+
@http = HTTPClient.new
|
42
|
+
end
|
43
|
+
|
44
|
+
if @options[:receive_timeout]
|
45
|
+
@http.receive_timeout = @options[:receive_timeout]
|
46
|
+
end
|
47
|
+
|
35
48
|
@http.set_cookie_store(options[:cookie_store]) if options[:cookie_store]
|
36
49
|
|
37
50
|
@http_client = Rets::HttpClient.new(@http, @options, @logger, @login_url)
|
@@ -61,6 +74,10 @@ module Rets
|
|
61
74
|
raise NoLogout.new('No logout method found for rets client')
|
62
75
|
end
|
63
76
|
http_get(capability_url("Logout"))
|
77
|
+
rescue UnknownResponse => e
|
78
|
+
unless e.message.match(/expected a 200, but got 401/)
|
79
|
+
raise e
|
80
|
+
end
|
64
81
|
end
|
65
82
|
|
66
83
|
# Finds records.
|
@@ -98,7 +115,7 @@ module Rets
|
|
98
115
|
begin
|
99
116
|
find_every(opts, resolve)
|
100
117
|
rescue AuthorizationFailure, InvalidRequest => e
|
101
|
-
if retries < 3
|
118
|
+
if retries < opts.fetch(:max_retries, 3)
|
102
119
|
retries += 1
|
103
120
|
@client_progress.find_with_retries_failed_a_retry(e, retries)
|
104
121
|
clean_setup
|
@@ -117,7 +134,7 @@ module Rets
|
|
117
134
|
if opts[:count] == COUNT.only
|
118
135
|
Parser::Compact.get_count(res.body)
|
119
136
|
else
|
120
|
-
results = Parser::Compact.parse_document(res.body)
|
137
|
+
results = Parser::Compact.parse_document(res.body.encode("UTF-8", "binary", :invalid => :replace, :undef => :replace))
|
121
138
|
if resolve
|
122
139
|
rets_class = find_rets_class(opts[:search_type], opts[:class])
|
123
140
|
decorate_results(results, rets_class)
|
@@ -239,11 +256,18 @@ module Rets
|
|
239
256
|
self.metadata = @cached_metadata
|
240
257
|
else
|
241
258
|
@client_progress.bad_cached_metadata(@cached_metadata)
|
242
|
-
|
243
|
-
self.metadata = Metadata::Root.new(&metadata_fetcher)
|
259
|
+
self.metadata = Metadata::Root.new(logger, retrieve_metadata)
|
244
260
|
end
|
245
261
|
end
|
246
262
|
|
263
|
+
def retrieve_metadata
|
264
|
+
raw_metadata = {}
|
265
|
+
Metadata::METADATA_TYPES.each {|type|
|
266
|
+
raw_metadata[type] = retrieve_metadata_type(type)
|
267
|
+
}
|
268
|
+
raw_metadata
|
269
|
+
end
|
270
|
+
|
247
271
|
def retrieve_metadata_type(type)
|
248
272
|
res = http_post(capability_url("GetMetadata"),
|
249
273
|
{ "Format" => "COMPACT",
|
@@ -322,6 +346,13 @@ module Rets
|
|
322
346
|
|
323
347
|
class ErrorChecker
|
324
348
|
def self.check(response)
|
349
|
+
# some RETS servers returns HTTP code 412 when session cookie expired, yet the response body
|
350
|
+
# passes XML check. We need to special case for this situation.
|
351
|
+
# This method is also called from multipart.rb where there are headers and body but no status_code
|
352
|
+
if response.respond_to?(:status_code) && response.status_code == 412
|
353
|
+
raise HttpError, "HTTP status: #{response.status_code}, body: #{response.body}"
|
354
|
+
end
|
355
|
+
|
325
356
|
# some RETS servers return success code in XML body but failure code 4xx in http status
|
326
357
|
# If xml body is present we ignore http status
|
327
358
|
|
data/lib/rets/http_client.rb
CHANGED
@@ -7,13 +7,16 @@ module Rets
|
|
7
7
|
@options = options
|
8
8
|
@logger = logger
|
9
9
|
@login_url = login_url
|
10
|
+
@options.fetch(:ca_certs, []).each {|c| @http.ssl_config.add_trust_ca(c) }
|
10
11
|
end
|
11
12
|
|
12
13
|
def http_get(url, params=nil, extra_headers={})
|
13
14
|
http.set_auth(url, options[:username], options[:password])
|
14
15
|
headers = extra_headers.merge(rets_extra_headers)
|
15
|
-
res =
|
16
|
-
log_http_traffic("POST", url, params, headers
|
16
|
+
res = nil
|
17
|
+
log_http_traffic("POST", url, params, headers) do
|
18
|
+
res = http.get(url, params, headers)
|
19
|
+
end
|
17
20
|
Client::ErrorChecker.check(res)
|
18
21
|
res
|
19
22
|
end
|
@@ -21,12 +24,34 @@ module Rets
|
|
21
24
|
def http_post(url, params, extra_headers = {})
|
22
25
|
http.set_auth(url, options[:username], options[:password])
|
23
26
|
headers = extra_headers.merge(rets_extra_headers)
|
24
|
-
res =
|
25
|
-
log_http_traffic("POST", url, params, headers
|
27
|
+
res = nil
|
28
|
+
log_http_traffic("POST", url, params, headers) do
|
29
|
+
res = http.post(url, params, headers)
|
30
|
+
end
|
26
31
|
Client::ErrorChecker.check(res)
|
27
32
|
res
|
28
33
|
end
|
29
34
|
|
35
|
+
def log_http_traffic(method, url, params, headers, &block)
|
36
|
+
# optimization, we don't want to compute log params
|
37
|
+
# if logging is off
|
38
|
+
if logger.debug?
|
39
|
+
logger.debug "Rets::Client >> #{method} #{url}"
|
40
|
+
logger.debug "Rets::Client >> params = #{params.inspect}"
|
41
|
+
logger.debug "Rets::Client >> headers = #{headers.inspect}"
|
42
|
+
end
|
43
|
+
|
44
|
+
res = block.call
|
45
|
+
|
46
|
+
# optimization, we don't want to compute log params
|
47
|
+
# if logging is off, especially when there is a loop just
|
48
|
+
# for logging
|
49
|
+
if logger.debug?
|
50
|
+
logger.debug "Rets::Client << Status #{res.status_code}"
|
51
|
+
res.headers.each { |k, v| logger.debug "Rets::Client << #{k}: #{v}" }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
30
55
|
def save_cookie_store(force=nil)
|
31
56
|
if options[:cookie_store]
|
32
57
|
if force
|
@@ -37,15 +62,6 @@ module Rets
|
|
37
62
|
end
|
38
63
|
end
|
39
64
|
|
40
|
-
def log_http_traffic(method, url, params, headers, res)
|
41
|
-
return unless logger.debug?
|
42
|
-
logger.debug "Rets::Client >> #{method} #{url}"
|
43
|
-
logger.debug "Rets::Client >> params = #{params.inspect}"
|
44
|
-
logger.debug "Rets::Client >> headers = #{headers.inspect}"
|
45
|
-
logger.debug "Rets::Client << Status #{res.status_code}"
|
46
|
-
res.headers.each { |k, v| logger.debug "Rets::Client << #{k}: #{v}" }
|
47
|
-
end
|
48
|
-
|
49
65
|
def rets_extra_headers
|
50
66
|
user_agent = options[:agent] || "Client/1.0"
|
51
67
|
rets_version = options[:version] || "RETS/1.7.2"
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Rets
|
2
2
|
module Metadata
|
3
3
|
class Resource
|
4
|
+
class MissingRetsClass < RuntimeError; end
|
4
5
|
attr_accessor :rets_classes
|
5
6
|
attr_accessor :lookup_types
|
6
7
|
attr_accessor :key_field
|
@@ -24,7 +25,12 @@ module Rets
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def self.find_rets_classes(metadata, resource)
|
27
|
-
metadata[:class].detect { |c| c.resource == resource.id }
|
28
|
+
class_container = metadata[:class].detect { |c| c.resource == resource.id }
|
29
|
+
if class_container.nil?
|
30
|
+
raise MissingRetsClass.new("No Metadata classes for #{resource.id}")
|
31
|
+
else
|
32
|
+
class_container.classes
|
33
|
+
end
|
28
34
|
end
|
29
35
|
|
30
36
|
def self.build_lookup_tree(resource, metadata)
|
@@ -52,12 +58,15 @@ module Rets
|
|
52
58
|
end
|
53
59
|
end
|
54
60
|
|
55
|
-
def self.build(resource_fragment, metadata)
|
61
|
+
def self.build(resource_fragment, metadata, logger)
|
56
62
|
resource = new(resource_fragment)
|
57
63
|
|
58
64
|
resource.lookup_types = build_lookup_tree(resource, metadata)
|
59
65
|
resource.rets_classes = build_classes(resource, metadata)
|
60
66
|
resource
|
67
|
+
rescue MissingRetsClass => e
|
68
|
+
logger.warn(e.message)
|
69
|
+
nil
|
61
70
|
end
|
62
71
|
|
63
72
|
def print_tree
|
@@ -3,12 +3,16 @@ module Rets
|
|
3
3
|
class RetsClass
|
4
4
|
attr_accessor :tables
|
5
5
|
attr_accessor :name
|
6
|
+
attr_accessor :visible_name
|
7
|
+
attr_accessor :description
|
6
8
|
attr_accessor :resource
|
7
9
|
|
8
10
|
def initialize(rets_class_fragment, resource)
|
9
11
|
self.resource = resource
|
10
12
|
self.tables = []
|
11
13
|
self.name = rets_class_fragment["ClassName"]
|
14
|
+
self.visible_name = rets_class_fragment["VisibleName"]
|
15
|
+
self.description = rets_class_fragment["Description"]
|
12
16
|
end
|
13
17
|
|
14
18
|
def self.find_table_container(metadata, resource, rets_class)
|
@@ -31,6 +35,8 @@ module Rets
|
|
31
35
|
|
32
36
|
def print_tree
|
33
37
|
puts " Class: #{name}"
|
38
|
+
puts " Visible Name: #{visible_name}"
|
39
|
+
puts " Description : #{description}"
|
34
40
|
tables.each(&:print_tree)
|
35
41
|
end
|
36
42
|
|
data/lib/rets/metadata/root.rb
CHANGED
@@ -47,33 +47,25 @@ module Rets
|
|
47
47
|
# Sources are the raw xml documents fetched for each metadata type
|
48
48
|
# they are stored as a hash with the type names as their keys
|
49
49
|
# and the raw xml as the values
|
50
|
-
|
50
|
+
attr_reader :sources
|
51
|
+
|
52
|
+
# Metadata can be unmarshalled from cache. @logger is not set during that process, constructor is not called.
|
53
|
+
# Client code must set it after unmarshalling.
|
54
|
+
attr_reader :logger
|
51
55
|
|
52
56
|
# fetcher is a proc that inverts control to the client to retrieve metadata
|
53
57
|
# types
|
54
|
-
def initialize(
|
58
|
+
def initialize(logger, sources)
|
59
|
+
@logger = logger
|
55
60
|
@tree = nil
|
56
61
|
@metadata_types = nil # TODO think up a better name ... containers?
|
57
|
-
@sources =
|
58
|
-
|
59
|
-
# allow Root's to be built with no fetcher. Makes for easy testing
|
60
|
-
return unless block_given?
|
61
|
-
|
62
|
-
fetch_sources(&fetcher)
|
63
|
-
end
|
64
|
-
|
65
|
-
def fetch_sources(&fetcher)
|
66
|
-
self.sources = Hash[*METADATA_TYPES.map {|type| [type, fetcher.call(type)] }.flatten]
|
62
|
+
@sources = sources
|
67
63
|
end
|
68
64
|
|
69
65
|
def marshal_dump
|
70
66
|
sources
|
71
67
|
end
|
72
68
|
|
73
|
-
def marshal_load(sources)
|
74
|
-
self.sources = sources
|
75
|
-
end
|
76
|
-
|
77
69
|
def version
|
78
70
|
metadata_types[:system].first.version
|
79
71
|
end
|
@@ -87,11 +79,11 @@ module Rets
|
|
87
79
|
# version was published, or a version number. These values may or may
|
88
80
|
# not exist on any given rets server.
|
89
81
|
def current?(current_timestamp, current_version)
|
90
|
-
|
91
|
-
|
92
|
-
|
82
|
+
if !current_version.to_s.empty? && !version.to_s.empty?
|
83
|
+
current_version == version
|
84
|
+
else
|
93
85
|
current_timestamp ? current_timestamp == date : true
|
94
|
-
|
86
|
+
end
|
95
87
|
end
|
96
88
|
|
97
89
|
def build_tree
|
@@ -101,8 +93,9 @@ module Rets
|
|
101
93
|
|
102
94
|
resource_containers.each do |resource_container|
|
103
95
|
resource_container.rows.each do |resource_fragment|
|
104
|
-
resource = Resource.build(resource_fragment, metadata_types)
|
105
|
-
|
96
|
+
resource = Resource.build(resource_fragment, metadata_types, @logger)
|
97
|
+
#some mlses list resource types without an associated data, throw those away
|
98
|
+
tree[resource.id.downcase] = resource if resource
|
106
99
|
end
|
107
100
|
end
|
108
101
|
|
data/lib/rets/metadata/table.rb
CHANGED
@@ -33,6 +33,7 @@ module Rets
|
|
33
33
|
puts " StandardName: #{ table_fragment["StandardName"] }"
|
34
34
|
puts " Units: #{ table_fragment["Units"] }"
|
35
35
|
puts " Searchable: #{ table_fragment["Searchable"] }"
|
36
|
+
puts " Required: #{table_fragment['Required']}"
|
36
37
|
end
|
37
38
|
|
38
39
|
def resolve(value)
|
@@ -67,6 +68,13 @@ module Rets
|
|
67
68
|
|
68
69
|
def print_tree
|
69
70
|
puts " LookupTable: #{name}"
|
71
|
+
puts " Required: #{table_fragment['Required']}"
|
72
|
+
puts " Searchable: #{ table_fragment["Searchable"] }"
|
73
|
+
puts " Units: #{ table_fragment["Units"] }"
|
74
|
+
puts " ShortName: #{ table_fragment["ShortName"] }"
|
75
|
+
puts " LongName: #{ table_fragment["LongName"] }"
|
76
|
+
puts " StandardName: #{ table_fragment["StandardName"] }"
|
77
|
+
puts " Types:"
|
70
78
|
|
71
79
|
lookup_types.each(&:print_tree)
|
72
80
|
end
|
data/lib/rets/parser/compact.rb
CHANGED
@@ -49,8 +49,14 @@ module Rets
|
|
49
49
|
|
50
50
|
def self.get_count(xml)
|
51
51
|
doc = Nokogiri.parse(xml.to_s)
|
52
|
-
doc.at("//COUNT")
|
52
|
+
if node = doc.at("//COUNT")
|
53
|
+
return node.attr('Records').to_i
|
54
|
+
elsif node = doc.at("//RETS-STATUS")
|
55
|
+
# Handle <RETS-STATUS ReplyCode="20201" ReplyText="No matching records were found" />
|
56
|
+
return 0 if node.attr('ReplyCode') == '20201'
|
57
|
+
end
|
53
58
|
end
|
59
|
+
|
54
60
|
end
|
55
61
|
end
|
56
62
|
end
|
data/test/fixtures.rb
CHANGED
@@ -27,6 +27,13 @@ COUNT_ONLY = <<XML
|
|
27
27
|
</RETS>
|
28
28
|
XML
|
29
29
|
|
30
|
+
RETS_STATUS_NO_MATCHING_RECORDS = <<XML
|
31
|
+
<?xml version="1.0"?>
|
32
|
+
<RETS ReplyCode="0" ReplyText="Operation Successful">
|
33
|
+
<RETS-STATUS ReplyCode="20201" ReplyText="No matching records were found" />
|
34
|
+
</RETS>
|
35
|
+
XML
|
36
|
+
|
30
37
|
CAPABILITIES_WITH_WHITESPACE = <<XML
|
31
38
|
<RETS ReplyCode="0" ReplyText="Operation Successful">
|
32
39
|
<RETS-RESPONSE>
|
data/test/test_client.rb
CHANGED
@@ -37,21 +37,23 @@ class TestClient < MiniTest::Test
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def test_metadata_when_not_initialized_with_metadata
|
40
|
+
new_raw_metadata = stub(:new_raw_metadata)
|
41
|
+
|
40
42
|
client = Rets::Client.new(:login_url => "http://example.com")
|
41
|
-
|
42
|
-
|
43
|
+
client.stubs(:retrieve_metadata).returns(new_raw_metadata)
|
44
|
+
|
45
|
+
assert_same new_raw_metadata, client.metadata.marshal_dump
|
43
46
|
end
|
44
47
|
|
45
|
-
def
|
48
|
+
def test_initialize_with_old_metadata_cached_contstructs_new_metadata_from_request
|
46
49
|
metadata = stub(:current? => false)
|
47
|
-
|
50
|
+
new_raw_metadata = stub(:new_raw_metadata)
|
51
|
+
|
48
52
|
client = Rets::Client.new(:login_url => "http://example.com", :metadata => metadata)
|
49
|
-
client.stubs(:capabilities
|
50
|
-
|
53
|
+
client.stubs(:capabilities).returns({})
|
54
|
+
client.stubs(:retrieve_metadata).returns(new_raw_metadata)
|
51
55
|
|
52
|
-
assert_same
|
53
|
-
# This second call ensures the expectations on Root are met
|
54
|
-
client.metadata
|
56
|
+
assert_same new_raw_metadata, client.metadata.marshal_dump
|
55
57
|
end
|
56
58
|
|
57
59
|
def test_initialize_with_current_metadata_cached_return_cached_metadata
|
@@ -210,4 +212,27 @@ class TestClient < MiniTest::Test
|
|
210
212
|
assert_equal response, result
|
211
213
|
end
|
212
214
|
|
215
|
+
def test_clean_setup_with_receive_timeout
|
216
|
+
HTTPClient.any_instance.expects(:receive_timeout=).with(1234)
|
217
|
+
@client = Rets::Client.new(
|
218
|
+
login_url: 'http://example.com/login',
|
219
|
+
receive_timeout: 1234
|
220
|
+
)
|
221
|
+
end
|
222
|
+
|
223
|
+
def test_clean_setup_with_proxy_auth
|
224
|
+
@login_url = 'http://example.com/login'
|
225
|
+
@proxy_url = 'http://example.com/proxy'
|
226
|
+
@proxy_username = 'username'
|
227
|
+
@proxy_password = 'password'
|
228
|
+
HTTPClient.any_instance.expects(:set_proxy_auth).with(@proxy_username, @proxy_password)
|
229
|
+
|
230
|
+
@client = Rets::Client.new(
|
231
|
+
login_url: @login_url,
|
232
|
+
http_proxy: @proxy_url,
|
233
|
+
proxy_username: @proxy_username,
|
234
|
+
proxy_password: @proxy_password
|
235
|
+
)
|
236
|
+
end
|
237
|
+
|
213
238
|
end
|
data/test/test_metadata.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require_relative "helper"
|
2
|
+
require 'logger'
|
2
3
|
|
3
4
|
class TestMetadata < MiniTest::Test
|
4
5
|
def setup
|
5
|
-
@root = Rets::Metadata::Root.new
|
6
|
+
@root = Rets::Metadata::Root.new(Logger.new(STDOUT), {})
|
6
7
|
$VERBOSE = true
|
7
8
|
end
|
8
9
|
|
@@ -10,23 +11,6 @@ class TestMetadata < MiniTest::Test
|
|
10
11
|
$VERBOSE = false
|
11
12
|
end
|
12
13
|
|
13
|
-
def test_metadata_root_fetch_sources_returns_hash_of_metadata_types
|
14
|
-
types = []
|
15
|
-
fake_fetcher = lambda do |type|
|
16
|
-
types << type
|
17
|
-
end
|
18
|
-
|
19
|
-
@root.fetch_sources(&fake_fetcher)
|
20
|
-
|
21
|
-
assert_equal(Rets::Metadata::METADATA_TYPES, types)
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_metadata_root_intialized_with_block
|
25
|
-
external = false
|
26
|
-
Rets::Metadata::Root.new { |source| external = true }
|
27
|
-
assert external
|
28
|
-
end
|
29
|
-
|
30
14
|
def test_metadata_root_build_tree
|
31
15
|
resource = stub(:id => "X")
|
32
16
|
Rets::Metadata::Resource.stubs(:build => resource)
|
@@ -111,10 +95,11 @@ class TestMetadata < MiniTest::Test
|
|
111
95
|
|
112
96
|
def test_metadata_root_metadata_types_constructs_a_hash_of_metadata_types_from_sources
|
113
97
|
test_sources = { "X" => "Y", "Z" => "W" }
|
114
|
-
|
115
|
-
|
98
|
+
root = Rets::Metadata::Root.new(stub(:logger), test_sources)
|
99
|
+
root.stubs(:build_containers => "Y--")
|
116
100
|
Nokogiri.stubs(:parse => "Y-")
|
117
|
-
|
101
|
+
|
102
|
+
assert_equal({:x => "Y--", :z => "Y--"}, root.metadata_types)
|
118
103
|
end
|
119
104
|
|
120
105
|
def test_metadata_root_build_containers_selects_correct_tags
|
@@ -211,12 +196,30 @@ class TestMetadata < MiniTest::Test
|
|
211
196
|
Rets::Metadata::Resource.stubs(:build_lookup_tree => lookup_types)
|
212
197
|
Rets::Metadata::Resource.stubs(:build_classes => classes)
|
213
198
|
|
214
|
-
resource = Rets::Metadata::Resource.build(fragment, metadata)
|
199
|
+
resource = Rets::Metadata::Resource.build(fragment, metadata, Logger.new(STDOUT))
|
215
200
|
|
216
201
|
assert_equal(lookup_types, resource.lookup_types)
|
217
202
|
assert_equal(classes, resource.rets_classes)
|
218
203
|
end
|
219
204
|
|
205
|
+
def test_resource_build_with_incomplete_classes
|
206
|
+
fragment = { "ResourceID" => "test" }
|
207
|
+
|
208
|
+
lookup_types = stub(:lookup_types)
|
209
|
+
metadata = stub(:metadata)
|
210
|
+
|
211
|
+
Rets::Metadata::Resource.stubs(:build_lookup_tree => lookup_types)
|
212
|
+
Rets::Metadata::Resource.stubs(:build_classes).raises(Rets::Metadata::Resource::MissingRetsClass)
|
213
|
+
|
214
|
+
error_log = StringIO.new
|
215
|
+
resource = Rets::Metadata::Resource.build(fragment, metadata, Logger.new(error_log))
|
216
|
+
|
217
|
+
error_log.rewind
|
218
|
+
error_msg = error_log.read
|
219
|
+
assert error_msg.include?('MissingRetsClass')
|
220
|
+
assert_equal(nil, resource)
|
221
|
+
end
|
222
|
+
|
220
223
|
def test_resource_find_lookup_containers
|
221
224
|
resource = stub(:id => "id")
|
222
225
|
metadata = { :lookup => [stub(:resource => "id"), stub(:resource => "id"), stub(:resource => "a")] }
|
@@ -440,18 +443,17 @@ class TestMetadata < MiniTest::Test
|
|
440
443
|
|
441
444
|
def test_root_can_be_serialized
|
442
445
|
sources = { :x => "a" }
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
assert_equal sources, @root.marshal_dump
|
446
|
+
root = Rets::Metadata::Root.new(stub(:logger), sources)
|
447
|
+
assert_equal sources, root.marshal_dump
|
447
448
|
end
|
448
449
|
|
449
450
|
def test_root_can_be_unserialized
|
451
|
+
logger = stub(:logger)
|
450
452
|
sources = { :x => "a" }
|
451
453
|
|
452
|
-
|
454
|
+
root_to_serialize = Rets::Metadata::Root.new(logger, sources)
|
455
|
+
new_root = Rets::Metadata::Root.new(logger, root_to_serialize.marshal_dump)
|
453
456
|
|
454
|
-
assert_equal
|
457
|
+
assert_equal root_to_serialize.marshal_dump, new_root.marshal_dump
|
455
458
|
end
|
456
|
-
|
457
459
|
end
|
data/test/test_parser_compact.rb
CHANGED
@@ -66,6 +66,11 @@ class TestParserCompact < MiniTest::Test
|
|
66
66
|
assert_equal 1234, count
|
67
67
|
end
|
68
68
|
|
69
|
+
def test_get_count_with_no_matching_records
|
70
|
+
count = Rets::Parser::Compact.get_count(RETS_STATUS_NO_MATCHING_RECORDS)
|
71
|
+
assert_equal 0, count
|
72
|
+
end
|
73
|
+
|
69
74
|
def test_parse_example
|
70
75
|
rows = Rets::Parser::Compact.parse_document(Nokogiri.parse(SAMPLE_COMPACT))
|
71
76
|
|
@@ -0,0 +1,262 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: http://rets.example.com/GetObject.asmx/GetObject
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: Resource=Property&Type=Photo&ID=2661580%3A1&Location=0
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Estately/1.0
|
12
|
+
Host:
|
13
|
+
- rets.example.com:80
|
14
|
+
Rets-Version:
|
15
|
+
- RETS/1.7.2
|
16
|
+
Authorization:
|
17
|
+
- Digest username="login", realm="fake_realm", qop="auth",
|
18
|
+
uri="/Login.asmx/Login", nonce="a8f4bc805062602c8ba7a87b2109f808", nc=00000001,
|
19
|
+
cnonce="6e2dd038eea6cbacf0b956fd11f914b6", response="fake_digest",
|
20
|
+
opaque="96eda461-41d0-4624-b7fe-b59e966035f3"
|
21
|
+
Cookie:
|
22
|
+
- ASP.NET_SessionId=mapij045k3bphj3gqqgpmmrx
|
23
|
+
Accept:
|
24
|
+
- image/jpeg, image/png;q=0.5, image/gif;q=0.1
|
25
|
+
Content-Type:
|
26
|
+
- application/x-www-form-urlencoded
|
27
|
+
Content-Length:
|
28
|
+
- '54'
|
29
|
+
response:
|
30
|
+
status:
|
31
|
+
code: 401
|
32
|
+
message: Unauthorized
|
33
|
+
headers:
|
34
|
+
Connection:
|
35
|
+
- close
|
36
|
+
Date:
|
37
|
+
- Thu, 26 Jul 2012 00:11:02 GMT
|
38
|
+
Server:
|
39
|
+
- Microsoft-IIS/6.0
|
40
|
+
Cache-Control:
|
41
|
+
- private
|
42
|
+
- private
|
43
|
+
X-Aspnet-Version:
|
44
|
+
- 2.0.50727
|
45
|
+
Www-Authenticate:
|
46
|
+
- Digest realm="fake_realm",nonce="dfd74deac03147e1a4fff598679f1a80",opaque="8e5029c6-bb9c-4562-b440-e7c2b8960743",qop="auth"
|
47
|
+
Content-Type:
|
48
|
+
- text/plain
|
49
|
+
body:
|
50
|
+
encoding: US-ASCII
|
51
|
+
string: ! "Unauthorized Request. Digest Session Invalid Reauthenticate\r\nReference:
|
52
|
+
e0ff89c0-2d64-4287-a6b7-6be27c7d3ade"
|
53
|
+
http_version:
|
54
|
+
recorded_at: Thu, 26 Jul 2012 00:11:13 GMT
|
55
|
+
- request:
|
56
|
+
method: post
|
57
|
+
uri: http://rets.example.com/Login.asmx/Login
|
58
|
+
body:
|
59
|
+
encoding: US-ASCII
|
60
|
+
string: ''
|
61
|
+
headers:
|
62
|
+
User-Agent:
|
63
|
+
- Estately/1.0
|
64
|
+
Host:
|
65
|
+
- rets.example.com:80
|
66
|
+
Rets-Version:
|
67
|
+
- RETS/1.7.2
|
68
|
+
Accept:
|
69
|
+
- ! '*/*'
|
70
|
+
response:
|
71
|
+
status:
|
72
|
+
code: 401
|
73
|
+
message: Unauthorized
|
74
|
+
headers:
|
75
|
+
Connection:
|
76
|
+
- close
|
77
|
+
Date:
|
78
|
+
- Thu, 26 Jul 2012 00:11:03 GMT
|
79
|
+
Server:
|
80
|
+
- Microsoft-IIS/6.0
|
81
|
+
Cache-Control:
|
82
|
+
- private
|
83
|
+
- private
|
84
|
+
X-Aspnet-Version:
|
85
|
+
- 2.0.50727
|
86
|
+
Www-Authenticate:
|
87
|
+
- Digest realm="fake_realm",nonce="17fdfe2c6125296bd93bb81bd88dc4be",opaque="aee1e7da-60eb-41b3-b704-8eb3969645fd",qop="auth"
|
88
|
+
Content-Type:
|
89
|
+
- text/plain
|
90
|
+
body:
|
91
|
+
encoding: US-ASCII
|
92
|
+
string: ! "Unauthorized Request. Authentication needed\r\nReference: c2c5ab9f-8c12-42bb-94ce-5b6ebf156da4"
|
93
|
+
http_version:
|
94
|
+
recorded_at: Thu, 26 Jul 2012 00:11:13 GMT
|
95
|
+
- request:
|
96
|
+
method: post
|
97
|
+
uri: http://rets.example.com/Login.asmx/Login
|
98
|
+
body:
|
99
|
+
encoding: US-ASCII
|
100
|
+
string: ''
|
101
|
+
headers:
|
102
|
+
User-Agent:
|
103
|
+
- Estately/1.0
|
104
|
+
Host:
|
105
|
+
- rets.example.com:80
|
106
|
+
Rets-Version:
|
107
|
+
- RETS/1.7.2
|
108
|
+
Authorization:
|
109
|
+
- Digest username="login", realm="fake_realm", qop="auth",
|
110
|
+
uri="/Login.asmx/Login", nonce="17fdfe2c6125296bd93bb81bd88dc4be", nc=00000001,
|
111
|
+
cnonce="ad805b9e2d6f08ad82175085e5febd8d", response="7402076f53663602f052e1aa343d3dab",
|
112
|
+
opaque="aee1e7da-60eb-41b3-b704-8eb3969645fd"
|
113
|
+
Accept:
|
114
|
+
- ! '*/*'
|
115
|
+
response:
|
116
|
+
status:
|
117
|
+
code: 200
|
118
|
+
message: OK
|
119
|
+
headers:
|
120
|
+
Date:
|
121
|
+
- Thu, 26 Jul 2012 00:11:03 GMT
|
122
|
+
Server:
|
123
|
+
- Microsoft-IIS/6.0
|
124
|
+
Cache-Control:
|
125
|
+
- private
|
126
|
+
- private, max-age=0
|
127
|
+
X-Aspnet-Version:
|
128
|
+
- 2.0.50727
|
129
|
+
Rets-Version:
|
130
|
+
- RETS/1.7.2
|
131
|
+
Rets-Server:
|
132
|
+
- Interealty-RETS/1.5.247.0
|
133
|
+
Transfer-Encoding:
|
134
|
+
- chunked
|
135
|
+
Set-Cookie:
|
136
|
+
- ASP.NET_SessionId=vuesmq55l0cpjy45zyn0e555; path=/; HttpOnly
|
137
|
+
- RETS-Session-ID=vuesmq55l0cpjy45zyn0e555; path=/
|
138
|
+
Content-Type:
|
139
|
+
- text/xml
|
140
|
+
body:
|
141
|
+
encoding: US-ASCII
|
142
|
+
string: ! "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<RETS ReplyCode=\"0\"
|
143
|
+
ReplyText=\"Success. Reference ID: a3073726-a011-4391-b22e-900adae5184b\">\r\n
|
144
|
+
\ <RETS-RESPONSE>\r\nMemberName=John Wolff\r\nUser=23756,60,RH,login\r\nBroker=BRDA,BRDA01\r\nMetadataVersion=31.76.69587\r\nMetadataTimeStamp=2012-07-24T17:33:07Z\r\nMinMetadataTimeStamp=2012-07-24T17:33:07Z\r\nTimeoutSeconds=7200\r\nChangePassword=/ChangePassword.asmx/ChangePassword\r\nGetObject=/GetObject.asmx/GetObject\r\nLogin=/Login.asmx/Login\r\nLogout=/Logout.asmx/Logout\r\nSearch=/Search.asmx/Search\r\nGetMetadata=/GetMetadata.asmx/GetMetadata\r\n</RETS-RESPONSE>\r\n</RETS>"
|
145
|
+
http_version:
|
146
|
+
recorded_at: Thu, 26 Jul 2012 00:11:14 GMT
|
147
|
+
- request:
|
148
|
+
method: post
|
149
|
+
uri: http://rets.example.com/Login.asmx/Login
|
150
|
+
body:
|
151
|
+
encoding: US-ASCII
|
152
|
+
string: ''
|
153
|
+
headers:
|
154
|
+
User-Agent:
|
155
|
+
- Estately/1.0
|
156
|
+
Host:
|
157
|
+
- rets.example.com:80
|
158
|
+
Rets-Version:
|
159
|
+
- RETS/1.7.2
|
160
|
+
Authorization:
|
161
|
+
- Digest username="login", realm="fake_realm", qop="auth",
|
162
|
+
uri="/Login.asmx/Login", nonce="17fdfe2c6125296bd93bb81bd88dc4be", nc=00000001,
|
163
|
+
cnonce="ad805b9e2d6f08ad82175085e5febd8d", response="7402076f53663602f052e1aa343d3dab",
|
164
|
+
opaque="aee1e7da-60eb-41b3-b704-8eb3969645fd"
|
165
|
+
Cookie:
|
166
|
+
- ASP.NET_SessionId=vuesmq55l0cpjy45zyn0e555; RETS-Session-ID=vuesmq55l0cpjy45zyn0e555
|
167
|
+
Accept:
|
168
|
+
- ! '*/*'
|
169
|
+
response:
|
170
|
+
status:
|
171
|
+
code: 200
|
172
|
+
message: OK
|
173
|
+
headers:
|
174
|
+
Date:
|
175
|
+
- Thu, 26 Jul 2012 00:11:03 GMT
|
176
|
+
Server:
|
177
|
+
- Microsoft-IIS/6.0
|
178
|
+
Cache-Control:
|
179
|
+
- private
|
180
|
+
- private, max-age=0
|
181
|
+
X-Aspnet-Version:
|
182
|
+
- 2.0.50727
|
183
|
+
Rets-Version:
|
184
|
+
- RETS/1.7.2
|
185
|
+
Rets-Server:
|
186
|
+
- Interealty-RETS/1.5.247.0
|
187
|
+
Transfer-Encoding:
|
188
|
+
- chunked
|
189
|
+
Set-Cookie:
|
190
|
+
- RETS-Session-ID=vuesmq55l0cpjy45zyn0e555; path=/
|
191
|
+
Content-Type:
|
192
|
+
- text/xml
|
193
|
+
body:
|
194
|
+
encoding: US-ASCII
|
195
|
+
string: ! "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<RETS ReplyCode=\"0\"
|
196
|
+
ReplyText=\"Success. Reference ID: a48b4f59-cb22-41be-8e7b-1cee28fb8644\">\r\n
|
197
|
+
\ <RETS-RESPONSE>\r\nMemberName=John Wolff\r\nUser=23756,60,RH,login\r\nBroker=BRDA,BRDA01\r\nMetadataVersion=31.76.69587\r\nMetadataTimeStamp=2012-07-24T17:33:07Z\r\nMinMetadataTimeStamp=2012-07-24T17:33:07Z\r\nTimeoutSeconds=7200\r\nChangePassword=/ChangePassword.asmx/ChangePassword\r\nGetObject=/GetObject.asmx/GetObject\r\nLogin=/Login.asmx/Login\r\nLogout=/Logout.asmx/Logout\r\nSearch=/Search.asmx/Search\r\nGetMetadata=/GetMetadata.asmx/GetMetadata\r\n</RETS-RESPONSE>\r\n</RETS>"
|
198
|
+
http_version:
|
199
|
+
recorded_at: Thu, 26 Jul 2012 00:11:14 GMT
|
200
|
+
- request:
|
201
|
+
method: post
|
202
|
+
uri: http://rets.example.com/GetObject.asmx/GetObject
|
203
|
+
body:
|
204
|
+
encoding: US-ASCII
|
205
|
+
string: Resource=Property&Type=Photo&ID=2661580%3A1&Location=0
|
206
|
+
headers:
|
207
|
+
User-Agent:
|
208
|
+
- Estately/1.0
|
209
|
+
Host:
|
210
|
+
- rets.example.com:80
|
211
|
+
Rets-Version:
|
212
|
+
- RETS/1.7.2
|
213
|
+
Authorization:
|
214
|
+
- Digest username="login", realm="fake_realm", qop="auth",
|
215
|
+
uri="/Login.asmx/Login", nonce="17fdfe2c6125296bd93bb81bd88dc4be", nc=00000001,
|
216
|
+
cnonce="ad805b9e2d6f08ad82175085e5febd8d", response="7402076f53663602f052e1aa343d3dab",
|
217
|
+
opaque="aee1e7da-60eb-41b3-b704-8eb3969645fd"
|
218
|
+
Cookie:
|
219
|
+
- ASP.NET_SessionId=vuesmq55l0cpjy45zyn0e555; RETS-Session-ID=vuesmq55l0cpjy45zyn0e555
|
220
|
+
Accept:
|
221
|
+
- image/jpeg, image/png;q=0.5, image/gif;q=0.1
|
222
|
+
Content-Type:
|
223
|
+
- application/x-www-form-urlencoded
|
224
|
+
Content-Length:
|
225
|
+
- '54'
|
226
|
+
response:
|
227
|
+
status:
|
228
|
+
code: 200
|
229
|
+
message: OK
|
230
|
+
headers:
|
231
|
+
Date:
|
232
|
+
- Thu, 26 Jul 2012 00:11:04 GMT
|
233
|
+
Server:
|
234
|
+
- Microsoft-IIS/6.0
|
235
|
+
Cache-Control:
|
236
|
+
- private
|
237
|
+
- private, max-age=0
|
238
|
+
X-Aspnet-Version:
|
239
|
+
- 2.0.50727
|
240
|
+
Mime-Version:
|
241
|
+
- '1.0'
|
242
|
+
Rets-Version:
|
243
|
+
- RETS/1.7.2
|
244
|
+
Rets-Server:
|
245
|
+
- Interealty-RETS/1.5.247.0
|
246
|
+
Content-Id:
|
247
|
+
- '2661580'
|
248
|
+
Object-Id:
|
249
|
+
- '1'
|
250
|
+
Transfer-Encoding:
|
251
|
+
- chunked
|
252
|
+
Set-Cookie:
|
253
|
+
- RETS-Session-ID=vuesmq55l0cpjy45zyn0e555; path=/
|
254
|
+
Content-Type:
|
255
|
+
- image/jpeg
|
256
|
+
body:
|
257
|
+
encoding: ASCII-8BIT
|
258
|
+
string: !binary |-
|
259
|
+
/9j/4AAQSkZJRgABAQEAYABgAAD///2Q==
|
260
|
+
http_version:
|
261
|
+
recorded_at: Thu, 26 Jul 2012 00:11:14 GMT
|
262
|
+
recorded_with: VCR 2.2.2
|
metadata
CHANGED
@@ -1,119 +1,118 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.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: 2014-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httpclient
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 2.3.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 2.3.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: nokogiri
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 1.5.2
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 1.5.2
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rdoc
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '4.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '4.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: mocha
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - ~>
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 0.11.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - ~>
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.11.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: vcr
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - ~>
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: 2.2.2
|
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
82
|
version: 2.2.2
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: webmock
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - ~>
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: 1.8.0
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - ~>
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: 1.8.0
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: hoe
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - ~>
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '3.
|
103
|
+
version: '3.6'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - ~>
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '3.
|
111
|
-
description:
|
112
|
-
|
110
|
+
version: '3.6'
|
111
|
+
description: |-
|
112
|
+
[![Build Status](https://secure.travis-ci.org/estately/rets.png?branch=master)](http://travis-ci.org/estately/rets)
|
113
113
|
A pure-ruby library for fetching data from [RETS] servers.
|
114
114
|
|
115
|
-
|
116
|
-
[RETS]: http://www.rets.org'
|
115
|
+
[RETS]: http://www.rets.org
|
117
116
|
email:
|
118
117
|
- opensource@estately.com
|
119
118
|
executables:
|
@@ -124,6 +123,7 @@ extra_rdoc_files:
|
|
124
123
|
- Manifest.txt
|
125
124
|
- README.md
|
126
125
|
files:
|
126
|
+
- ".gemtest"
|
127
127
|
- CHANGELOG.md
|
128
128
|
- Manifest.txt
|
129
129
|
- README.md
|
@@ -151,34 +151,33 @@ files:
|
|
151
151
|
- test/test_metadata.rb
|
152
152
|
- test/test_parser_compact.rb
|
153
153
|
- test/test_parser_multipart.rb
|
154
|
-
- .
|
154
|
+
- test/vcr_cassettes/unauthorized_response.yml
|
155
155
|
homepage: http://github.com/estately/rets
|
156
|
-
licenses:
|
157
|
-
- MIT
|
156
|
+
licenses: []
|
158
157
|
metadata: {}
|
159
158
|
post_install_message:
|
160
159
|
rdoc_options:
|
161
|
-
- --main
|
160
|
+
- "--main"
|
162
161
|
- README.md
|
163
162
|
require_paths:
|
164
163
|
- lib
|
165
164
|
required_ruby_version: !ruby/object:Gem::Requirement
|
166
165
|
requirements:
|
167
|
-
- -
|
166
|
+
- - ">="
|
168
167
|
- !ruby/object:Gem::Version
|
169
168
|
version: '0'
|
170
169
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
171
170
|
requirements:
|
172
|
-
- -
|
171
|
+
- - ">="
|
173
172
|
- !ruby/object:Gem::Version
|
174
173
|
version: '0'
|
175
174
|
requirements: []
|
176
175
|
rubyforge_project: rets
|
177
|
-
rubygems_version: 2.
|
176
|
+
rubygems_version: 2.2.0
|
178
177
|
signing_key:
|
179
178
|
specification_version: 4
|
180
|
-
summary:
|
181
|
-
A pure-ruby library for fetching data from [RETS] servers
|
179
|
+
summary: "[![Build Status](https://secure.travis-ci.org/estately/rets.png?branch=master)](http://travis-ci.org/estately/rets)
|
180
|
+
A pure-ruby library for fetching data from [RETS] servers"
|
182
181
|
test_files:
|
183
182
|
- test/test_client.rb
|
184
183
|
- test/test_locking_http_client.rb
|