spark_api 1.0.0

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.
Files changed (257) hide show
  1. data/History.txt +139 -0
  2. data/LICENSE +14 -0
  3. data/README.md +153 -0
  4. data/Rakefile +18 -0
  5. data/VERSION +1 -0
  6. data/bin/spark_api +8 -0
  7. data/bin/spark_api~ +8 -0
  8. data/lib/spark_api.rb +46 -0
  9. data/lib/spark_api/authentication.rb +55 -0
  10. data/lib/spark_api/authentication/api_auth.rb +104 -0
  11. data/lib/spark_api/authentication/api_auth.rb~ +104 -0
  12. data/lib/spark_api/authentication/base_auth.rb +47 -0
  13. data/lib/spark_api/authentication/base_auth.rb~ +47 -0
  14. data/lib/spark_api/authentication/oauth2.rb +198 -0
  15. data/lib/spark_api/authentication/oauth2.rb~ +199 -0
  16. data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb +87 -0
  17. data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb~ +87 -0
  18. data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb +48 -0
  19. data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb~ +49 -0
  20. data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb +44 -0
  21. data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb~ +45 -0
  22. data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb +35 -0
  23. data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb~ +36 -0
  24. data/lib/spark_api/authentication/oauth2_impl/middleware.rb +38 -0
  25. data/lib/spark_api/authentication/oauth2_impl/middleware.rb~ +39 -0
  26. data/lib/spark_api/authentication/oauth2_impl/password_provider.rb +24 -0
  27. data/lib/spark_api/authentication/oauth2_impl/password_provider.rb~ +25 -0
  28. data/lib/spark_api/cli.rb +158 -0
  29. data/lib/spark_api/cli.rb~ +158 -0
  30. data/lib/spark_api/cli/api_auth.rb +8 -0
  31. data/lib/spark_api/cli/api_auth.rb~ +8 -0
  32. data/lib/spark_api/cli/oauth2.rb +14 -0
  33. data/lib/spark_api/cli/oauth2.rb~ +14 -0
  34. data/lib/spark_api/cli/setup.rb +47 -0
  35. data/lib/spark_api/cli/setup.rb~ +47 -0
  36. data/lib/spark_api/client.rb +27 -0
  37. data/lib/spark_api/configuration.rb +54 -0
  38. data/lib/spark_api/configuration.rb~ +54 -0
  39. data/lib/spark_api/configuration/yaml.rb +101 -0
  40. data/lib/spark_api/configuration/yaml.rb~ +101 -0
  41. data/lib/spark_api/connection.rb +42 -0
  42. data/lib/spark_api/faraday.rb +64 -0
  43. data/lib/spark_api/faraday.rb~ +64 -0
  44. data/lib/spark_api/models.rb +33 -0
  45. data/lib/spark_api/models.rb~ +33 -0
  46. data/lib/spark_api/models/account.rb +115 -0
  47. data/lib/spark_api/models/account.rb~ +115 -0
  48. data/lib/spark_api/models/base.rb +118 -0
  49. data/lib/spark_api/models/base.rb~ +118 -0
  50. data/lib/spark_api/models/connect_prefs.rb +10 -0
  51. data/lib/spark_api/models/connect_prefs.rb~ +10 -0
  52. data/lib/spark_api/models/constraint.rb +16 -0
  53. data/lib/spark_api/models/constraint.rb~ +16 -0
  54. data/lib/spark_api/models/contact.rb +49 -0
  55. data/lib/spark_api/models/contact.rb~ +49 -0
  56. data/lib/spark_api/models/custom_fields.rb +12 -0
  57. data/lib/spark_api/models/custom_fields.rb~ +12 -0
  58. data/lib/spark_api/models/document.rb +11 -0
  59. data/lib/spark_api/models/document.rb~ +11 -0
  60. data/lib/spark_api/models/finders.rb +45 -0
  61. data/lib/spark_api/models/finders.rb~ +45 -0
  62. data/lib/spark_api/models/idx_link.rb +47 -0
  63. data/lib/spark_api/models/idx_link.rb~ +47 -0
  64. data/lib/spark_api/models/listing.rb +197 -0
  65. data/lib/spark_api/models/listing.rb~ +197 -0
  66. data/lib/spark_api/models/listing_cart.rb +72 -0
  67. data/lib/spark_api/models/listing_cart.rb~ +72 -0
  68. data/lib/spark_api/models/market_statistics.rb +33 -0
  69. data/lib/spark_api/models/market_statistics.rb~ +33 -0
  70. data/lib/spark_api/models/message.rb +21 -0
  71. data/lib/spark_api/models/message.rb~ +21 -0
  72. data/lib/spark_api/models/note.rb +41 -0
  73. data/lib/spark_api/models/note.rb~ +41 -0
  74. data/lib/spark_api/models/notification.rb +42 -0
  75. data/lib/spark_api/models/notification.rb~ +42 -0
  76. data/lib/spark_api/models/open_house.rb +24 -0
  77. data/lib/spark_api/models/open_house.rb~ +24 -0
  78. data/lib/spark_api/models/photo.rb +70 -0
  79. data/lib/spark_api/models/photo.rb~ +70 -0
  80. data/lib/spark_api/models/property_types.rb +7 -0
  81. data/lib/spark_api/models/property_types.rb~ +7 -0
  82. data/lib/spark_api/models/saved_search.rb +16 -0
  83. data/lib/spark_api/models/saved_search.rb~ +16 -0
  84. data/lib/spark_api/models/shared_listing.rb +35 -0
  85. data/lib/spark_api/models/shared_listing.rb~ +35 -0
  86. data/lib/spark_api/models/standard_fields.rb +50 -0
  87. data/lib/spark_api/models/standard_fields.rb~ +50 -0
  88. data/lib/spark_api/models/subresource.rb +19 -0
  89. data/lib/spark_api/models/subresource.rb~ +19 -0
  90. data/lib/spark_api/models/system_info.rb +14 -0
  91. data/lib/spark_api/models/system_info.rb~ +14 -0
  92. data/lib/spark_api/models/tour_of_home.rb +24 -0
  93. data/lib/spark_api/models/tour_of_home.rb~ +24 -0
  94. data/lib/spark_api/models/video.rb +16 -0
  95. data/lib/spark_api/models/video.rb~ +16 -0
  96. data/lib/spark_api/models/virtual_tour.rb +18 -0
  97. data/lib/spark_api/models/virtual_tour.rb~ +18 -0
  98. data/lib/spark_api/multi_client.rb +59 -0
  99. data/lib/spark_api/multi_client.rb~ +59 -0
  100. data/lib/spark_api/paginate.rb +109 -0
  101. data/lib/spark_api/paginate.rb~ +109 -0
  102. data/lib/spark_api/primary_array.rb +29 -0
  103. data/lib/spark_api/primary_array.rb~ +29 -0
  104. data/lib/spark_api/request.rb +96 -0
  105. data/lib/spark_api/request.rb~ +96 -0
  106. data/lib/spark_api/response.rb +70 -0
  107. data/lib/spark_api/response.rb~ +70 -0
  108. data/lib/spark_api/version.rb +4 -0
  109. data/lib/spark_api/version.rb~ +4 -0
  110. data/script/console +6 -0
  111. data/script/console~ +6 -0
  112. data/script/example.rb +27 -0
  113. data/script/example.rb~ +27 -0
  114. data/spec/fixtures/accounts/all.json +160 -0
  115. data/spec/fixtures/accounts/my.json +74 -0
  116. data/spec/fixtures/accounts/my_portal.json +20 -0
  117. data/spec/fixtures/accounts/my_put.json +5 -0
  118. data/spec/fixtures/accounts/my_save.json +5 -0
  119. data/spec/fixtures/accounts/office.json +142 -0
  120. data/spec/fixtures/accounts/password_save.json +6 -0
  121. data/spec/fixtures/authentication_failure.json +7 -0
  122. data/spec/fixtures/base.json +13 -0
  123. data/spec/fixtures/contacts/contacts.json +28 -0
  124. data/spec/fixtures/contacts/my.json +19 -0
  125. data/spec/fixtures/contacts/new.json +11 -0
  126. data/spec/fixtures/contacts/new_empty.json +8 -0
  127. data/spec/fixtures/contacts/new_notify.json +11 -0
  128. data/spec/fixtures/contacts/post.json +10 -0
  129. data/spec/fixtures/contacts/tags.json +11 -0
  130. data/spec/fixtures/count.json +10 -0
  131. data/spec/fixtures/empty.json +3 -0
  132. data/spec/fixtures/errors/expired.json +7 -0
  133. data/spec/fixtures/errors/failure.json +5 -0
  134. data/spec/fixtures/errors/failure_with_constraint.json +17 -0
  135. data/spec/fixtures/errors/failure_with_msg.json +7 -0
  136. data/spec/fixtures/generic_delete.json +1 -0
  137. data/spec/fixtures/generic_failure.json +5 -0
  138. data/spec/fixtures/listing_carts/add_listing.json +13 -0
  139. data/spec/fixtures/listing_carts/add_listing_post.json +5 -0
  140. data/spec/fixtures/listing_carts/empty.json +5 -0
  141. data/spec/fixtures/listing_carts/listing_cart.json +19 -0
  142. data/spec/fixtures/listing_carts/new.json +12 -0
  143. data/spec/fixtures/listing_carts/post.json +10 -0
  144. data/spec/fixtures/listing_carts/remove_listing.json +13 -0
  145. data/spec/fixtures/listings/constraints.json +18 -0
  146. data/spec/fixtures/listings/constraints_with_pagination.json +24 -0
  147. data/spec/fixtures/listings/document_index.json +19 -0
  148. data/spec/fixtures/listings/multiple.json +69 -0
  149. data/spec/fixtures/listings/no_subresources.json +38 -0
  150. data/spec/fixtures/listings/open_houses.json +21 -0
  151. data/spec/fixtures/listings/photos/index.json +469 -0
  152. data/spec/fixtures/listings/photos/new.json +12 -0
  153. data/spec/fixtures/listings/photos/post.json +20 -0
  154. data/spec/fixtures/listings/put.json +5 -0
  155. data/spec/fixtures/listings/put_expiration_date.json +5 -0
  156. data/spec/fixtures/listings/saved_search.json +17 -0
  157. data/spec/fixtures/listings/shared_listing_get.json +14 -0
  158. data/spec/fixtures/listings/shared_listing_new.json +9 -0
  159. data/spec/fixtures/listings/shared_listing_post.json +10 -0
  160. data/spec/fixtures/listings/tour_of_homes.json +23 -0
  161. data/spec/fixtures/listings/videos_index.json +18 -0
  162. data/spec/fixtures/listings/virtual_tours_index.json +42 -0
  163. data/spec/fixtures/listings/with_documents.json +52 -0
  164. data/spec/fixtures/listings/with_permissions.json +44 -0
  165. data/spec/fixtures/listings/with_photos.json +110 -0
  166. data/spec/fixtures/listings/with_supplement.json +39 -0
  167. data/spec/fixtures/listings/with_videos.json +54 -0
  168. data/spec/fixtures/listings/with_vtour.json +48 -0
  169. data/spec/fixtures/logo_fbs.png +0 -0
  170. data/spec/fixtures/messages/new.json +14 -0
  171. data/spec/fixtures/messages/new_empty.json +7 -0
  172. data/spec/fixtures/messages/new_with_recipients.json +15 -0
  173. data/spec/fixtures/messages/post.json +5 -0
  174. data/spec/fixtures/notes/add.json +11 -0
  175. data/spec/fixtures/notes/agent_shared.json +11 -0
  176. data/spec/fixtures/notes/agent_shared_empty.json +7 -0
  177. data/spec/fixtures/notes/new.json +5 -0
  178. data/spec/fixtures/notifications/mark_read.json +1 -0
  179. data/spec/fixtures/notifications/new.json +8 -0
  180. data/spec/fixtures/notifications/new_empty.json +7 -0
  181. data/spec/fixtures/notifications/notifications.json +43 -0
  182. data/spec/fixtures/notifications/post.json +10 -0
  183. data/spec/fixtures/notifications/unread.json +10 -0
  184. data/spec/fixtures/oauth2/access.json +3 -0
  185. data/spec/fixtures/oauth2/access_with_old_refresh.json +5 -0
  186. data/spec/fixtures/oauth2/access_with_refresh.json +5 -0
  187. data/spec/fixtures/oauth2/authorization_code_body.json +7 -0
  188. data/spec/fixtures/oauth2/error.json +3 -0
  189. data/spec/fixtures/oauth2/password_body.json +7 -0
  190. data/spec/fixtures/oauth2/refresh_body.json +7 -0
  191. data/spec/fixtures/oauth2_error.json +3 -0
  192. data/spec/fixtures/property_types/property_types.json +31 -0
  193. data/spec/fixtures/session.json +10 -0
  194. data/spec/fixtures/standardfields/city.json +1031 -0
  195. data/spec/fixtures/standardfields/nearby.json +53 -0
  196. data/spec/fixtures/standardfields/standardfields.json +188 -0
  197. data/spec/fixtures/standardfields/stateorprovince.json +36 -0
  198. data/spec/fixtures/success.json +5 -0
  199. data/spec/json_helper.rb +76 -0
  200. data/spec/mock_helper.rb +124 -0
  201. data/spec/oauth2_helper.rb +68 -0
  202. data/spec/spec_helper.rb +48 -0
  203. data/spec/unit/flexmls_api_spec.rb~ +23 -0
  204. data/spec/unit/spark_api/authentication/api_auth_spec.rb +169 -0
  205. data/spec/unit/spark_api/authentication/api_auth_spec.rb~ +169 -0
  206. data/spec/unit/spark_api/authentication/base_auth_spec.rb +10 -0
  207. data/spec/unit/spark_api/authentication/base_auth_spec.rb~ +10 -0
  208. data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb +10 -0
  209. data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb~ +10 -0
  210. data/spec/unit/spark_api/authentication/oauth2_spec.rb +205 -0
  211. data/spec/unit/spark_api/authentication/oauth2_spec.rb~ +205 -0
  212. data/spec/unit/spark_api/authentication_spec.rb +38 -0
  213. data/spec/unit/spark_api/authentication_spec.rb~ +38 -0
  214. data/spec/unit/spark_api/configuration/yaml_spec.rb +72 -0
  215. data/spec/unit/spark_api/configuration/yaml_spec.rb~ +72 -0
  216. data/spec/unit/spark_api/configuration_spec.rb +122 -0
  217. data/spec/unit/spark_api/configuration_spec.rb~ +122 -0
  218. data/spec/unit/spark_api/faraday_spec.rb +90 -0
  219. data/spec/unit/spark_api/faraday_spec.rb~ +90 -0
  220. data/spec/unit/spark_api/models/account_spec.rb +176 -0
  221. data/spec/unit/spark_api/models/base_spec.rb +106 -0
  222. data/spec/unit/spark_api/models/connect_prefs_spec.rb +9 -0
  223. data/spec/unit/spark_api/models/constraint_spec.rb +19 -0
  224. data/spec/unit/spark_api/models/contact_spec.rb +108 -0
  225. data/spec/unit/spark_api/models/contact_spec.rb~ +108 -0
  226. data/spec/unit/spark_api/models/document_spec.rb +32 -0
  227. data/spec/unit/spark_api/models/listing_cart_spec.rb +127 -0
  228. data/spec/unit/spark_api/models/listing_cart_spec.rb~ +127 -0
  229. data/spec/unit/spark_api/models/listing_spec.rb +320 -0
  230. data/spec/unit/spark_api/models/listing_spec.rb~ +320 -0
  231. data/spec/unit/spark_api/models/message_spec.rb +47 -0
  232. data/spec/unit/spark_api/models/message_spec.rb~ +47 -0
  233. data/spec/unit/spark_api/models/note_spec.rb +63 -0
  234. data/spec/unit/spark_api/models/note_spec.rb~ +63 -0
  235. data/spec/unit/spark_api/models/notification_spec.rb +62 -0
  236. data/spec/unit/spark_api/models/notification_spec.rb~ +62 -0
  237. data/spec/unit/spark_api/models/open_house_spec.rb +39 -0
  238. data/spec/unit/spark_api/models/photo_spec.rb +92 -0
  239. data/spec/unit/spark_api/models/property_types_spec.rb +33 -0
  240. data/spec/unit/spark_api/models/saved_search_spec.rb +40 -0
  241. data/spec/unit/spark_api/models/shared_listing_spec.rb +45 -0
  242. data/spec/unit/spark_api/models/shared_listing_spec.rb~ +45 -0
  243. data/spec/unit/spark_api/models/standard_fields_spec.rb +60 -0
  244. data/spec/unit/spark_api/models/system_info_spec.rb +83 -0
  245. data/spec/unit/spark_api/models/tour_of_home_spec.rb +44 -0
  246. data/spec/unit/spark_api/models/video_spec.rb +36 -0
  247. data/spec/unit/spark_api/models/virtual_tour_spec.rb +44 -0
  248. data/spec/unit/spark_api/multi_client_spec.rb +56 -0
  249. data/spec/unit/spark_api/multi_client_spec.rb~ +56 -0
  250. data/spec/unit/spark_api/paginate_spec.rb +224 -0
  251. data/spec/unit/spark_api/paginate_spec.rb~ +224 -0
  252. data/spec/unit/spark_api/primary_array_spec.rb +41 -0
  253. data/spec/unit/spark_api/primary_array_spec.rb~ +41 -0
  254. data/spec/unit/spark_api/request_spec.rb +344 -0
  255. data/spec/unit/spark_api/request_spec.rb~ +344 -0
  256. data/spec/unit/spark_api_spec.rb +23 -0
  257. metadata +725 -0
