simplyrets 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -0,0 +1,4 @@
1
+ module SimplyRets
2
+ VERSION = "0.1"
3
+ end
4
+
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