spark_api 1.2.1 → 1.3.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 (70) hide show
  1. data/History.txt +12 -0
  2. data/README.md +7 -0
  3. data/Rakefile +1 -0
  4. data/VERSION +1 -1
  5. data/lib/spark_api/authentication.rb +0 -3
  6. data/lib/spark_api/authentication/oauth2.rb +1 -1
  7. data/lib/spark_api/configuration.rb +6 -1
  8. data/lib/spark_api/models.rb +6 -2
  9. data/lib/spark_api/models/activity.rb +10 -0
  10. data/lib/spark_api/models/base.rb +25 -5
  11. data/lib/spark_api/models/comment.rb +9 -0
  12. data/lib/spark_api/models/concerns/destroyable.rb +1 -1
  13. data/lib/spark_api/models/concerns/savable.rb +3 -6
  14. data/lib/spark_api/models/contact.rb +38 -1
  15. data/lib/spark_api/models/dirty.rb +1 -1
  16. data/lib/spark_api/models/fields.rb +12 -0
  17. data/lib/spark_api/models/listing_cart.rb +6 -34
  18. data/lib/spark_api/models/portal.rb +37 -0
  19. data/lib/spark_api/models/saved_search.rb +36 -0
  20. data/lib/spark_api/models/vow_account.rb +44 -0
  21. data/lib/spark_api/request.rb +1 -1
  22. data/spec/fixtures/activities/get.json +22 -0
  23. data/spec/fixtures/base.json +2 -2
  24. data/spec/fixtures/comments/get.json +32 -0
  25. data/spec/fixtures/comments/new.json +7 -0
  26. data/spec/fixtures/comments/post.json +19 -0
  27. data/spec/fixtures/contacts/my.json +1 -0
  28. data/spec/fixtures/contacts/vow_accounts/edit.json +5 -0
  29. data/spec/fixtures/contacts/vow_accounts/get.json +15 -0
  30. data/spec/fixtures/contacts/vow_accounts/new.json +12 -0
  31. data/spec/fixtures/contacts/vow_accounts/post.json +10 -0
  32. data/spec/fixtures/fields/order.json +22 -0
  33. data/spec/fixtures/fields/order_a.json +39 -0
  34. data/spec/fixtures/portal/disable.json +5 -0
  35. data/spec/fixtures/portal/enable.json +5 -0
  36. data/spec/fixtures/portal/my.json +15 -0
  37. data/spec/fixtures/portal/my_non_existant.json +6 -0
  38. data/spec/fixtures/portal/new.json +7 -0
  39. data/spec/fixtures/portal/post.json +10 -0
  40. data/spec/fixtures/saved_searches/get.json +4 -1
  41. data/spec/spec_helper.rb +11 -7
  42. data/spec/unit/spark_api/authentication/oauth2_impl/faraday_middleware_spec.rb +2 -2
  43. data/spec/unit/spark_api/authentication/oauth2_impl/grant_type_base_spec.rb +1 -2
  44. data/spec/unit/spark_api/authentication/oauth2_spec.rb +3 -4
  45. data/spec/unit/spark_api/models/activity_spec.rb +29 -0
  46. data/spec/unit/spark_api/models/base_spec.rb +23 -0
  47. data/spec/unit/spark_api/models/concerns/destroyable_spec.rb +1 -1
  48. data/spec/unit/spark_api/models/concerns/savable_spec.rb +8 -4
  49. data/spec/unit/spark_api/models/contact_spec.rb +144 -21
  50. data/spec/unit/spark_api/models/fields_spec.rb +56 -0
  51. data/spec/unit/spark_api/models/listing_cart_spec.rb +1 -1
  52. data/spec/unit/spark_api/models/portal_spec.rb +50 -0
  53. data/spec/unit/spark_api/models/saved_search_spec.rb +60 -0
  54. data/spec/unit/spark_api/models/shared_listing_spec.rb +1 -1
  55. data/spec/unit/spark_api/models/vow_account_spec.rb +64 -0
  56. data/spec/unit/spark_api/request_spec.rb +1 -1
  57. data/spec/unit/spark_api_spec.rb +1 -1
  58. metadata +362 -360
  59. data/lib/spark_api/models/subscription.rb +0 -52
  60. data/spec/fixtures/subscriptions/get.json +0 -19
  61. data/spec/fixtures/subscriptions/new.json +0 -13
  62. data/spec/fixtures/subscriptions/post.json +0 -10
  63. data/spec/fixtures/subscriptions/put.json +0 -12
  64. data/spec/fixtures/subscriptions/subscribe.json +0 -5
  65. data/spec/fixtures/subscriptions/update.json +0 -6
  66. data/spec/json_hash_test_support.rb +0 -251
  67. data/spec/json_helper.rb +0 -76
  68. data/spec/mock_helper.rb +0 -132
  69. data/spec/oauth2_helper.rb +0 -70
  70. data/spec/unit/spark_api/models/subscription_spec.rb +0 -106
