sean-rets 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/CHANGELOG.md +89 -0
- data/Manifest.txt +28 -0
- data/README.md +47 -0
- data/Rakefile +27 -0
- data/bin/rets +202 -0
- data/lib/rets.rb +45 -0
- data/lib/rets/client.rb +391 -0
- data/lib/rets/client_progress_reporter.rb +44 -0
- data/lib/rets/http_client.rb +91 -0
- data/lib/rets/locking_http_client.rb +34 -0
- data/lib/rets/measuring_http_client.rb +27 -0
- data/lib/rets/metadata.rb +6 -0
- data/lib/rets/metadata/containers.rb +84 -0
- data/lib/rets/metadata/lookup_type.rb +17 -0
- data/lib/rets/metadata/resource.rb +84 -0
- data/lib/rets/metadata/rets_class.rb +48 -0
- data/lib/rets/metadata/root.rb +152 -0
- data/lib/rets/metadata/table.rb +113 -0
- data/lib/rets/parser/compact.rb +62 -0
- data/lib/rets/parser/multipart.rb +40 -0
- data/test/fixtures.rb +212 -0
- data/test/helper.rb +14 -0
- data/test/test_client.rb +238 -0
- data/test/test_locking_http_client.rb +29 -0
- data/test/test_metadata.rb +459 -0
- data/test/test_parser_compact.rb +86 -0
- data/test/test_parser_multipart.rb +39 -0
- data/test/vcr_cassettes/unauthorized_response.yml +262 -0
- metadata +186 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 679f076c80dc81f0dc15aecb394ff3435250244f
|
4
|
+
data.tar.gz: 4c287ca2d5ed9e28617cc3890701fa7e5d41707c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fcdde80697677da90f2952749b1259e88cee217c11f2b2a122584bf55424dc8defe9f782f0d03262d5a448dbcc0afcb570277831543af028dbcfa117c5426dd9
|
7
|
+
data.tar.gz: 0192696920f4fa35c48139503dc0db6d762b7902603555b401398d545ea8733a8c4c07efa2653f186113bfc6335b3a5f339f042b49a15e06145a31e4529172b5
|
data/.gemtest
ADDED
File without changes
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,89 @@
|
|
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
|
+
|
22
|
+
### 0.5.0 / 2013-09-05
|
23
|
+
|
24
|
+
* feature: Allow client.count to get integer count
|
25
|
+
* feature: Allow for downcased capability names
|
26
|
+
* fix: Handle the rets element being empty
|
27
|
+
* feature: Instrument rets client with stats reporting
|
28
|
+
* feature: Add a locking client
|
29
|
+
* feature: Support Basic Authentication
|
30
|
+
|
31
|
+
### 0.4.0 / 2012-08-29
|
32
|
+
|
33
|
+
* fix: update authentication header to uri matches path
|
34
|
+
|
35
|
+
### 0.3.0 / 2012-07-31
|
36
|
+
|
37
|
+
* correctly handle digest authentication
|
38
|
+
|
39
|
+
### 0.3.0.rc.0 / 2012-07-26
|
40
|
+
|
41
|
+
* feature: significantly better handling of authorization failures
|
42
|
+
|
43
|
+
### 0.2.1 / 2012-04-20
|
44
|
+
|
45
|
+
* fix: better handling of malformed RETS responses
|
46
|
+
|
47
|
+
### 0.2.0 / 2012-04-20
|
48
|
+
|
49
|
+
* feature: Ruby 1.9 compatibility!
|
50
|
+
|
51
|
+
### 0.1.7 / 2012-04-05
|
52
|
+
|
53
|
+
* feature: key_field lookup for resources
|
54
|
+
|
55
|
+
### 0.1.6 / 2012-04-03
|
56
|
+
|
57
|
+
* fix: user_agent authentication
|
58
|
+
|
59
|
+
### 0.1.5 / 2012-03-17
|
60
|
+
|
61
|
+
* fix: retries raise error after too many failures
|
62
|
+
* fix: raise error for failed multipart object request
|
63
|
+
* fix: retries start with a clean slate, fixing authorization errors during retry
|
64
|
+
|
65
|
+
### 0.1.4 / 2012-03-12
|
66
|
+
|
67
|
+
* fix: an MLS uses lower case in RETS tag
|
68
|
+
|
69
|
+
### 0.1.3 / 2012-03-05
|
70
|
+
|
71
|
+
* fixes to support location=1 in getobject query
|
72
|
+
|
73
|
+
### 0.1.2 / 2012-02-17
|
74
|
+
|
75
|
+
* bugfix - check ReplyCode in login, retry on errors
|
76
|
+
|
77
|
+
### 0.1.1 / 2012-01-11
|
78
|
+
|
79
|
+
* bugfix - prevent infinite loop in login
|
80
|
+
|
81
|
+
# rets Changelog
|
82
|
+
|
83
|
+
### 0.1.0 / 2011-06-23
|
84
|
+
|
85
|
+
* First public release!
|
86
|
+
|
87
|
+
### 0.0.1 / 2011-03-24
|
88
|
+
|
89
|
+
* Project Created
|
data/Manifest.txt
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
CHANGELOG.md
|
2
|
+
Manifest.txt
|
3
|
+
README.md
|
4
|
+
Rakefile
|
5
|
+
bin/rets
|
6
|
+
lib/rets.rb
|
7
|
+
lib/rets/client.rb
|
8
|
+
lib/rets/client_progress_reporter.rb
|
9
|
+
lib/rets/http_client.rb
|
10
|
+
lib/rets/locking_http_client.rb
|
11
|
+
lib/rets/measuring_http_client.rb
|
12
|
+
lib/rets/metadata.rb
|
13
|
+
lib/rets/metadata/containers.rb
|
14
|
+
lib/rets/metadata/lookup_type.rb
|
15
|
+
lib/rets/metadata/resource.rb
|
16
|
+
lib/rets/metadata/rets_class.rb
|
17
|
+
lib/rets/metadata/root.rb
|
18
|
+
lib/rets/metadata/table.rb
|
19
|
+
lib/rets/parser/compact.rb
|
20
|
+
lib/rets/parser/multipart.rb
|
21
|
+
test/fixtures.rb
|
22
|
+
test/helper.rb
|
23
|
+
test/test_client.rb
|
24
|
+
test/test_locking_http_client.rb
|
25
|
+
test/test_metadata.rb
|
26
|
+
test/test_parser_compact.rb
|
27
|
+
test/test_parser_multipart.rb
|
28
|
+
test/vcr_cassettes/unauthorized_response.yml
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# rets
|
2
|
+
|
3
|
+
* http://github.com/estately/rets
|
4
|
+
|
5
|
+
## DESCRIPTION:
|
6
|
+
|
7
|
+
[![Build Status](https://secure.travis-ci.org/estately/rets.png?branch=master)](http://travis-ci.org/estately/rets)
|
8
|
+
A pure-ruby library for fetching data from [RETS] servers.
|
9
|
+
|
10
|
+
[RETS]: http://www.rets.org
|
11
|
+
|
12
|
+
## REQUIREMENTS:
|
13
|
+
|
14
|
+
* [net-http-persistent]
|
15
|
+
* [nokogiri]
|
16
|
+
|
17
|
+
[net-http-persistent]: http://seattlerb.rubyforge.org/net-http-persistent/
|
18
|
+
[nokogiri]: http://nokogiri.org
|
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
|
+
|
24
|
+
## LICENSE:
|
25
|
+
|
26
|
+
(The MIT License)
|
27
|
+
|
28
|
+
Copyright (c) 2011 Estately, Inc. <opensource@estately.com>
|
29
|
+
|
30
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
31
|
+
a copy of this software and associated documentation files (the
|
32
|
+
'Software'), to deal in the Software without restriction, including
|
33
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
34
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
35
|
+
permit persons to whom the Software is furnished to do so, subject to
|
36
|
+
the following conditions:
|
37
|
+
|
38
|
+
The above copyright notice and this permission notice shall be included
|
39
|
+
in all copies or substantial portions of the Software.
|
40
|
+
|
41
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
42
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
43
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
44
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
45
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
46
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
47
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
Hoe.plugin :git, :doofus
|
6
|
+
Hoe.plugin :travis
|
7
|
+
Hoe.plugin :gemspec
|
8
|
+
|
9
|
+
Hoe.spec 'rets' do
|
10
|
+
developer 'Estately, Inc. Open Source', 'opensource@estately.com'
|
11
|
+
|
12
|
+
extra_deps << [ "httpclient", "~> 2.3.0" ]
|
13
|
+
extra_deps << [ "nokogiri", "~> 1.5.2" ]
|
14
|
+
|
15
|
+
extra_dev_deps << [ "mocha", "~> 0.11.0" ]
|
16
|
+
extra_dev_deps << [ "vcr", "~> 2.2.2" ]
|
17
|
+
extra_dev_deps << [ "webmock", "~> 1.8.0" ]
|
18
|
+
|
19
|
+
### Use markdown for changelog and readme
|
20
|
+
self.history_file = 'CHANGELOG.md'
|
21
|
+
self.readme_file = 'README.md'
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
Rake::TestTask.new do |t|
|
26
|
+
t.pattern = "test/test_*.rb"
|
27
|
+
end
|
data/bin/rets
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "optparse"
|
4
|
+
require "pp"
|
5
|
+
|
6
|
+
require "rubygems"
|
7
|
+
|
8
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
9
|
+
require "rets"
|
10
|
+
|
11
|
+
class RetsCli
|
12
|
+
def self.parse(args)
|
13
|
+
|
14
|
+
actions = %w(metadata search count object)
|
15
|
+
options = {:count => 5}
|
16
|
+
|
17
|
+
opts = OptionParser.new do |opts|
|
18
|
+
opts.banner = "Usage: #{File.basename($0)} URL [options] [query]"
|
19
|
+
|
20
|
+
opts.separator ""
|
21
|
+
opts.separator "Authentication options:"
|
22
|
+
|
23
|
+
opts.on("-U", "--username USERNAME", "The username to authenticate with.") do |username|
|
24
|
+
options[:username] = username
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-P", "--password [PASSWORD]", "The password to authenticate with.","Prompts if no argument is provided.") do |password|
|
28
|
+
options[:password] = password #or prompt # TODO
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("-A", "--agent AGENT", "User-Agent header to provide.") do |agent|
|
32
|
+
options[:agent] = agent
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-B", "--agent-password [PASSWORD]", "User-Agent password to provide.") do |ua_password|
|
36
|
+
options[:ua_password] = ua_password
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.separator ""
|
40
|
+
opts.separator "Actions:"
|
41
|
+
|
42
|
+
opts.on("-p", "--capabilities", "Print capabilities of the RETS server.") do |capabilities|
|
43
|
+
options[:capabilities] = capabilities
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on("-a", "--action ACTION", actions, "Action to perform (#{actions.join(",")}).") do |action|
|
47
|
+
options[:action] = action
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on("-m", "--metadata [FORMAT]", %w(tree long short), "Print metadata.", "Format is short, long or tree.", "Defaults to short.") do |format|
|
51
|
+
options[:action] = "metadata"
|
52
|
+
options[:metadata_format] = format || "short"
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.separator ""
|
56
|
+
opts.separator "Search action options:"
|
57
|
+
|
58
|
+
opts.on("-r", "--resource NAME", "Name of resource to search for.") do |name|
|
59
|
+
options[:resource] = name
|
60
|
+
end
|
61
|
+
|
62
|
+
opts.on("-c", "--class NAME", "Name of class to search for.") do |name|
|
63
|
+
options[:class] = name
|
64
|
+
end
|
65
|
+
|
66
|
+
opts.on("-n", "--number LIMIT", Integer, "Return LIMIT results. Defaults to 5.") do |limit|
|
67
|
+
options[:limit] = limit
|
68
|
+
end
|
69
|
+
|
70
|
+
opts.separator ""
|
71
|
+
opts.separator "Misc options:"
|
72
|
+
|
73
|
+
opts.on_tail("-v", "--verbose", "Be verbose.") do |verbose|
|
74
|
+
logger = Class.new do
|
75
|
+
def method_missing(method, *a, &b)
|
76
|
+
puts a
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
options[:logger] = logger.new
|
81
|
+
end
|
82
|
+
|
83
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
84
|
+
puts opts
|
85
|
+
exit
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on_tail("--version", "Show version") do
|
89
|
+
puts Rets::VERSION
|
90
|
+
exit
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
begin
|
96
|
+
opts.parse!(args.empty? ? ["-h"] : args)
|
97
|
+
rescue OptionParser::InvalidArgument => e
|
98
|
+
abort e.message
|
99
|
+
end
|
100
|
+
|
101
|
+
options
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
options = RetsCli.parse(ARGV)
|
107
|
+
url = ARGV[0] or abort "Need login URL"
|
108
|
+
query = ARGV[1]
|
109
|
+
|
110
|
+
client = Rets::Client.new(options.merge(:login_url => url))
|
111
|
+
|
112
|
+
if options[:capabilities]
|
113
|
+
pp client.capabilities
|
114
|
+
end
|
115
|
+
|
116
|
+
case options[:action]
|
117
|
+
when "metadata" then
|
118
|
+
metadata = client.metadata
|
119
|
+
|
120
|
+
if options[:metadata_format] != "tree"
|
121
|
+
preferred_fields = %w(ClassName SystemName ResourceID StandardName VisibleName MetadataEntryID KeyField)
|
122
|
+
|
123
|
+
|
124
|
+
# All types except system
|
125
|
+
types = Rets::METADATA_TYPES.map { |t| t.downcase.to_sym } - [:system]
|
126
|
+
|
127
|
+
types.each do |type|
|
128
|
+
# if RowContainer ...
|
129
|
+
rows = metadata[type]
|
130
|
+
|
131
|
+
puts type.to_s.capitalize
|
132
|
+
puts "="*40
|
133
|
+
|
134
|
+
print_key_value = lambda do |k,v|
|
135
|
+
key = "#{k}:".ljust(35)
|
136
|
+
value = "#{v}".ljust(35)
|
137
|
+
|
138
|
+
puts [key, value].join
|
139
|
+
end
|
140
|
+
|
141
|
+
rows.each do |row|
|
142
|
+
top, rest = row.partition { |k,v| preferred_fields.include?(k) }
|
143
|
+
|
144
|
+
top.each(&print_key_value)
|
145
|
+
|
146
|
+
rest.sort_by{|k,v|k}.each(&print_key_value) if options[:metadata_format] == "long"
|
147
|
+
|
148
|
+
puts
|
149
|
+
end
|
150
|
+
|
151
|
+
puts
|
152
|
+
end
|
153
|
+
|
154
|
+
# Tree format
|
155
|
+
else
|
156
|
+
metadata.print_tree
|
157
|
+
end
|
158
|
+
|
159
|
+
when "search" then
|
160
|
+
pp client.find(:all,
|
161
|
+
:search_type => options[:resource],
|
162
|
+
:class => options[:class],
|
163
|
+
:query => query,
|
164
|
+
:count => Rets::Client::COUNT.exclude,
|
165
|
+
:limit => options[:limit])
|
166
|
+
|
167
|
+
when "count" then
|
168
|
+
pp client.find(:all,
|
169
|
+
:search_type => options[:resource],
|
170
|
+
:class => options[:class],
|
171
|
+
:query => query,
|
172
|
+
:count => Rets::Client::COUNT.only,
|
173
|
+
:limit => options[:limit])
|
174
|
+
|
175
|
+
when "object" then
|
176
|
+
|
177
|
+
def write_objects(parts)
|
178
|
+
parts.each do |part|
|
179
|
+
cid = part.headers["content-id"].to_i
|
180
|
+
oid = part.headers["object-id"].to_i
|
181
|
+
|
182
|
+
File.open("tmp/#{cid}-#{oid}", "wb") do |f|
|
183
|
+
puts f.path
|
184
|
+
|
185
|
+
f.write part.body
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
parts = client.all_objects(
|
191
|
+
:resource => "Property",
|
192
|
+
:resource_id => 90020062739, # id from KeyField for a given property
|
193
|
+
:object_type => "Photo"
|
194
|
+
)
|
195
|
+
|
196
|
+
parts.each { |pt| p pt.headers }
|
197
|
+
|
198
|
+
write_objects(parts)
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
client.logout
|
data/lib/rets.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module Rets
|
6
|
+
VERSION = '0.6.0'
|
7
|
+
|
8
|
+
MalformedResponse = Class.new(ArgumentError)
|
9
|
+
UnknownResponse = Class.new(ArgumentError)
|
10
|
+
NoLogout = Class.new(ArgumentError)
|
11
|
+
|
12
|
+
class AuthorizationFailure < ArgumentError
|
13
|
+
attr_reader :status, :body
|
14
|
+
def initialize(status, body)
|
15
|
+
@status = status
|
16
|
+
@body = body
|
17
|
+
super("HTTP status: #{status} (#{body})")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class InvalidRequest < ArgumentError
|
22
|
+
attr_reader :error_code, :reply_text
|
23
|
+
def initialize(error_code, reply_text)
|
24
|
+
@error_code = error_code
|
25
|
+
@reply_text = reply_text
|
26
|
+
super("Got error code #{error_code} (#{reply_text})")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class UnknownCapability < ArgumentError
|
31
|
+
attr_reader :capability_name
|
32
|
+
def initialize(capability_name)
|
33
|
+
@capability_name = capability_name
|
34
|
+
super("unknown capability #{capability_name}")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'rets/client'
|
40
|
+
require 'rets/metadata'
|
41
|
+
require 'rets/parser/compact'
|
42
|
+
require 'rets/parser/multipart'
|
43
|
+
require 'rets/measuring_http_client'
|
44
|
+
require 'rets/locking_http_client'
|
45
|
+
require 'rets/client_progress_reporter'
|