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.
- data/History.txt +139 -0
- data/LICENSE +14 -0
- data/README.md +153 -0
- data/Rakefile +18 -0
- data/VERSION +1 -0
- data/bin/spark_api +8 -0
- data/bin/spark_api~ +8 -0
- data/lib/spark_api.rb +46 -0
- data/lib/spark_api/authentication.rb +55 -0
- data/lib/spark_api/authentication/api_auth.rb +104 -0
- data/lib/spark_api/authentication/api_auth.rb~ +104 -0
- data/lib/spark_api/authentication/base_auth.rb +47 -0
- data/lib/spark_api/authentication/base_auth.rb~ +47 -0
- data/lib/spark_api/authentication/oauth2.rb +198 -0
- data/lib/spark_api/authentication/oauth2.rb~ +199 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb +87 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_base.rb~ +87 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb +48 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_code.rb~ +49 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb +44 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_password.rb~ +45 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb +35 -0
- data/lib/spark_api/authentication/oauth2_impl/grant_type_refresh.rb~ +36 -0
- data/lib/spark_api/authentication/oauth2_impl/middleware.rb +38 -0
- data/lib/spark_api/authentication/oauth2_impl/middleware.rb~ +39 -0
- data/lib/spark_api/authentication/oauth2_impl/password_provider.rb +24 -0
- data/lib/spark_api/authentication/oauth2_impl/password_provider.rb~ +25 -0
- data/lib/spark_api/cli.rb +158 -0
- data/lib/spark_api/cli.rb~ +158 -0
- data/lib/spark_api/cli/api_auth.rb +8 -0
- data/lib/spark_api/cli/api_auth.rb~ +8 -0
- data/lib/spark_api/cli/oauth2.rb +14 -0
- data/lib/spark_api/cli/oauth2.rb~ +14 -0
- data/lib/spark_api/cli/setup.rb +47 -0
- data/lib/spark_api/cli/setup.rb~ +47 -0
- data/lib/spark_api/client.rb +27 -0
- data/lib/spark_api/configuration.rb +54 -0
- data/lib/spark_api/configuration.rb~ +54 -0
- data/lib/spark_api/configuration/yaml.rb +101 -0
- data/lib/spark_api/configuration/yaml.rb~ +101 -0
- data/lib/spark_api/connection.rb +42 -0
- data/lib/spark_api/faraday.rb +64 -0
- data/lib/spark_api/faraday.rb~ +64 -0
- data/lib/spark_api/models.rb +33 -0
- data/lib/spark_api/models.rb~ +33 -0
- data/lib/spark_api/models/account.rb +115 -0
- data/lib/spark_api/models/account.rb~ +115 -0
- data/lib/spark_api/models/base.rb +118 -0
- data/lib/spark_api/models/base.rb~ +118 -0
- data/lib/spark_api/models/connect_prefs.rb +10 -0
- data/lib/spark_api/models/connect_prefs.rb~ +10 -0
- data/lib/spark_api/models/constraint.rb +16 -0
- data/lib/spark_api/models/constraint.rb~ +16 -0
- data/lib/spark_api/models/contact.rb +49 -0
- data/lib/spark_api/models/contact.rb~ +49 -0
- data/lib/spark_api/models/custom_fields.rb +12 -0
- data/lib/spark_api/models/custom_fields.rb~ +12 -0
- data/lib/spark_api/models/document.rb +11 -0
- data/lib/spark_api/models/document.rb~ +11 -0
- data/lib/spark_api/models/finders.rb +45 -0
- data/lib/spark_api/models/finders.rb~ +45 -0
- data/lib/spark_api/models/idx_link.rb +47 -0
- data/lib/spark_api/models/idx_link.rb~ +47 -0
- data/lib/spark_api/models/listing.rb +197 -0
- data/lib/spark_api/models/listing.rb~ +197 -0
- data/lib/spark_api/models/listing_cart.rb +72 -0
- data/lib/spark_api/models/listing_cart.rb~ +72 -0
- data/lib/spark_api/models/market_statistics.rb +33 -0
- data/lib/spark_api/models/market_statistics.rb~ +33 -0
- data/lib/spark_api/models/message.rb +21 -0
- data/lib/spark_api/models/message.rb~ +21 -0
- data/lib/spark_api/models/note.rb +41 -0
- data/lib/spark_api/models/note.rb~ +41 -0
- data/lib/spark_api/models/notification.rb +42 -0
- data/lib/spark_api/models/notification.rb~ +42 -0
- data/lib/spark_api/models/open_house.rb +24 -0
- data/lib/spark_api/models/open_house.rb~ +24 -0
- data/lib/spark_api/models/photo.rb +70 -0
- data/lib/spark_api/models/photo.rb~ +70 -0
- data/lib/spark_api/models/property_types.rb +7 -0
- data/lib/spark_api/models/property_types.rb~ +7 -0
- data/lib/spark_api/models/saved_search.rb +16 -0
- data/lib/spark_api/models/saved_search.rb~ +16 -0
- data/lib/spark_api/models/shared_listing.rb +35 -0
- data/lib/spark_api/models/shared_listing.rb~ +35 -0
- data/lib/spark_api/models/standard_fields.rb +50 -0
- data/lib/spark_api/models/standard_fields.rb~ +50 -0
- data/lib/spark_api/models/subresource.rb +19 -0
- data/lib/spark_api/models/subresource.rb~ +19 -0
- data/lib/spark_api/models/system_info.rb +14 -0
- data/lib/spark_api/models/system_info.rb~ +14 -0
- data/lib/spark_api/models/tour_of_home.rb +24 -0
- data/lib/spark_api/models/tour_of_home.rb~ +24 -0
- data/lib/spark_api/models/video.rb +16 -0
- data/lib/spark_api/models/video.rb~ +16 -0
- data/lib/spark_api/models/virtual_tour.rb +18 -0
- data/lib/spark_api/models/virtual_tour.rb~ +18 -0
- data/lib/spark_api/multi_client.rb +59 -0
- data/lib/spark_api/multi_client.rb~ +59 -0
- data/lib/spark_api/paginate.rb +109 -0
- data/lib/spark_api/paginate.rb~ +109 -0
- data/lib/spark_api/primary_array.rb +29 -0
- data/lib/spark_api/primary_array.rb~ +29 -0
- data/lib/spark_api/request.rb +96 -0
- data/lib/spark_api/request.rb~ +96 -0
- data/lib/spark_api/response.rb +70 -0
- data/lib/spark_api/response.rb~ +70 -0
- data/lib/spark_api/version.rb +4 -0
- data/lib/spark_api/version.rb~ +4 -0
- data/script/console +6 -0
- data/script/console~ +6 -0
- data/script/example.rb +27 -0
- data/script/example.rb~ +27 -0
- data/spec/fixtures/accounts/all.json +160 -0
- data/spec/fixtures/accounts/my.json +74 -0
- data/spec/fixtures/accounts/my_portal.json +20 -0
- data/spec/fixtures/accounts/my_put.json +5 -0
- data/spec/fixtures/accounts/my_save.json +5 -0
- data/spec/fixtures/accounts/office.json +142 -0
- data/spec/fixtures/accounts/password_save.json +6 -0
- data/spec/fixtures/authentication_failure.json +7 -0
- data/spec/fixtures/base.json +13 -0
- data/spec/fixtures/contacts/contacts.json +28 -0
- data/spec/fixtures/contacts/my.json +19 -0
- data/spec/fixtures/contacts/new.json +11 -0
- data/spec/fixtures/contacts/new_empty.json +8 -0
- data/spec/fixtures/contacts/new_notify.json +11 -0
- data/spec/fixtures/contacts/post.json +10 -0
- data/spec/fixtures/contacts/tags.json +11 -0
- data/spec/fixtures/count.json +10 -0
- data/spec/fixtures/empty.json +3 -0
- data/spec/fixtures/errors/expired.json +7 -0
- data/spec/fixtures/errors/failure.json +5 -0
- data/spec/fixtures/errors/failure_with_constraint.json +17 -0
- data/spec/fixtures/errors/failure_with_msg.json +7 -0
- data/spec/fixtures/generic_delete.json +1 -0
- data/spec/fixtures/generic_failure.json +5 -0
- data/spec/fixtures/listing_carts/add_listing.json +13 -0
- data/spec/fixtures/listing_carts/add_listing_post.json +5 -0
- data/spec/fixtures/listing_carts/empty.json +5 -0
- data/spec/fixtures/listing_carts/listing_cart.json +19 -0
- data/spec/fixtures/listing_carts/new.json +12 -0
- data/spec/fixtures/listing_carts/post.json +10 -0
- data/spec/fixtures/listing_carts/remove_listing.json +13 -0
- data/spec/fixtures/listings/constraints.json +18 -0
- data/spec/fixtures/listings/constraints_with_pagination.json +24 -0
- data/spec/fixtures/listings/document_index.json +19 -0
- data/spec/fixtures/listings/multiple.json +69 -0
- data/spec/fixtures/listings/no_subresources.json +38 -0
- data/spec/fixtures/listings/open_houses.json +21 -0
- data/spec/fixtures/listings/photos/index.json +469 -0
- data/spec/fixtures/listings/photos/new.json +12 -0
- data/spec/fixtures/listings/photos/post.json +20 -0
- data/spec/fixtures/listings/put.json +5 -0
- data/spec/fixtures/listings/put_expiration_date.json +5 -0
- data/spec/fixtures/listings/saved_search.json +17 -0
- data/spec/fixtures/listings/shared_listing_get.json +14 -0
- data/spec/fixtures/listings/shared_listing_new.json +9 -0
- data/spec/fixtures/listings/shared_listing_post.json +10 -0
- data/spec/fixtures/listings/tour_of_homes.json +23 -0
- data/spec/fixtures/listings/videos_index.json +18 -0
- data/spec/fixtures/listings/virtual_tours_index.json +42 -0
- data/spec/fixtures/listings/with_documents.json +52 -0
- data/spec/fixtures/listings/with_permissions.json +44 -0
- data/spec/fixtures/listings/with_photos.json +110 -0
- data/spec/fixtures/listings/with_supplement.json +39 -0
- data/spec/fixtures/listings/with_videos.json +54 -0
- data/spec/fixtures/listings/with_vtour.json +48 -0
- data/spec/fixtures/logo_fbs.png +0 -0
- data/spec/fixtures/messages/new.json +14 -0
- data/spec/fixtures/messages/new_empty.json +7 -0
- data/spec/fixtures/messages/new_with_recipients.json +15 -0
- data/spec/fixtures/messages/post.json +5 -0
- data/spec/fixtures/notes/add.json +11 -0
- data/spec/fixtures/notes/agent_shared.json +11 -0
- data/spec/fixtures/notes/agent_shared_empty.json +7 -0
- data/spec/fixtures/notes/new.json +5 -0
- data/spec/fixtures/notifications/mark_read.json +1 -0
- data/spec/fixtures/notifications/new.json +8 -0
- data/spec/fixtures/notifications/new_empty.json +7 -0
- data/spec/fixtures/notifications/notifications.json +43 -0
- data/spec/fixtures/notifications/post.json +10 -0
- data/spec/fixtures/notifications/unread.json +10 -0
- data/spec/fixtures/oauth2/access.json +3 -0
- data/spec/fixtures/oauth2/access_with_old_refresh.json +5 -0
- data/spec/fixtures/oauth2/access_with_refresh.json +5 -0
- data/spec/fixtures/oauth2/authorization_code_body.json +7 -0
- data/spec/fixtures/oauth2/error.json +3 -0
- data/spec/fixtures/oauth2/password_body.json +7 -0
- data/spec/fixtures/oauth2/refresh_body.json +7 -0
- data/spec/fixtures/oauth2_error.json +3 -0
- data/spec/fixtures/property_types/property_types.json +31 -0
- data/spec/fixtures/session.json +10 -0
- data/spec/fixtures/standardfields/city.json +1031 -0
- data/spec/fixtures/standardfields/nearby.json +53 -0
- data/spec/fixtures/standardfields/standardfields.json +188 -0
- data/spec/fixtures/standardfields/stateorprovince.json +36 -0
- data/spec/fixtures/success.json +5 -0
- data/spec/json_helper.rb +76 -0
- data/spec/mock_helper.rb +124 -0
- data/spec/oauth2_helper.rb +68 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/unit/flexmls_api_spec.rb~ +23 -0
- data/spec/unit/spark_api/authentication/api_auth_spec.rb +169 -0
- data/spec/unit/spark_api/authentication/api_auth_spec.rb~ +169 -0
- data/spec/unit/spark_api/authentication/base_auth_spec.rb +10 -0
- data/spec/unit/spark_api/authentication/base_auth_spec.rb~ +10 -0
- data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb +10 -0
- data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb~ +10 -0
- data/spec/unit/spark_api/authentication/oauth2_spec.rb +205 -0
- data/spec/unit/spark_api/authentication/oauth2_spec.rb~ +205 -0
- data/spec/unit/spark_api/authentication_spec.rb +38 -0
- data/spec/unit/spark_api/authentication_spec.rb~ +38 -0
- data/spec/unit/spark_api/configuration/yaml_spec.rb +72 -0
- data/spec/unit/spark_api/configuration/yaml_spec.rb~ +72 -0
- data/spec/unit/spark_api/configuration_spec.rb +122 -0
- data/spec/unit/spark_api/configuration_spec.rb~ +122 -0
- data/spec/unit/spark_api/faraday_spec.rb +90 -0
- data/spec/unit/spark_api/faraday_spec.rb~ +90 -0
- data/spec/unit/spark_api/models/account_spec.rb +176 -0
- data/spec/unit/spark_api/models/base_spec.rb +106 -0
- data/spec/unit/spark_api/models/connect_prefs_spec.rb +9 -0
- data/spec/unit/spark_api/models/constraint_spec.rb +19 -0
- data/spec/unit/spark_api/models/contact_spec.rb +108 -0
- data/spec/unit/spark_api/models/contact_spec.rb~ +108 -0
- data/spec/unit/spark_api/models/document_spec.rb +32 -0
- data/spec/unit/spark_api/models/listing_cart_spec.rb +127 -0
- data/spec/unit/spark_api/models/listing_cart_spec.rb~ +127 -0
- data/spec/unit/spark_api/models/listing_spec.rb +320 -0
- data/spec/unit/spark_api/models/listing_spec.rb~ +320 -0
- data/spec/unit/spark_api/models/message_spec.rb +47 -0
- data/spec/unit/spark_api/models/message_spec.rb~ +47 -0
- data/spec/unit/spark_api/models/note_spec.rb +63 -0
- data/spec/unit/spark_api/models/note_spec.rb~ +63 -0
- data/spec/unit/spark_api/models/notification_spec.rb +62 -0
- data/spec/unit/spark_api/models/notification_spec.rb~ +62 -0
- data/spec/unit/spark_api/models/open_house_spec.rb +39 -0
- data/spec/unit/spark_api/models/photo_spec.rb +92 -0
- data/spec/unit/spark_api/models/property_types_spec.rb +33 -0
- data/spec/unit/spark_api/models/saved_search_spec.rb +40 -0
- data/spec/unit/spark_api/models/shared_listing_spec.rb +45 -0
- data/spec/unit/spark_api/models/shared_listing_spec.rb~ +45 -0
- data/spec/unit/spark_api/models/standard_fields_spec.rb +60 -0
- data/spec/unit/spark_api/models/system_info_spec.rb +83 -0
- data/spec/unit/spark_api/models/tour_of_home_spec.rb +44 -0
- data/spec/unit/spark_api/models/video_spec.rb +36 -0
- data/spec/unit/spark_api/models/virtual_tour_spec.rb +44 -0
- data/spec/unit/spark_api/multi_client_spec.rb +56 -0
- data/spec/unit/spark_api/multi_client_spec.rb~ +56 -0
- data/spec/unit/spark_api/paginate_spec.rb +224 -0
- data/spec/unit/spark_api/paginate_spec.rb~ +224 -0
- data/spec/unit/spark_api/primary_array_spec.rb +41 -0
- data/spec/unit/spark_api/primary_array_spec.rb~ +41 -0
- data/spec/unit/spark_api/request_spec.rb +344 -0
- data/spec/unit/spark_api/request_spec.rb~ +344 -0
- data/spec/unit/spark_api_spec.rb +23 -0
- metadata +725 -0
|
@@ -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,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
|