@@ -1,52 +0,0 @@
1
- module SparkApi
2
- module Models
3
-
4
- class Subscription < Base
5
- extend Finders
6
- include Concerns::Savable,
7
- Concerns::Destroyable
8
-
9
- self.element_name = "subscriptions"
10
-
11
- # list subscribers (private role)
12
- def subscribers
13
- return {} unless persisted?
14
- results = connection.get("#{self.class.path}/#{@attributes["Id"]}/subscribers")
15
- @attributes['RecipientIds'] = results.first['RecipientIds']
16
- results
17
- end
18
-
19
- # subscribe/unsubscribe contact (private role)
20
- [:subscribe, :unsubscribe].each do |action|
21
- method = (action == :subscribe ? :put : :delete)
22
- define_method(action) do |contact|
23
- return false unless persisted?
24
- self.errors = []
25
- contact_id = contact.is_a?(Contact) ? contact.Id : contact
26
- begin
27
- connection.send(method, "#{self.class.path}/#{@attributes["Id"]}/subscribers/#{contact_id}")
28
- rescue BadResourceRequest, NotFound => e
29
- self.errors << { :code => e.code, :message => e.message }
30
- SparkApi.logger.error("Failed to #{action} contact #{contact}: #{e.message}")
31
- return false
32
- end
33
- update_recipients(action, contact_id)
34
- true
35
- end
36
- end
37
-
38
- private
39
-
40
- def update_recipients(method, contact_id)
41
- @attributes['RecipientIds'] = [] if @attributes['RecipientIds'].nil?
42
- if method == :subscribe
43
- @attributes['RecipientIds'] << contact_id
44
- else
45
- @attributes['RecipientIds'].delete contact_id
46
- end
47
- end
48
-
49
- end
50
-
51
- end
52
- end
@@ -1,19 +0,0 @@
1
- {
2
- "D": {
3
- "Success": true,
4
- "Results": [
5
- {
6
- "Id":"20101230223226074204000000",
7
- "ResourceUri":"/v1/contacts/20101230223226074204000000",
8
- "Name":"First subscription",
9
- "RecipientIds": ["20101230223226074307000000"]
10
- },
11
- {
12
- "Id":"20101230223226074205000000",
13
- "ResourceUri":"/v1/contacts/20101230223226074205000000",
14
- "Name":"Second subscription",
15
- "RecipientIds": []
16
- }
17
- ]
18
- }
19
- }
@@ -1,13 +0,0 @@
1
- {
2
- "D": {
3
- "Subscriptions": [
4
- {
5
- "Name":"A new subscription name",
6
- "SearchId":"20101230223226074204000000",
7
- "RecipientIds": ["20101230223226074204000000"],
8
- "Subject": "my subject",
9
- "Message": "my message"
10
- }
11
- ]
12
- }
13
- }
@@ -1,10 +0,0 @@
1
- {
2
- "D": {
3
- "Success": true,
4
- "Results": [
5
- {
6
- "ResourceUri":"/v1/subscriptions/20101230223226074204000000"
7
- }
8
- ]
9
- }
10
- }
@@ -1,12 +0,0 @@
1
- {
2
- "D": {
3
- "Success": true,
4
- "Results": [
5
- {
6
- "Id":"20101230223226074204000000",
7
- "ResourceUri":"/v1/contacts/20101230223226074204000000",
8
- "Name":"A new subscription name"
9
- }
10
- ]
11
- }
12
- }
@@ -1,5 +0,0 @@
1
- {
2
- "D":{
3
- "Success":true
4
- }
5
- }
@@ -1,6 +0,0 @@
1
- {
2
- "D": {
3
- "Name":"A new subscription name"
4
- }
5
- }
6
-
@@ -1,251 +0,0 @@
1
- # Several workarounds to get our test setup working in multiple ruby environments credit is
2
- # attributed to all the projects that found solutions to these test headaches!
3
-
4
-
5
- module SparkApiTest
6
- # directly taken from Rails 3.1's OrderedHash
7
- # see https://github.com/rails/rails/blob/master/activesupport/lib/active_support/ordered_hash.rb
8
-
9
- # The order of iteration over hashes in Ruby 1.8 is undefined. For example, you do not know the
10
- # order in which +keys+ will return keys, or +each+ yield pairs. <tt>ActiveSupport::OrderedHash</tt>
11
- # implements a hash that preserves insertion order, as in Ruby 1.9:
12
- #
13
- # oh = ActiveSupport::OrderedHash.new
14
- # oh[:a] = 1
15
- # oh[:b] = 2
16
- # oh.keys # => [:a, :b], this order is guaranteed
17
- #
18
- # <tt>ActiveSupport::OrderedHash</tt> is namespaced to prevent conflicts with other implementations.
19
- class OrderedHash < ::Hash #:nodoc:
20
- def to_yaml_type
21
- "!tag:yaml.org,2002:omap"
22
- end
23
-
24
- def encode_with(coder)
25
- coder.represent_seq '!omap', map { |k,v| { k => v } }
26
- end
27
-
28
- def to_yaml(opts = {})
29
- if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck?
30
- return super
31
- end
32
-
33
- YAML.quick_emit(self, opts) do |out|
34
- out.seq(taguri) do |seq|
35
- each do |k, v|
36
- seq.add(k => v)
37
- end
38
- end
39
- end
40
- end
41
-
42
- def nested_under_indifferent_access
43
- self
44
- end
45
-
46
- # Hash is ordered in Ruby 1.9!
47
- if RUBY_VERSION < '1.9'
48
-
49
- # In MRI the Hash class is core and written in C. In particular, methods are
50
- # programmed with explicit C function calls and polymorphism is not honored.
51
- #
52
- # For example, []= is crucial in this implementation to maintain the @keys
53
- # array but hash.c invokes rb_hash_aset() originally. This prevents method
54
- # reuse through inheritance and forces us to reimplement stuff.
55
- #
56
- # For instance, we cannot use the inherited #merge! because albeit the algorithm
57
- # itself would work, our []= is not being called at all by the C code.
58
-
59
- def initialize(*args, &block)
60
- super
61
- @keys = []
62
- end
63
-
64
- def self.[](*args)
65
- ordered_hash = new
66
-
67
- if (args.length == 1 && args.first.is_a?(Array))
68
- args.first.each do |key_value_pair|
69
- next unless (key_value_pair.is_a?(Array))
70
- ordered_hash[key_value_pair[0]] = key_value_pair[1]
71
- end
72
-
73
- return ordered_hash
74
- end
75
-
76
- unless (args.size % 2 == 0)
77
- raise ArgumentError.new("odd number of arguments for Hash")
78
- end
79
-
80
- args.each_with_index do |val, ind|
81
- next if (ind % 2 != 0)
82
- ordered_hash[val] = args[ind + 1]
83
- end
84
-
85
- ordered_hash
86
- end
87
-
88
- def initialize_copy(other)
89
- super
90
- # make a deep copy of keys
91
- @keys = other.keys
92
- end
93
-
94
- def []=(key, value)
95
- @keys << key unless has_key?(key)
96
- super
97
- end
98
-
99
- def delete(key)
100
- if has_key? key
101
- index = @keys.index(key)
102
- @keys.delete_at index
103
- end
104
- super
105
- end
106
-
107
- def delete_if
108
- super
109
- sync_keys!
110
- self
111
- end
112
-
113
- def reject!
114
- super
115
- sync_keys!
116
- self
117
- end
118
-
119
- def reject(&block)
120
- dup.reject!(&block)
121
- end
122
-
123
- def keys
124
- @keys.dup
125
- end
126
-
127
- def values
128
- @keys.collect { |key| self[key] }
129
- end
130
-
131
- def to_hash
132
- self
133
- end
134
-
135
- def to_a
136
- @keys.map { |key| [ key, self[key] ] }
137
- end
138
-
139
- def each_key
140
- return to_enum(:each_key) unless block_given?
141
- @keys.each { |key| yield key }
142
- self
143
- end
144
-
145
- def each_value
146
- return to_enum(:each_value) unless block_given?
147
- @keys.each { |key| yield self[key]}
148
- self
149
- end
150
-
151
- def each
152
- return to_enum(:each) unless block_given?
153
- @keys.each {|key| yield [key, self[key]]}
154
- self
155
- end
156
-
157
- alias_method :each_pair, :each
158
-
159
- alias_method :select, :find_all
160
-
161
- def clear
162
- super
163
- @keys.clear
164
- self
165
- end
166
-
167
- def shift
168
- k = @keys.first
169
- v = delete(k)
170
- [k, v]
171
- end
172
-
173
- def merge!(other_hash)
174
- if block_given?
175
- other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v }
176
- else
177
- other_hash.each { |k, v| self[k] = v }
178
- end
179
- self
180
- end
181
-
182
- alias_method :update, :merge!
183
-
184
- def merge(other_hash, &block)
185
- dup.merge!(other_hash, &block)
186
- end
187
-
188
- # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
189
- def replace(other)
190
- super
191
- @keys = other.keys
192
- self
193
- end
194
-
195
- def invert
196
- OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}]
197
- end
198
-
199
- private
200
- def sync_keys!
201
- @keys.delete_if {|k| !has_key?(k)}
202
- end
203
- end
204
- end
205
- end
206
-
207
- # Originally based on a fix found in Koala, a facebook client gem that has compatibile licensing.
208
- # See: https://raw.github.com/arsduo/koala/master/spec/support/json_testing_fix.rb
209
- #
210
- # when testing across Ruby versions, we found that JSON string creation inconsistently ordered keys
211
- # which is a problem because our mock testing service ultimately matches strings to see if requests are mocked
212
- # this fix solves that problem by ensuring all hashes are created with a consistent key order every time
213
- module MultiJson
214
-
215
- class << self
216
- def dump_with_ordering(object)
217
- # if it's a hash, recreate it with k/v pairs inserted in sorted-by-key order
218
- # (for some reason, REE fails if we don't assign the ternary result as a local variable
219
- # separately from calling encode_original)
220
- dump_original(sort_object(object))
221
- end
222
-
223
- alias_method :dump_original, :dump
224
- alias_method :dump, :dump_with_ordering
225
-
226
- def load_with_ordering(string)
227
- sort_object(load_original(string))
228
- end
229
-
230
- alias_method :load_original, :load
231
- alias_method :load, :load_with_ordering
232
-
233
- private
234
-
235
- def sort_object(object)
236
- if object.is_a?(Hash)
237
- sort_hash(object)
238
- elsif object.is_a?(Array)
239
- object.collect {|item| item.is_a?(Hash) ? sort_hash(item) : item}
240
- else
241
- object
242
- end
243
- end
244
-
245
- def sort_hash(unsorted_hash)
246
- sorted_hash = SparkApiTest::OrderedHash.new(sorted_hash)
247
- unsorted_hash.keys.sort {|a, b| a.to_s <=> b.to_s}.inject(sorted_hash) {|hash, k| hash[k] = unsorted_hash[k]; hash}
248
- end
249
- end
250
- end
251
-
@@ -1,76 +0,0 @@
1
- class ListingJson
2
- @counter = 0
3
- def self.next_tech_id()
4
- "#{@counter + 20110101000000000000}000000"
5
- end
6
- def self.next_list_id()
7
- "06-#{@counter + 1000}"
8
- end
9
- def self.bump()
10
- @counter += 1
11
- end
12
-
13
- def self.create
14
- bump
15
- json = <<-JSON
16
- {
17
- "ResourceUri": "/v1/listings/#{next_tech_id}",
18
- "StandardFields": #{standard_fields},
19
- "Id": "#{next_tech_id}"
20
- }
21
- JSON
22
-
23
- end
24
-
25
- def self.create_all(count = 1)
26
- listings = []
27
- count.times { listings << create }
28
- json = listings * ","
29
- end
30
-
31
- private
32
- def self.standard_fields
33
- json = <<-JSON
34
- {
35
- "StreetNumber": "7298",
36
- "Longitude": "-116.3237",
37
- "City": "Bonners Ferry",
38
- "ListingId": "#{next_list_id}",
39
- "PublicRemarks": "Afforadable home in town close to hospital,yet quiet country like setting. Good views. Must see",
40
- "BuildingAreaTotal": 924.0,
41
- "YearBuilt": 1977,
42
- "StreetName": "BIRCH",
43
- "ListPrice": 50000.0,
44
- "PostalCode": 83805,
45
- "Latitude": "48.7001",
46
- "BathsThreeQuarter": null,
47
- "BathsFull": 1.0,
48
- "BathsTotal": 1.0,
49
- "StateOrProvince": "ID",
50
- "PropertyType": "A",
51
- "StreetAdditionalInfo": null,
52
- "StreetDirPrefix": null,
53
- "BedsTotal": 3,
54
- "StreetDirSuffix": null,
55
- "ListingKey": "#{next_tech_id}",
56
- "ListOfficeName": "Century 21 On The Lake",
57
- "BathsHalf": null,
58
- "ModificationTimestamp": "2010-11-22T20:47:21Z",
59
- "CountyOrParish": "Boundary"
60
- }
61
- JSON
62
- end
63
-
64
- end
65
-
66
- def paginate_json(current_page = 1)
67
- json = <<-JSON
68
- "Pagination": {
69
- "TotalRows": 38,
70
- "PageSize": 10,
71
- "TotalPages": 4,
72
- "CurrentPage": #{current_page}
73
- }
74
- JSON
75
- end
76
-