simplyrets 0.1
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 +7 -0
- data/#example.rb# +29 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/LICENSE +21 -0
- data/README.org +109 -0
- data/Rakefile +2 -0
- data/example.rb +26 -0
- data/lib/monkey.rb +90 -0
- data/lib/properties_api.rb +75 -0
- data/lib/simplyrets.rb +57 -0
- data/lib/simplyrets.rb~ +85 -0
- data/lib/simplyrets/configuration.rb +25 -0
- data/lib/simplyrets/request.rb +205 -0
- data/lib/simplyrets/response.rb +70 -0
- data/lib/simplyrets/version.rb +4 -0
- data/models/agent.rb +39 -0
- data/models/contactinformation.rb +36 -0
- data/models/geographicdata.rb +43 -0
- data/models/listing.rb +95 -0
- data/models/mlsinformation.rb +35 -0
- data/models/office.rb +39 -0
- data/models/property.rb +84 -0
- data/models/school.rb +27 -0
- data/models/streetaddress.rb +47 -0
- data/models/tax.rb +28 -0
- data/pkg/simplyrets-0.1.gem +0 -0
- data/simplyrets.gemspec +25 -0
- metadata +144 -0
data/lib/simplyrets.rb~
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
|
2
|
+
require 'monkey'
|
3
|
+
require 'simplyrets/configuration'
|
4
|
+
require 'simplyrets/request'
|
5
|
+
require 'simplyrets/response'
|
6
|
+
require 'simplyrets/version'
|
7
|
+
require 'logger'
|
8
|
+
|
9
|
+
module SimplyRets
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_accessor :logger
|
13
|
+
|
14
|
+
# A SimplyRets configuration object. Must act like a hash and return sensible
|
15
|
+
# values for all SimplyRets configuration options. See SimplyRets::Configuration.
|
16
|
+
attr_accessor :configuration
|
17
|
+
|
18
|
+
attr_accessor :resources
|
19
|
+
|
20
|
+
# Call this method to modify defaults in your initializers.
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# SimplyRets.configure do |config|
|
24
|
+
# config.api_key = '1234567890abcdef' # required
|
25
|
+
# config.username = 'wordlover' # optional, but needed for user-related functions
|
26
|
+
# config.password = 'i<3words' # optional, but needed for user-related functions
|
27
|
+
# config.format = 'json' # optional, defaults to 'json'
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
def configure
|
31
|
+
self.configuration ||= Configuration.new
|
32
|
+
yield(configuration) if block_given?
|
33
|
+
|
34
|
+
# Configure logger. Default to use Rails
|
35
|
+
self.logger ||= configuration.logger || (defined?(Rails) ? Rails.logger : Logger.new(STDOUT))
|
36
|
+
|
37
|
+
# remove :// from scheme
|
38
|
+
configuration.scheme.sub!(/:\/\//, '')
|
39
|
+
|
40
|
+
# remove http(s):// and anything after a slash
|
41
|
+
configuration.host.sub!(/https?:\/\//, '')
|
42
|
+
configuration.host = configuration.host.split('/').first
|
43
|
+
|
44
|
+
# Add leading and trailing slashes to base_path
|
45
|
+
configuration.base_path = "/#{configuration.base_path}".gsub(/\/+/, '/')
|
46
|
+
configuration.base_path = "" if configuration.base_path == "/"
|
47
|
+
end
|
48
|
+
|
49
|
+
def authenticated?
|
50
|
+
SimplyRets.configuration.auth_token.present?
|
51
|
+
end
|
52
|
+
|
53
|
+
def de_authenticate
|
54
|
+
SimplyRets.configuration.auth_token = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def authenticate
|
58
|
+
return if SimplyRets.authenticated?
|
59
|
+
|
60
|
+
if SimplyRets.configuration.username.blank? || SimplyRets.configuration.password.blank?
|
61
|
+
raise ClientError, "Username and password are required to authenticate."
|
62
|
+
end
|
63
|
+
|
64
|
+
request = SimplyRets::Request.new(
|
65
|
+
:get,
|
66
|
+
"account/authenticate/{username}",
|
67
|
+
:params => {
|
68
|
+
:username => SimplyRets.configuration.username,
|
69
|
+
:password => SimplyRets.configuration.password
|
70
|
+
}
|
71
|
+
)
|
72
|
+
|
73
|
+
response_body = request.response.body
|
74
|
+
SimplyRets.configuration.auth_token = response_body['token']
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
class ServerError < StandardError
|
82
|
+
end
|
83
|
+
|
84
|
+
class ClientError < StandardError
|
85
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module SimplyRets
|
2
|
+
|
3
|
+
class Configuration
|
4
|
+
require 'simplyrets/version'
|
5
|
+
|
6
|
+
attr_accessor :format, :api_key, :username, :password,
|
7
|
+
:auth_token, :scheme, :host, :base_path,
|
8
|
+
:user_agent, :logger, :inject_format,
|
9
|
+
:force_ending_format, :camelize_params
|
10
|
+
|
11
|
+
# Defaults go in here..
|
12
|
+
def initialize
|
13
|
+
@format = 'json'
|
14
|
+
@scheme = 'https'
|
15
|
+
@host = 'api.simplyrets.com'
|
16
|
+
@base_path = '/'
|
17
|
+
@user_agent = "ruby-#{SimplyRets::VERSION}"
|
18
|
+
@inject_format = false
|
19
|
+
@force_ending_format = false
|
20
|
+
@camelize_params = true
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
module SimplyRets
|
2
|
+
|
3
|
+
class Request
|
4
|
+
require 'uri'
|
5
|
+
require 'addressable/uri'
|
6
|
+
require 'typhoeus'
|
7
|
+
require 'base64'
|
8
|
+
require "simplyrets/version"
|
9
|
+
|
10
|
+
attr_accessor :host, :path, :format, :params, :body, :http_method, :headers
|
11
|
+
|
12
|
+
|
13
|
+
# All requests must have an HTTP method and a path
|
14
|
+
# Optionals parameters are :params, :headers, :body, :format, :host
|
15
|
+
#
|
16
|
+
def initialize(http_method, path, attributes={})
|
17
|
+
attributes[:format] ||= SimplyRets.configuration.format
|
18
|
+
attributes[:params] ||= {}
|
19
|
+
|
20
|
+
# Set default headers
|
21
|
+
default_headers = {
|
22
|
+
'Content-Type' => "application/#{attributes[:format].downcase}",
|
23
|
+
:api_key => SimplyRets.configuration.api_key
|
24
|
+
}
|
25
|
+
|
26
|
+
# api_key from headers hash trumps the default, even if its value is blank
|
27
|
+
if attributes[:headers].present? && attributes[:headers].has_key?(:api_key)
|
28
|
+
default_headers.delete(:api_key)
|
29
|
+
end
|
30
|
+
|
31
|
+
# api_key from params hash trumps all others (headers and default_headers)
|
32
|
+
if attributes[:params].present? && attributes[:params].has_key?(:api_key)
|
33
|
+
default_headers.delete(:api_key)
|
34
|
+
attributes[:headers].delete(:api_key) if attributes[:headers].present?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Merge argument headers into defaults
|
38
|
+
attributes[:headers] = default_headers.merge(attributes[:headers] || {})
|
39
|
+
|
40
|
+
# Stick in the auth header
|
41
|
+
auth = SimplyRets.configuration.username + ":" + SimplyRets.configuration.password
|
42
|
+
attributes[:headers].merge!({"Authorization" =>
|
43
|
+
("Basic " + Base64.encode64(auth))})
|
44
|
+
|
45
|
+
self.http_method = http_method.to_sym
|
46
|
+
self.path = path
|
47
|
+
attributes.each do |name, value|
|
48
|
+
send("#{name.to_s.underscore.to_sym}=", value)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Construct a base URL
|
53
|
+
#
|
54
|
+
def url(options = {})
|
55
|
+
u = Addressable::URI.new(
|
56
|
+
:scheme => SimplyRets.configuration.scheme,
|
57
|
+
:host => SimplyRets.configuration.host,
|
58
|
+
:path => self.interpreted_path,
|
59
|
+
:query => self.query_string.sub(/\?/, '')
|
60
|
+
).to_s
|
61
|
+
|
62
|
+
# Drop trailing question mark, if present
|
63
|
+
u.sub! /\?$/, ''
|
64
|
+
|
65
|
+
u
|
66
|
+
end
|
67
|
+
|
68
|
+
# Iterate over the params hash, injecting any path values into the path string
|
69
|
+
#
|
70
|
+
# NOTE: This is currently not supported in the SimplyRETS API. Content
|
71
|
+
# formatting is handled via the accept header.
|
72
|
+
#
|
73
|
+
# e.g. /properties.{format}/{word}/entries => /word.json/cat/entries
|
74
|
+
def interpreted_path
|
75
|
+
p = self.path.dup
|
76
|
+
|
77
|
+
# Fill in the path params
|
78
|
+
self.params.each_pair do |key, value|
|
79
|
+
p = p.gsub("{#{key}}", value.to_s)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Stick a .{format} placeholder into the path if there isn't
|
83
|
+
# one already or an actual format like json or xml
|
84
|
+
# e.g. /properties/mlsId => /properties.{format}/mlsId
|
85
|
+
if SimplyRets.configuration.inject_format
|
86
|
+
unless ['.json', '.xml', '{format}'].any? {|s| p.downcase.include? s }
|
87
|
+
p = p.sub(/^(\/?\w+)/, "\\1.#{format}")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Stick a .{format} placeholder on the end of the path if there
|
92
|
+
# isn't one already or an actual format like json or xml
|
93
|
+
#
|
94
|
+
# NOTE: This is currently not supported in the SimplyRETS
|
95
|
+
# API. Content formatting is handled via the accept header.
|
96
|
+
#
|
97
|
+
# e.g. /properties/blah => /properties/blah.{format}
|
98
|
+
if SimplyRets.configuration.force_ending_format
|
99
|
+
unless ['.json', '.xml', '{format}'].any? {|s| p.downcase.include? s }
|
100
|
+
p = "#{p}.#{format}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
p = p.sub("{format}", self.format.to_s)
|
105
|
+
|
106
|
+
URI.encode [SimplyRets.configuration.base_path, p].join("/").gsub(/\/+/, '/')
|
107
|
+
end
|
108
|
+
|
109
|
+
# Massage the request body into a state of readiness If body is a
|
110
|
+
# hash, camelize all keys then convert to a json string
|
111
|
+
def body=(value)
|
112
|
+
if value.is_a?(Hash)
|
113
|
+
value = value.inject({}) do |memo, (k,v)|
|
114
|
+
memo[k.to_s.camelize(:lower).to_sym] = v
|
115
|
+
memo
|
116
|
+
end
|
117
|
+
end
|
118
|
+
@body = value
|
119
|
+
end
|
120
|
+
|
121
|
+
# If body is an object, JSONify it before making the actual request.
|
122
|
+
#
|
123
|
+
def outgoing_body
|
124
|
+
body.is_a?(String) ? body : body.to_json
|
125
|
+
end
|
126
|
+
|
127
|
+
# Construct a query string from the query-string-type params
|
128
|
+
def query_string
|
129
|
+
|
130
|
+
# Iterate over all params,
|
131
|
+
# .. removing the ones that are part of the path itself.
|
132
|
+
# .. stringifying values so Addressable doesn't blow up.
|
133
|
+
query_values = {}
|
134
|
+
self.params.each_pair do |key, value|
|
135
|
+
next if self.path.include? "{#{key}}" # skip path params
|
136
|
+
next if value.blank? && value.class != FalseClass # skip empties
|
137
|
+
if SimplyRets.configuration.camelize_params
|
138
|
+
# api_key is not a camelCased param
|
139
|
+
#
|
140
|
+
# NOTE: api_key param is not currently used by SimplyRETS API
|
141
|
+
key = key.to_s.camelize(:lower).to_sym unless key.to_sym == :api_key
|
142
|
+
end
|
143
|
+
query_values[key] = value
|
144
|
+
end
|
145
|
+
|
146
|
+
# We don't want to end up with '?' as our query string
|
147
|
+
# if there aren't really any params
|
148
|
+
return "" if query_values.blank?
|
149
|
+
|
150
|
+
# Addressable requires query_values to be set after initialization..
|
151
|
+
qs = Addressable::URI.new
|
152
|
+
qs.query_values = query_values
|
153
|
+
qs.to_s
|
154
|
+
end
|
155
|
+
|
156
|
+
def make
|
157
|
+
logger = Logger.new STDOUT
|
158
|
+
logger.debug self.url
|
159
|
+
response = case self.http_method.to_sym
|
160
|
+
when :get,:GET
|
161
|
+
Typhoeus::Request.get(
|
162
|
+
self.url,
|
163
|
+
:headers => self.headers.stringify_keys,
|
164
|
+
)
|
165
|
+
|
166
|
+
when :post,:POST
|
167
|
+
Typhoeus::Request.post(
|
168
|
+
self.url,
|
169
|
+
:body => self.outgoing_body,
|
170
|
+
:headers => self.headers.stringify_keys,
|
171
|
+
)
|
172
|
+
|
173
|
+
when :put,:PUT
|
174
|
+
Typhoeus::Request.put(
|
175
|
+
self.url,
|
176
|
+
:body => self.outgoing_body,
|
177
|
+
:headers => self.headers.stringify_keys,
|
178
|
+
)
|
179
|
+
|
180
|
+
when :delete,:DELETE
|
181
|
+
Typhoeus::Request.delete(
|
182
|
+
self.url,
|
183
|
+
:body => self.outgoing_body,
|
184
|
+
:headers => self.headers.stringify_keys,
|
185
|
+
)
|
186
|
+
end
|
187
|
+
Response.new(response)
|
188
|
+
end
|
189
|
+
|
190
|
+
def response
|
191
|
+
self.make
|
192
|
+
end
|
193
|
+
|
194
|
+
def response_code_pretty
|
195
|
+
return unless @response.present?
|
196
|
+
@response.code.to_s
|
197
|
+
end
|
198
|
+
|
199
|
+
def response_headers_pretty
|
200
|
+
return unless @response.present?
|
201
|
+
@response.headers.gsub(/\n/, '<br/>') # <- This is for Typhoeus
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module SimplyRets
|
2
|
+
|
3
|
+
class Response
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
attr_accessor :raw
|
7
|
+
|
8
|
+
def initialize(raw)
|
9
|
+
self.raw = raw
|
10
|
+
|
11
|
+
case self.code
|
12
|
+
when 500..510 then raise(ServerError, self.error_message)
|
13
|
+
when 299..426 then raise(ClientError, self.error_message)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def code
|
18
|
+
raw.code
|
19
|
+
end
|
20
|
+
|
21
|
+
# Account for error messages that take different forms...
|
22
|
+
def error_message
|
23
|
+
body['message']
|
24
|
+
rescue
|
25
|
+
body
|
26
|
+
end
|
27
|
+
|
28
|
+
# If body is JSON, parse it
|
29
|
+
# Otherwise return raw string
|
30
|
+
def body
|
31
|
+
JSON.parse raw.body
|
32
|
+
rescue
|
33
|
+
raw.body
|
34
|
+
end
|
35
|
+
|
36
|
+
# `headers_hash` is a Typhoeus-specific extension of Hash,
|
37
|
+
# so simplify it back into a regular old Hash.
|
38
|
+
def headers
|
39
|
+
h = {}
|
40
|
+
raw.headers_hash.each {|k,v| h[k] = v }
|
41
|
+
h
|
42
|
+
end
|
43
|
+
|
44
|
+
# Extract the response format from the header hash
|
45
|
+
# e.g. {'Content-Type' => 'application/json'}
|
46
|
+
def format
|
47
|
+
headers['Content-Type'].split("/").last.downcase
|
48
|
+
end
|
49
|
+
|
50
|
+
def json?
|
51
|
+
format == 'json'
|
52
|
+
end
|
53
|
+
|
54
|
+
def xml?
|
55
|
+
format == 'xml'
|
56
|
+
end
|
57
|
+
|
58
|
+
def pretty_body
|
59
|
+
return unless body.present?
|
60
|
+
case format
|
61
|
+
when 'json' then JSON.pretty_generate(body).gsub(/\n/, '<br/>')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def pretty_headers
|
66
|
+
JSON.pretty_generate(headers).gsub(/\n/, '<br/>')
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
data/models/agent.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
class Agent
|
2
|
+
attr_accessor :last_name, :contact, :first_name, :id
|
3
|
+
|
4
|
+
# :internal => :external
|
5
|
+
def self.attribute_map
|
6
|
+
{
|
7
|
+
:last_name => :lastName,
|
8
|
+
:contact => :contact,
|
9
|
+
:first_name => :firstName,
|
10
|
+
:id => :id
|
11
|
+
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(attributes = {})
|
16
|
+
return if attributes.empty?
|
17
|
+
# Morph attribute keys into undescored rubyish style
|
18
|
+
if self.class.attribute_map[:"last_name"]
|
19
|
+
@last_name = attributes["lastName"]
|
20
|
+
end
|
21
|
+
if self.class.attribute_map[:"contact"]
|
22
|
+
@contact = attributes["contact"]
|
23
|
+
end
|
24
|
+
if self.class.attribute_map[:"first_name"]
|
25
|
+
@first_name = attributes["firstName"]
|
26
|
+
end
|
27
|
+
if self.class.attribute_map[:"id"]
|
28
|
+
@id = attributes["id"]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_body
|
33
|
+
body = {}
|
34
|
+
self.class.attribute_map.each_pair do |key, value|
|
35
|
+
body[value] = self.send(key) unless self.send(key).nil?
|
36
|
+
end
|
37
|
+
body
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class ContactInformation
|
2
|
+
attr_accessor :office, :cell, :full
|
3
|
+
|
4
|
+
# :internal => :external
|
5
|
+
def self.attribute_map
|
6
|
+
{
|
7
|
+
:office => :office,
|
8
|
+
:cell => :cell,
|
9
|
+
:full => :full
|
10
|
+
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(attributes = {})
|
15
|
+
return if attributes.empty?
|
16
|
+
# Morph attribute keys into undescored rubyish style
|
17
|
+
if self.class.attribute_map[:"office"]
|
18
|
+
@office = attributes["office"]
|
19
|
+
end
|
20
|
+
if self.class.attribute_map[:"cell"]
|
21
|
+
@cell = attributes["cell"]
|
22
|
+
end
|
23
|
+
if self.class.attribute_map[:"full"]
|
24
|
+
@full = attributes["full"]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_body
|
30
|
+
body = {}
|
31
|
+
self.class.attribute_map.each_pair do |key, value|
|
32
|
+
body[value] = self.send(key) unless self.send(key).nil?
|
33
|
+
end
|
34
|
+
body
|
35
|
+
end
|
36
|
+
end
|