@@ -0,0 +1,197 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ class Listing < Base
4
+ extend Finders
5
+ attr_accessor :photos, :videos, :virtual_tours, :documents
6
+ attr_accessor :constraints
7
+ self.element_name="listings"
8
+ DATA_MASK = "********"
9
+ WRITEABLE_FIELDS = ["ListPrice", "ExpirationDate"]
10
+
11
+ def initialize(attributes={})
12
+ @photos = []
13
+ @videos = []
14
+ @virtual_tours = []
15
+ @documents = []
16
+ @constraints = []
17
+
18
+ if attributes.has_key?('StandardFields')
19
+ pics, vids, tours, docs = attributes['StandardFields'].values_at('Photos','Videos', 'VirtualTours', 'Documents')
20
+ end
21
+
22
+ if pics != nil
23
+ pics.collect { |pic| @photos.push(Photo.new(pic)) }
24
+ attributes['StandardFields'].delete('Photos')
25
+ end
26
+
27
+ if vids != nil
28
+ vids.collect { |vid| @videos.push(Video.new(vid)) }
29
+ attributes['StandardFields'].delete('Videos')
30
+ end
31
+
32
+ if tours != nil
33
+ tours.collect { |tour| @virtual_tours.push(VirtualTour.new(tour)) }
34
+ attributes['StandardFields'].delete('VirtualTours')
35
+ end
36
+
37
+ if docs != nil
38
+ docs.collect { |doc| @documents.push(Document.new(doc)) }
39
+ attributes['StandardFields'].delete('Documents')
40
+ end
41
+
42
+ super(attributes)
43
+ end
44
+
45
+ def self.find_by_cart_id(cart_id, options={})
46
+ query = {:_filter => "ListingCart Eq '#{cart_id}'"}
47
+ find(:all, options.merge(query))
48
+ end
49
+
50
+ def self.my(arguments={})
51
+ collect(connection.get("/my/listings", arguments))
52
+ end
53
+
54
+ def self.office(arguments={})
55
+ collect(connection.get("/office/listings", arguments))
56
+ end
57
+
58
+ def self.company(arguments={})
59
+ collect(connection.get("/company/listings", arguments))
60
+ end
61
+
62
+ def self.nearby(latitude, longitude, arguments={})
63
+ nearby_args = {:Lat => latitude, :Lon => longitude}.merge(arguments)
64
+ collect(connection.get("/listings/nearby", nearby_args))
65
+ end
66
+
67
+ def tour_of_homes(arguments={})
68
+ return @tour_of_homes unless @tour_of_homes.nil?
69
+ @tour_of_homes = TourOfHome.find_by_listing_key(self.Id, arguments)
70
+ end
71
+
72
+ def open_houses(arguments={})
73
+ return @open_houses unless @open_houses.nil?
74
+ @open_houses = OpenHouse.find_by_listing_key(self.Id, arguments)
75
+ end
76
+
77
+ def my_notes
78
+ Note.build_subclass.tap do |note|
79
+ note.prefix = "/listings/#{self.ListingKey}"
80
+ note.element_name = "/my/notes"
81
+ FlexmlsApi.logger.info("Note.path: #{note.path}")
82
+ end
83
+ end
84
+
85
+ # 'fore' is required when accessing an agent's shared
86
+ # notes for a specific contact. If the ApiUser /is/ the
87
+ # contact, then it can be inferred by the api, so it's
88
+ # unecessary
89
+ def shared_notes(fore=nil)
90
+ Note.build_subclass.tap do |note|
91
+ note.prefix = "/listings/#{self.ListingKey}"
92
+ if fore.nil?
93
+ note.element_name = "/shared/notes"
94
+ else
95
+ note.element_name = "/shared/notes/contacts/#{fore}"
96
+ end
97
+ end
98
+ end
99
+
100
+ def street_address
101
+ "#{self.StreetNumber} #{self.StreetDirPrefix} #{self.StreetName} #{self.StreetSuffix} #{self.StreetDirSuffix} #{self.StreetAdditionalInfo}".delete(DATA_MASK).strip().gsub(/\s{2,}/, ' ')
102
+ end
103
+
104
+ def region_address
105
+ "#{self.City}, #{self.StateOrProvince} #{self.PostalCode}".delete(DATA_MASK).strip().gsub(/^,\s/, '').gsub(/,$/, '')
106
+ end
107
+
108
+ def full_address
109
+ "#{self.street_address}, #{self.region_address}".strip().gsub(/^,\s/, '').gsub(/,$/, '')
110
+ end
111
+
112
+ def save(arguments={})
113
+ self.errors = []
114
+ begin
115
+ return save!(arguments)
116
+ rescue BadResourceRequest => e
117
+ self.errors << {:code => e.code, :message => e.message}
118
+ FlexmlsApi.logger.debug("BHDEBUG: #{e.inspect}")
119
+ if e.code == 1053
120
+ @constraints = []
121
+ e.details.each do |detail|
122
+ detail.each_pair do |k,v|
123
+ v.each { |constraint| @constraints << Constraint.new(constraint)}
124
+ end
125
+ end
126
+ end
127
+ FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
128
+ rescue NotFound => e
129
+ FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
130
+ end
131
+ false
132
+ end
133
+ def save!(arguments={})
134
+ writable_changed_keys = changed & WRITEABLE_FIELDS
135
+ if writable_changed_keys.empty?
136
+ FlexmlsApi.logger.warn("No supported listing change detected")
137
+ else
138
+ results = connection.put "#{self.class.path}/#{self.Id}", build_hash(writable_changed_keys), arguments
139
+ @contstraints = []
140
+ results.details.each do |detail|
141
+ detail.each_pair do |k,v|
142
+ v.each { |constraint| @constraints << Constraint.new(constraint)}
143
+ end
144
+ end
145
+ end
146
+ true
147
+ end
148
+
149
+ def editable?(editable_settings = [])
150
+ settings = Array(editable_settings)
151
+ editable = attributes.include?("Permissions") && self.Permissions["Editable"] == true
152
+ if editable
153
+ settings.each{ |setting| editable = false unless self.Permissions["EditableSettings"][setting.to_s] == true }
154
+ end
155
+ editable
156
+ end
157
+
158
+ def ExpirationDate
159
+ attributes["ExpirationDate"]
160
+ end
161
+ def ExpirationDate=(value)
162
+ write_attribute("ExpirationDate", value)
163
+ end
164
+
165
+
166
+ private
167
+
168
+ # TODO trim this down so we're only overriding the StandardFields access
169
+ def method_missing(method_symbol, *arguments)
170
+ method_name = method_symbol.to_s
171
+
172
+ if method_name =~ /(=|\?)$/
173
+ case $1
174
+ when "="
175
+ write_attribute($`,arguments.first)
176
+ # TODO figure out a nice way to present setters for the standard fields
177
+ when "?"
178
+ attributes[$`]
179
+ end
180
+ else
181
+ return attributes[method_name] if attributes.include?(method_name)
182
+ return @attributes['StandardFields'][method_name] if attributes['StandardFields'].include?(method_name)
183
+ super # GTFO
184
+ end
185
+ end
186
+
187
+ def build_hash(keys)
188
+ hash = {}
189
+ keys.each do |key|
190
+ hash[key] = attributes[key]
191
+ end
192
+ hash
193
+ end
194
+
195
+ end
196
+ end
197
+ end
@@ -0,0 +1,72 @@
1
+ module SparkApi
2
+ module Models
3
+ class ListingCart < Base
4
+ extend Finders
5
+ self.element_name="listingcarts"
6
+
7
+ def ListingIds=(listing_ids)
8
+ attributes["ListingIds"] = Array(listing_ids)
9
+ end
10
+ def Name=(name)
11
+ attributes["Name"] = name
12
+ end
13
+
14
+ def add_listing(listing)
15
+ id = listing.respond_to?(:Id) ? listing.Id : listing.to_s
16
+ results = connection.post("#{self.class.path}/#{self.Id}", {"ListingIds" => [ listing ]})
17
+ self.ListingCount = results.first["ListingCount"]
18
+ end
19
+
20
+ def remove_listing(listing)
21
+ id = listing.respond_to?(:Id) ? listing.Id : listing.to_s
22
+ results = connection.delete("#{self.class.path}/#{self.Id}/listings/#{id}")
23
+ self.ListingCount = results.first["ListingCount"]
24
+ end
25
+
26
+ def self.for(listings,arguments={})
27
+ keys = Array(listings).map { |l| l.respond_to?(:Id) ? l.Id : l.to_s }
28
+ collect(connection.get("/#{self.element_name}/for/#{keys.join(",")}", arguments))
29
+ end
30
+
31
+ def self.my(arguments={})
32
+ collect(connection.get("/my/#{self.element_name}", arguments))
33
+ end
34
+
35
+ def self.portal(arguments={})
36
+ collect(connection.get("/#{self.element_name}/portal", arguments))
37
+ end
38
+
39
+ def save(arguments={})
40
+ begin
41
+ return save!(arguments)
42
+ rescue BadResourceRequest => e
43
+ rescue NotFound => e
44
+ # log and leave
45
+ SparkApi.logger.error("Failed to save contact #{self}: #{e.message}")
46
+ end
47
+ false
48
+ end
49
+ def save!(arguments={})
50
+ attributes['Id'].nil? ? create!(arguments) : update!(arguments)
51
+ end
52
+
53
+ def delete(args={})
54
+ connection.delete("#{self.class.path}/#{self.Id}", args)
55
+ end
56
+
57
+ private
58
+ def create!(arguments={})
59
+ results = connection.post self.class.path, {"ListingCarts" => [ attributes ]}, arguments
60
+ result = results.first
61
+ attributes['ResourceUri'] = result['ResourceUri']
62
+ attributes['Id'] = parse_id(result['ResourceUri'])
63
+ true
64
+ end
65
+ def update!(arguments={})
66
+ results = connection.put "#{self.class.path}/#{self.Id}", {"ListingCarts" => [ {"Name" => attributes["Name"], "ListingIds" => attributes["ListingIds"]} ] }, arguments
67
+ true
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,72 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ class ListingCart < Base
4
+ extend Finders
5
+ self.element_name="listingcarts"
6
+
7
+ def ListingIds=(listing_ids)
8
+ attributes["ListingIds"] = Array(listing_ids)
9
+ end
10
+ def Name=(name)
11
+ attributes["Name"] = name
12
+ end
13
+
14
+ def add_listing(listing)
15
+ id = listing.respond_to?(:Id) ? listing.Id : listing.to_s
16
+ results = connection.post("#{self.class.path}/#{self.Id}", {"ListingIds" => [ listing ]})
17
+ self.ListingCount = results.first["ListingCount"]
18
+ end
19
+
20
+ def remove_listing(listing)
21
+ id = listing.respond_to?(:Id) ? listing.Id : listing.to_s
22
+ results = connection.delete("#{self.class.path}/#{self.Id}/listings/#{id}")
23
+ self.ListingCount = results.first["ListingCount"]
24
+ end
25
+
26
+ def self.for(listings,arguments={})
27
+ keys = Array(listings).map { |l| l.respond_to?(:Id) ? l.Id : l.to_s }
28
+ collect(connection.get("/#{self.element_name}/for/#{keys.join(",")}", arguments))
29
+ end
30
+
31
+ def self.my(arguments={})
32
+ collect(connection.get("/my/#{self.element_name}", arguments))
33
+ end
34
+
35
+ def self.portal(arguments={})
36
+ collect(connection.get("/#{self.element_name}/portal", arguments))
37
+ end
38
+
39
+ def save(arguments={})
40
+ begin
41
+ return save!(arguments)
42
+ rescue BadResourceRequest => e
43
+ rescue NotFound => e
44
+ # log and leave
45
+ FlexmlsApi.logger.error("Failed to save contact #{self}: #{e.message}")
46
+ end
47
+ false
48
+ end
49
+ def save!(arguments={})
50
+ attributes['Id'].nil? ? create!(arguments) : update!(arguments)
51
+ end
52
+
53
+ def delete(args={})
54
+ connection.delete("#{self.class.path}/#{self.Id}", args)
55
+ end
56
+
57
+ private
58
+ def create!(arguments={})
59
+ results = connection.post self.class.path, {"ListingCarts" => [ attributes ]}, arguments
60
+ result = results.first
61
+ attributes['ResourceUri'] = result['ResourceUri']
62
+ attributes['Id'] = parse_id(result['ResourceUri'])
63
+ true
64
+ end
65
+ def update!(arguments={})
66
+ results = connection.put "#{self.class.path}/#{self.Id}", {"ListingCarts" => [ {"Name" => attributes["Name"], "ListingIds" => attributes["ListingIds"]} ] }, arguments
67
+ true
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,33 @@
1
+ module SparkApi
2
+ module Models
3
+ class MarketStatistics < Base
4
+ self.element_name="marketstatistics"
5
+
6
+ def self.absorption(parameters={})
7
+ self.stat('absorption',parameters)
8
+ end
9
+ def self.inventory(parameters={})
10
+ self.stat('inventory',parameters)
11
+ end
12
+ def self.price(parameters={})
13
+ self.stat('price',parameters)
14
+ end
15
+ def self.ratio(parameters={})
16
+ self.stat('ratio',parameters)
17
+ end
18
+ def self.dom(parameters={})
19
+ self.stat('dom',parameters)
20
+ end
21
+ def self.volume(parameters={})
22
+ self.stat('volume',parameters)
23
+ end
24
+
25
+ private
26
+ def self.stat(stat_name, parameters={})
27
+ resp = connection.get("#{path}/#{stat_name}", parameters)
28
+ new(resp.first)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ class MarketStatistics < Base
4
+ self.element_name="marketstatistics"
5
+
6
+ def self.absorption(parameters={})
7
+ self.stat('absorption',parameters)
8
+ end
9
+ def self.inventory(parameters={})
10
+ self.stat('inventory',parameters)
11
+ end
12
+ def self.price(parameters={})
13
+ self.stat('price',parameters)
14
+ end
15
+ def self.ratio(parameters={})
16
+ self.stat('ratio',parameters)
17
+ end
18
+ def self.dom(parameters={})
19
+ self.stat('dom',parameters)
20
+ end
21
+ def self.volume(parameters={})
22
+ self.stat('volume',parameters)
23
+ end
24
+
25
+ private
26
+ def self.stat(stat_name, parameters={})
27
+ resp = connection.get("#{path}/#{stat_name}", parameters)
28
+ new(resp.first)
29
+ end
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ module SparkApi
2
+ module Models
3
+ class Message < Base
4
+ self.element_name="messages"
5
+
6
+ def save(arguments={})
7
+ begin
8
+ return save!(arguments)
9
+ rescue NotFound, BadResourceRequest => e
10
+ SparkApi.logger.error("Failed to save resource #{self}: #{e.message}")
11
+ end
12
+ false
13
+ end
14
+ def save!(arguments={})
15
+ results = connection.post self.class.path, {"Messages" => [ attributes ]}, arguments
16
+ true
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ class Message < Base
4
+ self.element_name="messages"
5
+
6
+ def save(arguments={})
7
+ begin
8
+ return save!(arguments)
9
+ rescue NotFound, BadResourceRequest => e
10
+ FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
11
+ end
12
+ false
13
+ end
14
+ def save!(arguments={})
15
+ results = connection.post self.class.path, {"Messages" => [ attributes ]}, arguments
16
+ true
17
+ end
18
+
19
+ end
20
+ end
21
+ end