rets 0.5.1 → 0.6.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 +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
|
+
[](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: "[](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
|