you_got_listed 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/.document +5 -0
  2. data/.gitignore +25 -0
  3. data/Gemfile +2 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +226 -0
  6. data/Rakefile +18 -0
  7. data/lib/you_got_listed.rb +17 -0
  8. data/lib/you_got_listed/accounts.rb +9 -0
  9. data/lib/you_got_listed/agent.rb +24 -0
  10. data/lib/you_got_listed/client.rb +25 -0
  11. data/lib/you_got_listed/complex.rb +42 -0
  12. data/lib/you_got_listed/complexes.rb +53 -0
  13. data/lib/you_got_listed/error.rb +17 -0
  14. data/lib/you_got_listed/lead.rb +13 -0
  15. data/lib/you_got_listed/listing.rb +73 -0
  16. data/lib/you_got_listed/listings.rb +110 -0
  17. data/lib/you_got_listed/resource.rb +19 -0
  18. data/lib/you_got_listed/response.rb +25 -0
  19. data/lib/you_got_listed/version.rb +3 -0
  20. data/spec/fixtures/responses/error.xml +2 -0
  21. data/spec/spec.opts +1 -0
  22. data/spec/spec_helper.rb +18 -0
  23. data/spec/support/complex_helper.rb +82 -0
  24. data/spec/support/listing_helper.rb +59 -0
  25. data/spec/support/webmock_helper.rb +26 -0
  26. data/spec/you_got_listed/accounts_spec.rb +26 -0
  27. data/spec/you_got_listed/agent_spec.rb +94 -0
  28. data/spec/you_got_listed/client_spec.rb +17 -0
  29. data/spec/you_got_listed/complex_spec.rb +26 -0
  30. data/spec/you_got_listed/complexes_spec.rb +80 -0
  31. data/spec/you_got_listed/error_spec.rb +10 -0
  32. data/spec/you_got_listed/lead_spec.rb +56 -0
  33. data/spec/you_got_listed/listing_spec.rb +110 -0
  34. data/spec/you_got_listed/listings_spec.rb +229 -0
  35. data/spec/you_got_listed/resource_spec.rb +17 -0
  36. data/spec/you_got_listed/response_spec.rb +119 -0
  37. data/spec/you_got_listed_api_key.yml.example +1 -0
  38. data/you_got_listed.gemspec +31 -0
  39. metadata +231 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ Gemfile.lock
