mls 0.14.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Rakefile +6 -6
  4. data/lib/mls.rb +73 -427
  5. data/lib/mls/account.rb +15 -0
  6. data/lib/mls/address.rb +4 -0
  7. data/lib/mls/agency.rb +6 -0
  8. data/lib/mls/brokerage.rb +7 -0
  9. data/lib/mls/coworking_space.rb +5 -0
  10. data/lib/mls/floorplan.rb +11 -0
  11. data/lib/mls/flyer.rb +11 -0
  12. data/lib/mls/lease.rb +2 -0
  13. data/lib/mls/listing.rb +135 -0
  14. data/lib/mls/locality.rb +6 -0
  15. data/lib/mls/photo.rb +18 -0
  16. data/lib/mls/property.rb +24 -0
  17. data/lib/mls/region.rb +13 -0
  18. data/lib/mls/sale.rb +2 -0
  19. data/lib/mls/space.rb +3 -0
  20. data/lib/mls/sublease.rb +2 -0
  21. data/lib/mls/use.rb +25 -0
  22. data/mls.gemspec +11 -15
  23. data/test/mls/attribute_test.rb +55 -0
  24. data/test/mls/resource_test.rb +31 -0
  25. data/test/mls_test.rb +27 -0
  26. data/test/test_helper.rb +26 -27
  27. metadata +43 -75
  28. data/lib/mls/attribute.rb +0 -44
  29. data/lib/mls/attributes/array.rb +0 -11
  30. data/lib/mls/attributes/boolean.rb +0 -9
  31. data/lib/mls/attributes/datetime.rb +0 -25
  32. data/lib/mls/attributes/decimal.rb +0 -21
  33. data/lib/mls/attributes/fixnum.rb +0 -16
  34. data/lib/mls/attributes/hash.rb +0 -15
  35. data/lib/mls/attributes/string.rb +0 -15
  36. data/lib/mls/errors.rb +0 -29
  37. data/lib/mls/factories/account.rb +0 -34
  38. data/lib/mls/factories/address.rb +0 -16
  39. data/lib/mls/factories/listing.rb +0 -29
  40. data/lib/mls/factories_helper.rb +0 -7
  41. data/lib/mls/model.rb +0 -125
  42. data/lib/mls/models/account.rb +0 -174
  43. data/lib/mls/models/address.rb +0 -58
  44. data/lib/mls/models/brokerage.rb +0 -31
  45. data/lib/mls/models/floorplan.rb +0 -45
  46. data/lib/mls/models/flyer.rb +0 -45
  47. data/lib/mls/models/listing.rb +0 -394
  48. data/lib/mls/models/pdf.rb +0 -15
  49. data/lib/mls/models/photo.rb +0 -40
  50. data/lib/mls/models/property.rb +0 -114
  51. data/lib/mls/models/region.rb +0 -56
  52. data/lib/mls/models/tour.rb +0 -54
  53. data/lib/mls/models/video.rb +0 -18
  54. data/lib/mls/parser.rb +0 -74
  55. data/lib/mls/resource.rb +0 -99
  56. data/lib/rdoc/generator/template/42floors/_context.rhtml +0 -209
  57. data/lib/rdoc/generator/template/42floors/_head.rhtml +0 -7
  58. data/lib/rdoc/generator/template/42floors/class.rhtml +0 -39
  59. data/lib/rdoc/generator/template/42floors/file.rhtml +0 -35
  60. data/lib/rdoc/generator/template/42floors/index.rhtml +0 -13
  61. data/lib/rdoc/generator/template/42floors/resources/apple-touch-icon.png +0 -0
  62. data/lib/rdoc/generator/template/42floors/resources/css/github.css +0 -129
  63. data/lib/rdoc/generator/template/42floors/resources/css/main.css +0 -339
  64. data/lib/rdoc/generator/template/42floors/resources/css/panel.css +0 -389
  65. data/lib/rdoc/generator/template/42floors/resources/css/reset.css +0 -48
  66. data/lib/rdoc/generator/template/42floors/resources/favicon.ico +0 -0
  67. data/lib/rdoc/generator/template/42floors/resources/i/arrows.png +0 -0
  68. data/lib/rdoc/generator/template/42floors/resources/i/results_bg.png +0 -0
  69. data/lib/rdoc/generator/template/42floors/resources/i/tree_bg.png +0 -0
  70. data/lib/rdoc/generator/template/42floors/resources/js/highlight.pack.js +0 -1
  71. data/lib/rdoc/generator/template/42floors/resources/js/jquery-1.3.2.min.js +0 -19
  72. data/lib/rdoc/generator/template/42floors/resources/js/jquery-effect.js +0 -593
  73. data/lib/rdoc/generator/template/42floors/resources/js/main.js +0 -20
  74. data/lib/rdoc/generator/template/42floors/resources/js/searchdoc.js +0 -442
  75. data/lib/rdoc/generator/template/42floors/resources/panel/index.html +0 -73
  76. data/lib/rdoc/generator/template/42floors/se_index.rhtml +0 -8
  77. data/test/units/properties/test_boolean.rb +0 -4
  78. data/test/units/test_errors.rb +0 -32
  79. data/test/units/test_mls.rb +0 -294
  80. data/test/units/test_property.rb +0 -35
