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,10 @@
1
+ module SparkApi
2
+ module Models
3
+ class Connect < Base
4
+ self.element_name="connect"
5
+ def self.prefs
6
+ connection.get("#{path}/prefs")
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ class Connect < Base
4
+ self.element_name="connect"
5
+ def self.prefs
6
+ connection.get("#{path}/prefs")
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module SparkApi
2
+ module Models
3
+ class Constraint
4
+ ATTRIBUTES = ["RuleValue","Value","RuleFieldValue","RuleField","RuleName"]
5
+ attr_accessor *ATTRIBUTES
6
+ def initialize(args)
7
+ ATTRIBUTES.each { |f| send("#{f}=", args[f]) if args.include?(f) || args.include?(f.to_sym) }
8
+ end
9
+
10
+ def to_s
11
+ "#{self.RuleName}: Field(#{self.RuleField},#{self.RuleFieldValue}) Value(#{self.RuleValue},#{self.Value})"
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,16 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ class Constraint
4
+ ATTRIBUTES = ["RuleValue","Value","RuleFieldValue","RuleField","RuleName"]
5
+ attr_accessor *ATTRIBUTES
6
+ def initialize(args)
7
+ ATTRIBUTES.each { |f| send("#{f}=", args[f]) if args.include?(f) || args.include?(f.to_sym) }
8
+ end
9
+
10
+ def to_s
11
+ "#{self.RuleName}: Field(#{self.RuleField},#{self.RuleFieldValue}) Value(#{self.RuleValue},#{self.Value})"
12
+ end
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,49 @@
1
+ module SparkApi
2
+ module Models
3
+ class Contact < Base
4
+ extend Finders
5
+ self.element_name="contacts"
6
+
7
+ def save(arguments={})
8
+ self.errors = [] # clear the errors hash
9
+ begin
10
+ return save!(arguments)
11
+ rescue BadResourceRequest => e
12
+ self.errors << {:code => e.code, :message => e.message}
13
+ SparkApi.logger.error("Failed to save resource #{self}: #{e.message}")
14
+ rescue NotFound => e
15
+ SparkApi.logger.error("Failed to save resource #{self}: #{e.message}")
16
+ end
17
+ false
18
+ end
19
+ def save!(arguments={})
20
+ results = connection.post self.class.path, {"Contacts" => [ attributes ], "Notify" => notify? }, arguments
21
+ result = results.first
22
+ attributes['ResourceUri'] = result['ResourceUri']
23
+ attributes['Id'] = parse_id(result['ResourceUri'])
24
+ true
25
+ end
26
+
27
+ def self.by_tag(tag_name, arguments={})
28
+ collect(connection.get("#{path}/tags/#{tag_name}", arguments))
29
+ end
30
+
31
+ def self.tags(arguments={})
32
+ connection.get("#{path}/tags", arguments)
33
+ end
34
+
35
+ def self.my(arguments={})
36
+ new(connection.get('/my/contact', arguments).first)
37
+ end
38
+
39
+ # Notify the agent of contact creation via a Spark notification.
40
+ def notify?
41
+ @notify == true
42
+ end
43
+ def notify=(notify_me=true)
44
+ @notify = notify_me
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ class Contact < Base
4
+ extend Finders
5
+ self.element_name="contacts"
6
+
7
+ def save(arguments={})
8
+ self.errors = [] # clear the errors hash
9
+ begin
10
+ return save!(arguments)
11
+ rescue BadResourceRequest => e
12
+ self.errors << {:code => e.code, :message => e.message}
13
+ FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
14
+ rescue NotFound => e
15
+ FlexmlsApi.logger.error("Failed to save resource #{self}: #{e.message}")
16
+ end
17
+ false
18
+ end
19
+ def save!(arguments={})
20
+ results = connection.post self.class.path, {"Contacts" => [ attributes ], "Notify" => notify? }, arguments
21
+ result = results.first
22
+ attributes['ResourceUri'] = result['ResourceUri']
23
+ attributes['Id'] = parse_id(result['ResourceUri'])
24
+ true
25
+ end
26
+
27
+ def self.by_tag(tag_name, arguments={})
28
+ collect(connection.get("#{path}/tags/#{tag_name}", arguments))
29
+ end
30
+
31
+ def self.tags(arguments={})
32
+ connection.get("#{path}/tags", arguments)
33
+ end
34
+
35
+ def self.my(arguments={})
36
+ new(connection.get('/my/contact', arguments).first)
37
+ end
38
+
39
+ # Notify the agent of contact creation via a flexmls message.
40
+ def notify?
41
+ @notify == true
42
+ end
43
+ def notify=(notify_me=true)
44
+ @notify = notify_me
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,12 @@
1
+ module SparkApi
2
+ module Models
3
+ class CustomFields < Base
4
+ self.element_name="customfields"
5
+
6
+
7
+ def self.find_by_property_type(card_fmt, arguments={})
8
+ collect(connection.get("#{self.path}/#{card_fmt}", arguments))
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ class CustomFields < Base
4
+ self.element_name="customfields"
5
+
6
+
7
+ def self.find_by_property_type(card_fmt, arguments={})
8
+ collect(connection.get("#{self.path}/#{card_fmt}", arguments))
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module SparkApi
2
+ module Models
3
+ class Document < Base
4
+ extend Subresource
5
+
6
+ self.element_name="documents"
7
+
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ class Document < Base
4
+ extend Subresource
5
+
6
+ self.element_name="documents"
7
+
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,45 @@
1
+ module SparkApi
2
+ module Models
3
+ # =Rails-like finders module
4
+ # Adds the base set of finder class methods to the models that support them (not all of them do)
5
+ module Finders
6
+
7
+ def find(*arguments)
8
+ scope = arguments.slice!(0)
9
+ options = arguments.slice!(0) || {}
10
+ case scope
11
+ when :all then find_every(options)
12
+ when :first then find_every(options).first
13
+ when :last then find_every(options).last
14
+ when :one then find_one(options)
15
+ else find_single(scope, options)
16
+ end
17
+ end
18
+
19
+ def first(*arguments)
20
+ find(:first, *arguments)
21
+ end
22
+
23
+ def last(*arguments)
24
+ find(:last, *arguments)
25
+ end
26
+
27
+ private
28
+
29
+ def find_every(options)
30
+ collect(connection.get("/#{element_name}", options))
31
+ end
32
+
33
+ def find_one(options)
34
+ raise NotImplementedError # TODO um... what?
35
+ end
36
+
37
+ def find_single(scope, options)
38
+ resp = connection.get("/#{element_name}/#{scope}", options)
39
+ new(resp.first)
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,45 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ # =Rails-like finders module
4
+ # Adds the base set of finder class methods to the models that support them (not all of them do)
5
+ module Finders
6
+
7
+ def find(*arguments)
8
+ scope = arguments.slice!(0)
9
+ options = arguments.slice!(0) || {}
10
+ case scope
11
+ when :all then find_every(options)
12
+ when :first then find_every(options).first
13
+ when :last then find_every(options).last
14
+ when :one then find_one(options)
15
+ else find_single(scope, options)
16
+ end
17
+ end
18
+
19
+ def first(*arguments)
20
+ find(:first, *arguments)
21
+ end
22
+
23
+ def last(*arguments)
24
+ find(:last, *arguments)
25
+ end
26
+
27
+ private
28
+
29
+ def find_every(options)
30
+ collect(connection.get("/#{element_name}", options))
31
+ end
32
+
33
+ def find_one(options)
34
+ raise NotImplementedError # TODO um... what?
35
+ end
36
+
37
+ def find_single(scope, options)
38
+ resp = connection.get("/#{element_name}/#{scope}", options)
39
+ new(resp.first)
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,47 @@
1
+ module SparkApi
2
+ module Models
3
+ class IdxLink < Base
4
+ self.element_name="idxlinks"
5
+
6
+ LINK_TYPES = ["QuickSearch", "SavedSearch", "MyListings", "Roster"]
7
+
8
+ #TODO Work all below into common base class
9
+ def self.find(*arguments)
10
+ scope = arguments.slice!(0)
11
+ options = arguments.slice!(0) || {}
12
+
13
+ case scope
14
+ when :all then find_every(options)
15
+ when :first then find_every(options).first
16
+ when :last then find_every(options).last
17
+ when :one then find_one(options)
18
+ else find_single(scope, options)
19
+ end
20
+ end
21
+
22
+ def self.first(*arguments)
23
+ find(:first, *arguments)
24
+ end
25
+
26
+ def self.last(*arguments)
27
+ find(:last, *arguments)
28
+ end
29
+
30
+ private
31
+
32
+ def self.find_every(options)
33
+ raise NotImplementedError # TODO
34
+ end
35
+
36
+ def self.find_one(options)
37
+ raise NotImplementedError # TODO
38
+ end
39
+
40
+ def self.find_single(scope, options)
41
+ resp = SparkApi.client.get("/idxlinks/#{scope}", options)
42
+ new(resp.first)
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,47 @@
1
+ module FlexmlsApi
2
+ module Models
3
+ class IdxLink < Base
4
+ self.element_name="idxlinks"
5
+
6
+ LINK_TYPES = ["QuickSearch", "SavedSearch", "MyListings", "Roster"]
7
+
8
+ #TODO Work all below into common base class
9
+ def self.find(*arguments)
10
+ scope = arguments.slice!(0)
11
+ options = arguments.slice!(0) || {}
12
+
13
+ case scope
14
+ when :all then find_every(options)
15
+ when :first then find_every(options).first
16
+ when :last then find_every(options).last
17
+ when :one then find_one(options)
18
+ else find_single(scope, options)
19
+ end
20
+ end
21
+
22
+ def self.first(*arguments)
23
+ find(:first, *arguments)
24
+ end
25
+
26
+ def self.last(*arguments)
27
+ find(:last, *arguments)
28
+ end
29
+
30
+ private
31
+
32
+ def self.find_every(options)
33
+ raise NotImplementedError # TODO
34
+ end
35
+
36
+ def self.find_one(options)
37
+ raise NotImplementedError # TODO
38
+ end
39
+
40
+ def self.find_single(scope, options)
41
+ resp = FlexmlsApi.client.get("/idxlinks/#{scope}", options)
42
+ new(resp.first)
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,197 @@
1
+ module SparkApi
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
+ SparkApi.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
+ SparkApi.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
+ SparkApi.logger.error("Failed to save resource #{self}: #{e.message}")
128
+ rescue NotFound => e
129
+ SparkApi.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
+ SparkApi.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