23
+ *.gem
24
+ spec/you_got_listed_api_key.yml
25
+ spec/fixtures/vcr_cassettes/*.yml
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 tcocca
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,226 @@
1
+ = you_got_listed
2
+
3
+ YouGotListed is a ruby gem that wraps the YouGotListings api. YouGotListed is built on top of HTTParty and Rash (a Hashie::Mash extension).
4
+
5
+ You must have access to the YouGotListings API via an api key.
6
+
7
+ Currently the gem supports a subset of the full YouGotListings api and is READ-ONLY wrapper with the exception of creating Leads.
8
+
9
+ YouGotListings API documentation can be found here: http://www.yougotlistings.com/wiki/index.php/Developers
10
+
11
+
12
+ == Installation
13
+
14
+ gem install you_got_listed
15
+
16
+ In a rails 2.3.x app
17
+
18
+ config.gem 'you_got_listed'
19
+
20
+
21
+ == Usage
22
+
23
+ Responses all come back as Hashie::Rash objects (see http://github.com/tcocca/rash).
24
+
25
+ HTTParty parsed the xml into a Hash and Hashie::Rash will convert all keys to underscored names from camel case and then makes the keys accessible as methods.
26
+
27
+ Responses by default do not raise exceptions if the API errors, but the success? method will return false.
28
+ There are some methods that you can call that will trigger exceptions (typically the ! methods, see below).
29
+
30
+ Timeout::Errors and other Exceptions are automatically rescued in the YouGotListed::Client class and will return a
31
+ fake response telling you that there was a timeout or an unknown error in the @response.error method, same as any actual YGL api error messages.
32
+
33
+
34
+ === Creating the YouGotListed Client
35
+
36
+ A client object is necessary for each request type. The client is the base class that performs all of the operations, it requires your api key.
37
+
38
+ @ygl = YouGotListed::Client.new('you_api_key')
39
+
40
+ ** For all operations the "key" required parameter is automatically passed so you never need to worry about this parameter as the client class includes it by default.
41
+
42
+ === Searching Accounts
43
+
44
+ To find all of the accounts that your API key can access call #search off the Accounts class:
45
+
46
+ @accounts = YouGotListed::Accounts.new(@ygl)
47
+ @results = @accounts.search
48
+
49
+ The search method takes an optional hash of options that can be found here: http://www.yougotlistings.com/wiki/index.php/Account_API
50
+ Pass the options in the following format:
51
+
52
+ @results = @accounts.search(:page_count => 25, :page_index => 2)
53
+
54
+ The key names are the same as the param name.
55
+
56
+ == Searching for Agents
57
+
58
+ @agents = YouGotListed::Agent.new(@ygl)
59
+
60
+ The YouGotListed::Agent class has 3 methods:
61
+
62
+ See the Documentation here: http://www.yougotlistings.com/wiki/index.php/Agents_API
63
+
64
+ #find_all Will return all agents belonging to your accounts, there are not params required.
65
+
66
+ @result = @agents.find_all
67
+
68
+ #find : YouGotListed::Agent#find takes 1 argument an agent_id. The operation will raise an error if the api returns an error
69
+
70
+ @result = @agents.find(1)
71
+
72
+ #find_by_id : YouGotListed::Agent#find_by_id is the same as #find however it will not raise an error.
73
+
74
+ @result = @agents.find_by_id(1)
75
+
76
+
77
+ == Creating a Lead
78
+
79
+ The YouGotListed::Lead class has 2 methods to create a lead in the YouGotListings system: http://www.yougotlistings.com/wiki/index.php/Leads_Insert_API
80
+
81
+ #create will insert a lead and will NOT raise an error if the api returns one
82
+
83
+ #create! is the exact same but will raise an error
84
+
85
+ @leads = YouGotListed::Lead.new(@ygl)
86
+ @leads.create(:first_name => 'Test', :last_name => 'Lead', :email => 'test@lead.com')
87
+
88
+
89
+ == Searching for Complexes
90
+
91
+ See the API documentation: http://www.yougotlistings.com/wiki/index.php/Complexes_Search_API
92
+
93
+ @complexes = YouGotListed::Complexes.new(@ygl)
94
+
95
+ There are two methods in the Complexes class: #search and #find_by_id
96
+
97
+ Fist off, searching: The #search method takes an optional hash of params. The keys/values directly correspond to the api documentation
98
+
99
+ @results = @complexes.search
100
+ @results = @complexes.search(:cities => 'Boston')
101
+
102
+
103
+ The results from complex methods return a custom YouGotListed::Complexes::SearchResponse which inherits from the standard YouGotListed::Response. The YouGotListed::Complexes::SearchResponse provides a few helper methods:
104
+
105
+ @results.property_complexes # => Returns an array of YouGotListed::Complex objects (see below)
106
+ @results.paginator # => Returns a WillPaginate::Collection object for the response. The collection is the #property_complexes array that is returned.
107
+
108
+
109
+ The other methods to access complexes is #find_by_id
110
+
111
+ @complex = @complexes.find_by_id('BOS-001-001')
112
+
113
+ #find_by_id returns either a single YouGotListed::Complex object or nil, there is no response object.
114
+
115
+
116
+ == YouGotListed::Complex
117
+
118
+ The YouGotListed::Complex takes a Hashie::Rash object and turns it into a first level object. Each key in that hash is turned into an instance variable and a getter is defined for the key so that you can use the methods in others. It also takes a second param, a YouGotListed::Client object. The class provides 3 helper methods:
119
+
120
+ #properties - Returns an array of YouGotListed::Listing objects (see below)
121
+ #pictures - Returns an array of photo urls for the complex
122
+ #find_address - Takes an address_id for and returns the corresponding address object. Addresses are tied to the complex but properties for the complex just return an address_id, so this is how we tie the address information to the property
123
+
124
+
125
+ == Searching for Listings
126
+
127
+ @listings = YouGotListed::Listings.new(@ygl)
128
+
129
+ There are a number of ways to search for / find listings, reference documentation here: http://www.yougotlistings.com/wiki/index.php/Listings_API
130
+
131
+ #search - Takes an optional hash of parameters, keys and values correspond to the documentation above
132
+
133
+ @results = @listings.search(:city_neighborhood => 'Boston:Back Bay')
134
+
135
+ #featured - Takes 2 params (both optional). The first is a params hash (same as search), the second is the 'Featured Tag' in the system. The default is "Featured Rentals"
136
+
137
+ @results = @listings.featured
138
+ @results = @listings.featured(:page_count => 30)
139
+ @results = @listings.featured({}, 'Featured Listings') # only changing the featured tag
140
+
141
+ The reason that the featured tag is second is for pagination, you must pass the :page_index through and I did not want to have to pass the default tag first, so that is why the options hash is first (I would not be completely opposed to changing this however, if somebody has a better option).
142
+
143
+ Both #search and #featured return a custom YouGotListed::Listings::SearchResponse class that inherits from YouGotListed::Response. This class provides 3 additional methods:
144
+
145
+ #properties - returns an array of YouGotListed::Listing objects (see below)
146
+ #paginator - returns a WillPaginate::Collection object (the collection is the properties array)
147
+ #mls_results? - returns true if any of the properties have a source of 'MLS'
148
+
149
+
150
+ There are 3 other methods for accessing listings:
151
+
152
+ #find_by_id - takes an id and returns either a YouGotListed::Listing or nil
153
+
154
+ @listings.find_by_id('CAM-001-002')
155
+
156
+ #find_all - takes a params hash just like search but will iterate over all pages of the results and return a single array of YouGotListed::Listing objects
157
+
158
+ #find_all_by_ids - works the sames as find_all but will find the listings by IDs. Takes 2 params. First is either an array or comma separated string of listing_ids. The second (optional, defaults to true) is a boolean to return off market listings.
159
+
160
+ @listings.find_all_by_ids(['CAM-001-001', 'CAM-001-002'])
161
+
162
+ Again, this method iterates over all the pages of results and returns a single array of YouGotListed::Listing objects.
163
+
164
+
165
+ == YouGotListed::Listing
166
+
167
+ The YouGotListed::Listing class is similar to YouGotListed::Complex, it takes a Hashie::Rash object for a listing and provides some additional methods.
168
+
169
+ #town_neighborhood - returns a string of "#{city} - #{neighborhood}" useful for display purposes, if neighborhood is blank it will just return "#{city}"
170
+ #city_neighborhood - return a string of ""#{city}:#{neighborhood}", if neighborhood is blank it will only return "#{city}". This method is useful because to search for listings in a location you must pass the location in this format.
171
+ #mls_listing? - returns true if the source is "MLS"
172
+ #latitude - returns the latitude for the property except as a float instead of a string
173
+ #longitude - returns the longitude for the property except as a float instead of a string
174
+ #pictures - returns an array of photo urls for the property, if there are no photos it returns nil
175
+ #main_picture - returns the first photo url or nil if there are none
176
+
177
+ #similar_listings - This method performs a search and returns an array for YouGotListed::Listing objects based on the following criteria
178
+
179
+ :min_rent => self.price.to_i * 0.9,
180
+ :max_rent => self.price.to_i * 1.1,
181
+ :min_bed => ((self.bedrooms.to_i - 1) <= 0 ? 0 : (self.bedrooms.to_i - 1)),
182
+ :max_bed => self.bedrooms.to_i + 1,
183
+ :baths => [(self.baths.to_i - 1), self.baths, (self.baths.to_i - 1)].join(','),
184
+ :city_neighborhood => self.city_neighborhood
185
+
186
+ You can also pass a limit to the similar_listings method, the default is 6 properties (max) returned.
187
+
188
+ @property.similar_listings(10)
189
+
190
+ ** The method actually search for limit + 1 listings and returns the first 6 (there is no way to exclude an ID from the search so we need to pull limit + 1 in case the listing that you are on is returned so we can filter that out and still provide the number of listings you requested).
191
+
192
+
193
+ == Extending YouGotListed::Listing
194
+ Another benefit to this YouGotListed::Listing class is that in your app you can open the class again and add more methods if you wish. For example create a file: RAILS_ROOT/lib/ygl_extensions.rb with the following:
195
+
196
+ module YouGotListed
197
+ class Listing
198
+
199
+ def address
200
+ addr = ""
201
+ addr = "#{street_number} " unless street_number.blank?
202
+ addr += "#{street_name}" unless street_name.blank?
203
+ addr += ", Unit #{unit}" unless unit.blank? || unit == "NULL"
204
+ addr
205
+ end
206
+
207
+ end
208
+ end
209
+
210
+ and add require 'ygl_extensions' to your config/environment.rb file.
211
+
212
+
213
+ == Note on Patches/Pull Requests
214
+
215
+ * Fork the project.
216
+ * Make your feature addition or bug fix.
217
+ * Add tests for it. This is important so I don't break it in a
218
+ future version unintentionally.
219
+ * Commit, do not mess with rakefile, version, or history.
220
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
221
+ * Send me a pull request. Bonus points for topic branches.
222
+
223
+
224
+ == Copyright
225
+
226
+ Copyright (c) 2010-2011 Tom Cocca. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :test => :spec
8
+ task :default => :spec
9
+
10
+ require 'rake/rdoctask'
11
+ Rake::RDocTask.new do |rdoc|
12
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
13
+
14
+ rdoc.rdoc_dir = 'rdoc'
15
+ rdoc.title = "you_got_listed #{version}"
16
+ rdoc.rdoc_files.include('README*')
17
+ rdoc.rdoc_files.include('lib/**/*.rb')
18
+ end
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'httparty'
3
+ require 'hashie'
4
+ require 'rash'
5
+ require 'will_paginate'
6
+
7
+ require 'you_got_listed/client'
8
+ require 'you_got_listed/resource'
9
+ require 'you_got_listed/response'
10
+ require 'you_got_listed/error'
11
+ require 'you_got_listed/accounts'
12
+ require 'you_got_listed/agent'
13
+ require 'you_got_listed/listings'
14
+ require 'you_got_listed/listing'
15
+ require 'you_got_listed/complexes'
16
+ require 'you_got_listed/complex'
17
+ require 'you_got_listed/lead'
@@ -0,0 +1,9 @@
1
+ module YouGotListed
2
+ class Accounts < Resource
3
+
4
+ def search(optional_params = {})
5
+ process_get("/accounts/search.php", optional_params)
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ module YouGotListed
2
+ class Agent < Resource
3
+
4
+ def find_all
5
+ process_get("/agents/search.php")
6
+ end
7
+
8
+ def find_by_id(agent_id)
9
+ get_agent(agent_id)
10
+ end
11
+
12
+ def find(agent_id)
13
+ get_agent(agent_id, true)
14
+ end
15
+
16
+ private
17
+
18
+ def get_agent(agent_id, raise_error = false)
19
+ params = {:id => agent_id}
20
+ process_get("/agents/search.php", params, raise_error)
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ module YouGotListed
2
+ class Client
3
+
4
+ include HTTParty
5
+ format :xml
6
+ base_uri "https://yougotlistings.com/api"
7
+
8
+ attr_accessor :api_key
9
+
10
+ def initialize(api_key)
11
+ self.class.default_params :key => api_key
12
+ end
13
+
14
+ def perform_request(http_method, path, params = {})
15
+ begin
16
+ self.class.send(http_method, path, :query => params)
17
+ rescue Timeout::Error
18
+ {"YGLResponse" => {"responseCode" => "996", "Error" => "Timeout"}}
19
+ rescue Exception
20
+ {"YGLResponse" => {"responseCode" => "999", "Error" => "Unknown Error"}}
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ module YouGotListed
2
+ class Complex
3
+
4
+ attr_accessor :client
5
+
6
+ def initialize(listing, client)
7
+ listing.each do |key, value|
8
+ self.instance_variable_set('@'+key, value)
9
+ self.class.send(:define_method, key, proc{self.instance_variable_get("@#{key}")})
10
+ end
11
+ self.client = client
12
+ end
13
+
14
+ def properties
15
+ return [] if self.listings.blank?
16
+ props = []
17
+ if self.listings.listing.is_a?(Array)
18
+ self.listings.listing.each do |listing|
19
+ listing = listing.merge(find_address(listing.address_id))
20
+ props << YouGotListed::Listing.new(listing, self.client)
21
+ end
22
+ else
23
+ listing = self.listings.listing.merge(find_address(listing.address_id))
24
+ props << YouGotListed::Listing.new(listing, self.client)
25
+ end
26
+ props
27
+ end
28
+
29
+ def pictures
30
+ self.photos.photo unless self.photos.blank? || self.photos.photo.blank?
31
+ end
32
+
33
+ def find_address(address_id)
34
+ if addresses.address.is_a?(Array)
35
+ addresses.address.find{|address| address.id == address_id}
36
+ else
37
+ addresses.address
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,53 @@
1
+ module YouGotListed
2
+ class Complexes < Resource
3
+
4
+ def search(params = {})
5
+ params[:page_count] ||= 20
6
+ params[:page_index] ||= 1
7
+ params[:sort_name] ||= "Name"
8
+ params[:sort_dir] ||= "asc"
9
+ params[:detail_level] ||= 2
10
+ SearchResponse.new(self.client.perform_request(:get, '/complexes/search.php', params), self.client, params[:page_count])
11
+ end
12
+
13
+ def find_by_id(complex_id)
14
+ response = SearchResponse.new(self.client.perform_request(:get, '/complexes/search.php', {:complex_id => complex_id}), self.client, 20)
15
+ (response.success? && response.property_complexes.size > 0) ? response.property_complexes.first : nil
16
+ end
17
+
18
+ class SearchResponse < YouGotListed::Response
19
+
20
+ attr_accessor :limit, :paginator_cache, :client
21
+
22
+ def initialize(response, client, limit = 20, raise_error = false)
23
+ super(response, raise_error)
24
+ self.limit = limit
25
+ self.client = client
26
+ end
27
+
28
+ def property_complexes
29
+ return [] if self.ygl_response.complexes.blank?
30
+ props = []
31
+ if self.ygl_response.complexes.complex.is_a?(Array)
32
+ self.ygl_response.complexes.complex.each do |complex|
33
+ props << YouGotListed::Complex.new(complex, self.client)
34
+ end
35
+ else
36
+ props << YouGotListed::Complex.new(self.ygl_response.complexes.complex, self.client)
37
+ end
38
+ props
39
+ end
40
+
41
+ def paginator
42
+ paginator_cache if paginator_cache
43
+ self.paginator_cache = WillPaginate::Collection.create(
44
+ (self.ygl_response.page_index ? self.ygl_response.page_index : 1),
45
+ self.limit,
46
+ (self.ygl_response.total ? self.ygl_response.total : properties.size)) do |pager|
47
+ pager.replace property_complexes
48
+ end
49
+ end
50
+ end
51
+
52
+ end
53
+ end