@@ -1,58 +0,0 @@
1
- class MLS::Address < MLS::Resource
2
-
3
- attribute :id, Fixnum, :serialize => :false
4
- attribute :property_id, Fixnum, :serialize => :false
5
- attribute :slug, String, :serialize => :false
6
-
7
- attribute :formatted_address, String
8
- attribute :street_number, String
9
- attribute :street, String
10
- attribute :neighborhood, String
11
- attribute :city, String
12
- attribute :county, String
13
- attribute :state, String
14
- attribute :country, String
15
- attribute :postal_code, String
16
-
17
- attr_accessor :property
18
-
19
- def save
20
- MLS.put("/addresses/#{id}", {:address => to_hash}, 400) do |response, code|
21
- if code == 200 || code == 400
22
- MLS::Address::Parser.update(self, response.body)
23
- code == 200
24
- else
25
- raise MLS::Exception::UnexpectedResponse, code
26
- end
27
- end
28
- end
29
-
30
- def to_param
31
- slug
32
- end
33
-
34
- class << self
35
-
36
- def find(id)
37
- response = MLS.get("/addresses/#{id}")
38
- MLS::Address::Parser.parse(response.body)
39
- end
40
-
41
- # currently supported options are :include, :where, :limit, :offset
42
- def all(options={})
43
- response = MLS.get('/addresses', options)
44
- MLS::Address::Parser.parse_collection(response.body)
45
- end
46
-
47
- end
48
-
49
- end
50
-
51
-
52
- class MLS::Address::Parser < MLS::Parser
53
-
54
- def property=(property)
55
- @object.property = MLS::Property::Parser.build(property)
56
- end
57
-
58
- end
@@ -1,31 +0,0 @@
1
- class MLS::Brokerage < MLS::Resource
2
-
3
- attr_accessor :avatar
4
-
5
- attribute :id, Fixnum, :serialize => :if_present
6
- attribute :name, String, :serialize => :if_present
7
- attribute :admin_id, Fixnum, :serialize => :if_present
8
- attribute :slug, String, :serialize => false
9
- attribute :palette, Hash, :serialize => :if_present
10
-
11
- class << self
12
-
13
- def find(id)
14
- response = MLS.get("/brokerages/#{id}")
15
- MLS::Brokerage::Parser.parse(response.body)
16
- end
17
-
18
- def all(options={})
19
- response = MLS.get('/brokerages', options)
20
- MLS::Brokerage::Parser.parse_collection(response.body)
21
- end
22
-
23
- end
24
-
25
- end
26
-
27
- class MLS::Brokerage::Parser < MLS::Parser
28
- def avatar=(avatar)
29
- @object.avatar = MLS::Photo::Parser.build(avatar)
30
- end
31
- end
@@ -1,45 +0,0 @@
1
- require 'restclient'
2
-
3
- class MLS::Floorplan < MLS::Resource
4
-
5
- attribute :id, Fixnum
6
- attribute :digest, String
7
- attribute :avatar_digest, String
8
- attribute :file_name, String
9
- attribute :file_size, Fixnum
10
-
11
- def url(protocol='http')
12
- "#{protocol}://#{MLS.asset_host}/floorplans/#{digest}/compressed/#{file_name}"
13
- end
14
-
15
- def avatar(size='150x100#', protocol='http')
16
- "#{protocol}://#{MLS.image_host}/#{avatar_digest}.jpg?s=#{URI.escape(size)}"
17
- end
18
-
19
- def self.create(attrs)
20
- attrs[:file].rewind
21
- url = MLS.url.dup
22
- url.user = nil
23
- url.path = "/floorplans"
24
-
25
- if attrs[:subject]
26
- attrs[:subject_id] = attrs[:subject].id
27
- attrs[:subject_type] = attrs[:subject].class.name.split("::").last
28
- attrs.delete(:subject)
29
- end
30
- response = RestClient.post(url.to_s, {:floorplan => attrs}, MLS.headers)
31
- attrs[:file].close unless attrs[:file].closed?
32
-
33
- MLS::Floorplan::Parser.parse(response.body)
34
- end
35
-
36
- def self.find(id)
37
- response = MLS.get("/floorplans/#{id}")
38
- MLS::Floorplan::Parser.parse(response.body)
39
- end
40
-
41
- end
42
-
43
- class MLS::Floorplan::Parser < MLS::Parser
44
-
45
- end
@@ -1,45 +0,0 @@
1
- require 'restclient'
2
-
3
- class MLS::Flyer < MLS::Resource
4
-
5
- attribute :id, Fixnum
6
- attribute :digest, String
7
- attribute :avatar_digest, String
8
- attribute :file_name, String
9
- attribute :file_size, Fixnum
10
-
11
- def url(protocol='http')
12
- "#{protocol}://#{MLS.asset_host}/flyers/#{digest}/compressed/#{file_name}"
13
- end
14
-
15
- def avatar(size='150x100#', protocol='http')
16
- "#{protocol}://#{MLS.image_host}/#{avatar_digest}.jpg?s=#{URI.escape(size)}"
17
- end
18
-
19
- def self.create(attrs)
20
- attrs[:file].rewind
21
- url = MLS.url.dup
22
- url.user = nil
23
- url.path = "/flyers"
24
-
25
- if attrs[:subject]
26
- attrs[:subject_id] = attrs[:subject].id
27
- attrs[:subject_type] = attrs[:subject].class.name.split("::").last
28
- attrs.delete(:subject)
29
- end
30
- response = RestClient.post(url.to_s, {:flyer => attrs}, MLS.headers)
31
- attrs[:file].close unless attrs[:file].closed?
32
-
33
- MLS::Flyer::Parser.parse(response.body)
34
- end
35
-
36
- def self.find(id)
37
- response = MLS.get("/flyers/#{id}")
38
- MLS::Flyer::Parser.parse(response.body)
39
- end
40
-
41
- end
42
-
43
- class MLS::Flyer::Parser < MLS::Parser
44
-
45
- end
@@ -1,394 +0,0 @@
1
- class MLS::Listing < MLS::Resource
2
-
3
- WORKFLOW_STATES = %w(visible processing invisible expired)
4
- LEASE_STATES = %w(listed leased)
5
- TYPES = %w(lease sublease coworking_space)
6
- SPACE_TYPES = %w(unit floor building)
7
- LEASE_TERMS = ['Full Service', 'NNN', 'Modified Gross']
8
- RATE_UNITS = ['/sqft/yr', '/sqft/mo', '/mo', '/yr', '/desk/mo']
9
- USES = ["Office", "Creative", "Loft", "Medical Office", "Flex Space", "R&D", "Office Showroom", "Industrial", "Retail"]
10
- SOURCE_TYPES = %w(website flyer)
11
- CHANNELS = %w(excavator mls staircase broker_dashboard)
12
-
13
- attribute :id, Fixnum, :serialize => :false
14
- attribute :address_id, Fixnum, :serialize => :false
15
- attribute :slug, String, :serialize => :false
16
- attribute :use, String, :serialize => :if_present
17
- attribute :account_id, Fixnum
18
- attribute :private, Boolean, :default => false, :serialize => false
19
- attribute :source, String
20
- attribute :source_url, String
21
- attribute :source_type, String, :serialize => :if_present
22
- attribute :channel, String, :serialize => :if_present
23
- attribute :photo_ids, Array, :serialize => :if_present
24
-
25
- attribute :name, String
26
- attribute :type, String, :default => 'lease'
27
- attribute :workflow_state, String, :default => 'visible'
28
- attribute :lease_state, String, :default => 'listed'
29
- attribute :space_type, String, :default => 'unit'
30
- attribute :unit, String
31
- attribute :floor, Fixnum
32
- attribute :description, String
33
-
34
- attribute :size, Fixnum
35
- attribute :maximum_contiguous_size, Fixnum
36
- attribute :minimum_divisible_size, Fixnum
37
-
38
- attribute :amenities, Hash
39
- attribute :lease_terms, String
40
- attribute :rate, Decimal
41
- attribute :rate_units, String, :default => '/sqft/mo'
42
- attribute :low_rate, Decimal, :serialize => :false
43
- attribute :high_rate, Decimal, :serialize => :false
44
- attribute :sublease_expiration, DateTime
45
-
46
- attribute :forecast_rate_per_year, Decimal, :serialize => :false
47
- attribute :forecast_rate_per_month, Decimal, :serialize => :false
48
- attribute :forecast_rate_per_sqft_per_month, Decimal, :serialize => :false
49
- attribute :forecast_rate_per_sqft_per_year, Decimal, :serialize => :false
50
-
51
- attribute :available_on, DateTime
52
- attribute :term, Fixnum
53
- attribute :term_units, String, :default => 'years'
54
-
55
- attribute :weekday_hours, String
56
- attribute :saturday_hours, String
57
- attribute :sunday_hours, String
58
-
59
- attribute :offices, Fixnum
60
- attribute :desks, Fixnum
61
- attribute :conference_rooms, Fixnum
62
- attribute :bathrooms, Fixnum
63
-
64
- attribute :kitchen, Boolean
65
- attribute :showers, Boolean
66
- attribute :patio, Boolean
67
- attribute :reception_area, Boolean
68
- attribute :ready_to_move_in, Boolean
69
- attribute :furniture_available, Boolean
70
- attribute :natural_light, Boolean
71
- attribute :high_ceilings, Boolean
72
-
73
- attribute :created_at, DateTime, :serialize => :false
74
- attribute :updated_at, DateTime, :serialize => :false
75
- attribute :touched_at, DateTime, :serialize => :false
76
- attribute :leased_on, DateTime
77
- attribute :photography_requested_on, DateTime, :serialize => :false
78
-
79
- attribute :awesome_score, Fixnum
80
- attribute :awesome_needs, Array, :serialize => :if_present
81
- attribute :awesome_label, String
82
-
83
- attribute :flyer_id, Fixnum, :serialize => :if_present
84
- attribute :floorplan_id, Fixnum, :serialize => :if_present
85
-
86
- attribute :avatar_digest, String, :serialize => false
87
-
88
- # Counter Caches
89
- attribute :photos_count, Fixnum, :serialize => :false
90
-
91
- attr_accessor :property, :address, :agents, :account, :photos, :flyer, :floorplan, :videos, :similar_photos, :spaces, :primary_agent
92
-
93
- def avatar(size='150x100#', protocol='http')
94
- if avatar_digest
95
- "#{protocol}://#{MLS.image_host}/#{avatar_digest}.jpg?s=#{URI.escape(size)}"
96
- else
97
- address.avatar(size, protocol)
98
- end
99
- end
100
-
101
- def processing?
102
- workflow_state == 'processing'
103
- end
104
-
105
- def leased?
106
- lease_state == 'leased'
107
- end
108
-
109
- def active?
110
- lease_state == 'listed' && workflow_state == 'visible'
111
- end
112
-
113
- def inactive?
114
- !self.active?
115
- end
116
-
117
- def lease?
118
- type == 'lease'
119
- end
120
-
121
- def sublease?
122
- type == 'sublease'
123
- end
124
-
125
- def coworking?
126
- type == 'coworking_space'
127
- end
128
-
129
-
130
- def space_name
131
- return name if !name.nil?
132
-
133
- case space_type
134
- when 'unit'
135
- if unit
136
- "Unit #{unit}"
137
- elsif floor
138
- "#{floor.ordinalize} Floor"
139
- else
140
- "Unit Lease"
141
- end
142
- when 'building'
143
- "Entire Building"
144
- when 'floor'
145
- if floor
146
- "#{floor.ordinalize} Floor"
147
- elsif unit
148
- "Unit #{unit}"
149
- else
150
- "Floor Lease"
151
- end
152
- end
153
- end
154
-
155
- def rate?
156
- !!@rate
157
- end
158
-
159
- # TODO: remove /desk/mo conversions
160
- def rate(units=nil)
161
- return nil if !@rate
162
- units ||= rate_units
163
-
164
- price = if rate_units == '/sqft/mo'
165
- if units == '/sqft/mo'
166
- @rate
167
- elsif units == '/sqft/yr'
168
- @rate * 12.0
169
- elsif units == '/mo'
170
- @rate * @size
171
- elsif units == '/yr'
172
- @rate * @size * 12.0
173
- elsif units == '/desk/mo'
174
- @rate * 200.0
175
- else
176
- raise "Invalid rate conversion (#{rate_units} => #{units})"
177
- end
178
-
179
- elsif rate_units == '/sqft/yr'
180
- if units == '/sqft/mo'
181
- @rate / 12.0
182
- elsif units == '/sqft/yr'
183
- @rate
184
- elsif units == '/mo'
185
- (@rate * @size) / 12.0
186
- elsif units == '/yr'
187
- @rate * @size
188
- elsif units == '/desk/mo'
189
- (@rate / 12.0) * 200.0
190
- else
191
- raise "Invalid rate conversion (#{rate_units} => #{units})"
192
- end
193
-
194
- elsif rate_units == '/mo'
195
- if units == '/sqft/mo'
196
- @rate / @size.to_f
197
- elsif units == '/sqft/yr'
198
- (@rate * 12) / @size.to_f
199
- elsif units == '/mo'
200
- @rate
201
- elsif units == '/yr'
202
- @rate * 12
203
- elsif units == '/desk/mo'
204
- (@rate / @size.to_f) * 200.0
205
- else
206
- raise "Invalid rate conversion (#{rate_units} => #{units})"
207
- end
208
-
209
- elsif rate_units == '/yr'
210
- if units == '/sqft/mo'
211
- (@rate / 12.0) / @size.to_f
212
- elsif units == '/sqft/yr'
213
- @rate / @size.to_f
214
- elsif units == '/mo'
215
- @rate / 12.0
216
- elsif units == '/yr'
217
- @rate
218
- elsif units == '/desk/mo'
219
- ((@rate / 12.0) / @size.to_f) * 200.0
220
- else
221
- raise "Invalid rate conversion (#{rate_units} => #{units})"
222
- end
223
-
224
- elsif rate_units == '/desk/mo'
225
- if units == '/sqft/mo'
226
- @rate / 200.0
227
- elsif units == '/sqft/yr'
228
- (@rate * 12) / 200.0
229
- elsif units == '/mo'
230
- @rate
231
- elsif units == '/yr'
232
- @rate * 12
233
- elsif units == '/desk/mo'
234
- @rate
235
- else
236
- raise "Invalid rate conversion (#{rate_units} => #{units})"
237
- end
238
-
239
- end
240
-
241
- price.round(2)
242
- end
243
-
244
- # Creates a tour request for the listing.
245
- #
246
- # Paramaters::
247
- #
248
- # * +account+ - A +Hash+ of the user account. Valid keys are:
249
- # * +:name+ - Name of the User requesting the tour (Required)
250
- # * +:email+ - Email of the User requesting the tour (Required)
251
- # * +:phone+ - Phone of the User requesting the tour
252
- # * +info+ - A optional +Hash+ of *company* info. Valid keys are:
253
- # * +:message+ - Overrides the default message on the email sent to the broker
254
- # * +:company+ - The name of the company that is interested in the space
255
- # * +:population+ - The current number of employees at the company
256
- # * +:growing+ - A boolean of weather or not the company is expecting to grow
257
- #
258
- # Examples:
259
- #
260
- # #!ruby
261
- # listing = MLS::Listing.find(@id)
262
- # info => {:company => 'name', :population => 10, :funding => 'string', :move_id => '2012-09-12'}
263
- # listing.request_tour('name', 'email@address.com', info) # => #<MLS::Tour>
264
- #
265
- # listing.request_tour('', 'emai', info) # => #<MLS::Tour> will have errors on account
266
- def request_tour(account, tour={})
267
- MLS::Tour.create(id, account, tour)
268
- end
269
-
270
-
271
- def create
272
- MLS.post('/listings', {:listing => to_hash}, 201, 400) do |response, code|
273
- raise MLS::Exception::UnexpectedResponse if ![201, 400].include?(code)
274
- MLS::Listing::Parser.update(self, response.body)
275
- end
276
- end
277
-
278
- def save
279
- return create unless id
280
- MLS.put("/listings/#{id}", {:listing => to_hash}, 400) do |response, code|
281
- if code == 200 || code == 400
282
- MLS::Listing::Parser.update(self, response.body)
283
- code == 200
284
- else
285
- raise MLS::Exception::UnexpectedResponse, code
286
- end
287
- end
288
- end
289
-
290
- def to_hash
291
- hash = super
292
- hash[:address_attributes] = address.to_hash if address
293
- hash[:spaces_attributes] = spaces.inject([]) { |acc, x| acc << x.to_hash; acc } if spaces
294
- hash[:agents_attributes] = agents.inject([]) { |acc, x| acc << x.to_hash; acc } if agents
295
- hash[:photo_ids] = photos.map(&:id) if photos
296
- hash[:videos_attributes] = videos.map(&:to_hash) unless videos.blank?
297
- hash
298
- end
299
-
300
- def to_param
301
- "#{address.to_param}/#{id}"
302
- end
303
-
304
- def import #TODO test me
305
- result = :failure
306
- MLS.post('/import', {:listing => to_hash}, 400) do |response, code|
307
- case code
308
- when 200
309
- result = :duplicate
310
- when 201
311
- result = :created
312
- when 202
313
- result = :updated
314
- when 400
315
- result = :failure
316
- else
317
- raise MLS::Exception::UnexpectedResponse, code
318
- end
319
- MLS::Listing::Parser.update(self, response.body)
320
- end
321
- result
322
- end
323
-
324
- def similar
325
- [] # Similar Listings not supported for now
326
- end
327
-
328
- class << self
329
-
330
- def find(id)
331
- response = MLS.get("/listings/#{id}")
332
- MLS::Listing::Parser.parse(response.body)
333
- end
334
-
335
- # currently supported options are filters, page, per_page, offset, order
336
- def all(options={})
337
- response = MLS.get('/listings', options)
338
- MLS::Listing::Parser.parse_collection(response.body)
339
- end
340
-
341
- def import(attrs)
342
- model = self.new(attrs)
343
- {:status => model.import, :model => model}
344
- end
345
-
346
- def amenities
347
- @amenities ||= Yajl::Parser.new.parse(MLS.get('/listings/amenities').body).map(&:to_sym)
348
- end
349
-
350
- end
351
-
352
- end
353
-
354
-
355
- class MLS::Listing::Parser < MLS::Parser
356
-
357
- def photos=(photos)
358
- @object.photos = photos.map {|p| MLS::Photo::Parser.build(p)}
359
- end
360
-
361
- def similar_photos=(photos)
362
- @object.similar_photos = photos.map { |p| MLS::Photo::Parser.build(p) }
363
- end
364
-
365
- def videos=(videos)
366
- @object.videos = videos.map do |video|
367
- MLS::Video::Parser.build(video)
368
- end
369
- end
370
-
371
- def floorplan=(floorplan)
372
- @object.floorplan = MLS::Floorplan::Parser.build(floorplan)
373
- end
374
-
375
- def flyer=(flyer)
376
- @object.flyer = MLS::Flyer::Parser.build(flyer)
377
- end
378
-
379
- def address=(address)
380
- @object.address = MLS::Address::Parser.build(address)
381
- end
382
-
383
- def property=(property)
384
- @object.property = MLS::Property::Parser.build(property)
385
- end
386
-
387
- def agents=(agents)
388
- @object.agents = agents.map {|a| MLS::Account::Parser.build(a) }
389
- end
390
-
391
- def primary_agent=(agent)
392
- @object.primary_agent = MLS::Account::Parser.build(agent)
393
- end
394
- end