restforce-db 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: df2303b0a1b785e92719f690b7d5ea5f6ec9ad09
4
- data.tar.gz: 1add9035c044ff8022562d547c1cab48c6c35299
3
+ metadata.gz: 0949316bfb922720bc820e65d71b71dcaf830c47
4
+ data.tar.gz: 270a8d50a820d805316b0f3a54bf5386f3fbfdc9
5
5
  SHA512:
6
- metadata.gz: e7de9ccdec2e054a494cb31d1caf00636e0893de35dd474aaa11862b42b2028be1fa8c50174a600594fa61e23284f7f0510946340c8f19f4bfbcb02d2a9a9c44
7
- data.tar.gz: dbae2200380469cd1ef662a9b05f3324309f37e6ac0a6615bb41cedb94b7e99b68e080207728eccdb45c0ec3245f4c8093196ecee698bc9523a6714b768264fc
6
+ metadata.gz: fc8d66f4c534f6b683e074c35c0f74ad88eb2ed085866c7f28c46ea2ff2ec06dcf641e2cb459bc364ebaa188d7d7bbf3860d648df939d6b30a1174f9ba342761
7
+ data.tar.gz: e12dab58a707f332f69bcf63f2fdfa5752128566567ac73eeaaa1fb1ffd335c1b87820669e4539e9b7257269c551ce9682f6ad40f134b484a5a8c047141df341
data/README.md CHANGED
@@ -145,6 +145,8 @@ Associations in `Restforce::DB` can be a little tricky, as they depend on your A
145
145
 
146
146
  If your Salesforce objects have parity with your ActiveRecord models, your association mappings will likely have parity, as well. But, as demonstrated above, you should define your association mappings based on your Salesforce schema.
147
147
 
148
+ Associations can be nested arbitrarily, so it's not an issue to have several layers of `passive` record associations -- they'll all be created on initial sync.
149
+
148
150
  ##### `belongs_to`
149
151
 
150
152
  This defines an association type in which the Lookup (i.e., foreign key) _is on the mapped Salesforce model_. In the example above, the `Restaurant__c` object type in Salesforce has two Lookup fields:
@@ -116,7 +116,7 @@ module Restforce
116
116
  #
117
117
  # Returns a Restforce::DB::Associations::Base.
118
118
  def association_for(reflection)
119
- inverse = reflection.send(:inverse_name)
119
+ inverse = inverse_association_name(reflection)
120
120
  Registry[reflection.klass].detect do |mapping|
121
121
  association = mapping.associations.detect { |a| a.name == inverse }
122
122
  break association if association
@@ -133,10 +133,33 @@ module Restforce
133
133
  database_record.association(name).scope
134
134
  end
135
135
 
136
+ # Internal: Construct all associated records for the passed database
137
+ # record, based on the mapping represented by the passed Salesforce
138
+ # instance.
139
+ #
140
+ # parent_record - The parent of the associated_record; an instance
141
+ # of an ActiveRecord::Base subclass.
142
+ # associated_record - The child of the parent_record; an instance of
143
+ # an ActiveRecord::Base subclass.
144
+ # salesforce_instance - A Restforce::DB::Instances::Salesforce which
145
+ # corresponds to the associated_record.
146
+ #
147
+ # Returns an Array of associated records.
148
+ def nested_records(parent_record, database_record, salesforce_instance)
149
+ # We need to identify _this_ association to prevent backtracking.
150
+ inverse = inverse_association_name(target_reflection(parent_record))
151
+ nested = salesforce_instance.mapping.associations.flat_map do |a|
152
+ next if a.name == inverse
153
+ a.build(database_record, salesforce_instance.record)
154
+ end
155
+
156
+ nested.compact
157
+ end
158
+
136
159
  # Internal: Get the Salesforce ID belonging to the associated record
137
160
  # for a supplied instance. Must be implemented per-association.
138
161
  #
139
- # instance - A Restforce::DB::Instances::Base
162
+ # instance - A Restforce::DB::Instances::Base.
140
163
  #
141
164
  # Returns a String.
142
165
  def associated_salesforce_id(_instance)
@@ -15,38 +15,32 @@ module Restforce
15
15
  # database_record - An instance of an ActiveRecord::Base subclass.
16
16
  # salesforce_record - A Hashie::Mash representing a Salesforce object.
17
17
  #
18
- # Returns the constructed association record.
18
+ # Returns an Array of constructed association records.
19
19
  def build(database_record, salesforce_record)
20
20
  lookups = {}
21
+ instances = []
21
22
 
22
23
  attributes = Registry[target_class(database_record)].inject({}) do |hash, mapping|
23
24
  lookup_id = salesforce_record[lookup_field(mapping, database_record)]
24
-
25
25
  lookups[mapping.lookup_column] = lookup_id
26
- hash.merge(attributes_for(mapping, lookup_id))
26
+
27
+ instance = mapping.salesforce_record_type.find(lookup_id)
28
+ instances << instance
29
+
30
+ hash.merge(mapping.convert(mapping.database_model, instance.attributes))
27
31
  end
28
32
 
29
33
  associated = association_scope(database_record).find_by(lookups)
30
34
  associated ||= database_record.association(name).build(lookups)
31
35
 
32
36
  associated.assign_attributes(attributes)
33
- associated
37
+ nested = instances.flat_map { |i| nested_records(database_record, associated, i) }
38
+
39
+ [associated, *nested]
34
40
  end
35
41
 
36
42
  private
37
43
 
38
- # Internal: Get a database-ready Hash of attributes from the Salesforce
39
- # record identified by the passed lookup ID.
40
- #
41
- # mapping - A Restforce::DB::Mapping.
42
- # lookup_id - A Lookup ID for the Salesforce record type in the Mapping.
43
- #
44
- # Returns a Hash.
45
- def attributes_for(mapping, lookup_id)
46
- salesforce_instance = mapping.salesforce_record_type.find(lookup_id)
47
- mapping.convert(mapping.database_model, salesforce_instance.attributes)
48
- end
49
-
50
44
  # Internal: Get the Salesforce ID belonging to the associated record
51
45
  # for a supplied instance. Must be implemented per-association.
52
46
  #
@@ -39,7 +39,7 @@ module Restforce
39
39
  # database_record - An instance of an ActiveRecord::Base subclass.
40
40
  # salesforce_instance - A Restforce::DB::Instances::Salesforce.
41
41
  #
42
- # Returns the constructed object.
42
+ # Returns an Array of constructed associated objects.
43
43
  def construct_for(database_record, salesforce_instance)
44
44
  mapping = salesforce_instance.mapping
45
45
  lookups = { mapping.lookup_column => salesforce_instance.id }
@@ -52,7 +52,7 @@ module Restforce
52
52
  )
53
53
 
54
54
  associated.assign_attributes(attributes)
55
- associated
55
+ [associated, *nested_records(database_record, associated, salesforce_instance)]
56
56
  end
57
57
 
58
58
  # Internal: Get the Salesforce ID belonging to the associated record
@@ -15,7 +15,7 @@ module Restforce
15
15
  # database_record - An instance of an ActiveRecord::Base subclass.
16
16
  # salesforce_record - A Hashie::Mash representing a Salesforce object.
17
17
  #
18
- # Returns the constructed association records.
18
+ # Returns an Array of constructed association records.
19
19
  def build(database_record, salesforce_record)
20
20
  target = target_mapping(database_record)
21
21
  lookup_id = "#{lookup_field(target, database_record)} = '#{salesforce_record.Id}'"
@@ -25,7 +25,7 @@ module Restforce
25
25
  records << construct_for(database_record, instance)
26
26
  end
27
27
 
28
- records
28
+ records.flatten
29
29
  end
30
30
 
31
31
  end
@@ -15,7 +15,7 @@ module Restforce
15
15
  # database_record - An instance of an ActiveRecord::Base subclass.
16
16
  # salesforce_record - A Hashie::Mash representing a Salesforce object.
17
17
  #
18
- # Returns the constructed association record.
18
+ # Returns an Array of constructed association records.
19
19
  def build(database_record, salesforce_record)
20
20
  target = target_mapping(database_record)
21
21
  query = "#{lookup_field(target, database_record)} = '#{salesforce_record.Id}'"
@@ -3,7 +3,7 @@ module Restforce
3
3
  # :nodoc:
4
4
  module DB
5
5
 
6
- VERSION = "1.2.0"
6
+ VERSION = "1.2.1"
7
7
 
8
8
  end
9
9
 
@@ -0,0 +1,389 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://<host>/services/oauth2/token
6
+ body:
7
+ encoding: US-ASCII
8
+ string: grant_type=password&client_id=<client_id>&client_secret=<client_secret>&username=<username>&password=<password><security_token>
9
+ headers:
10
+ User-Agent:
11
+ - Faraday v0.9.1
12
+ Content-Type:
13
+ - application/x-www-form-urlencoded
14
+ Accept-Encoding:
15
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
16
+ Accept:
17
+ - "*/*"
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Date:
24
+ - Thu, 16 Apr 2015 19:25:59 GMT
25
+ Set-Cookie:
26
+ - BrowserId=wRO9AsU-SU6xl-dn4F8y0w;Path=/;Domain=.salesforce.com;Expires=Mon,
27
+ 15-Jun-2015 19:25:59 GMT
28
+ Expires:
29
+ - Thu, 01 Jan 1970 00:00:00 GMT
30
+ Pragma:
31
+ - no-cache
32
+ Cache-Control:
33
+ - no-cache, no-store
34
+ Content-Type:
35
+ - application/json;charset=UTF-8
36
+ Transfer-Encoding:
37
+ - chunked
38
+ body:
39
+ encoding: ASCII-8BIT
40
+ string: '{"id":"https://login.salesforce.com/id/00D1a000000H3O9EAK/0051a000000UGT8AAO","issued_at":"1429212359162","token_type":"Bearer","instance_url":"https://<host>","signature":"GigH5zHmZ1IsG+SaU0QdxmWhrvcKdrCm9X2rf1EkiwM=","access_token":"00D1a000000H3O9!AQ4AQPSOXZt5DLhfr69D_LCpoJzz_hcsgL5M3__5XXvOQMahZ5HtzzBveYd9BpdCGejgkgFYX6_ZfJkTvkaXqAWbAEpg555q"}'
41
+ http_version:
42
+ recorded_at: Thu, 16 Apr 2015 19:25:58 GMT
43
+ - request:
44
+ method: post
45
+ uri: https://<host>/services/data/v26.0/sobjects/Contact
46
+ body:
47
+ encoding: UTF-8
48
+ string: '{"Email":"somebody@example.com","LastName":"Somebody"}'
49
+ headers:
50
+ User-Agent:
51
+ - Faraday v0.9.1
52
+ Content-Type:
53
+ - application/json
54
+ Authorization:
55
+ - OAuth 00D1a000000H3O9!AQ4AQPSOXZt5DLhfr69D_LCpoJzz_hcsgL5M3__5XXvOQMahZ5HtzzBveYd9BpdCGejgkgFYX6_ZfJkTvkaXqAWbAEpg555q
56
+ Accept-Encoding:
57
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
58
+ Accept:
59
+ - "*/*"
60
+ response:
61
+ status:
62
+ code: 201
63
+ message: Created
64
+ headers:
65
+ Date:
66
+ - Thu, 16 Apr 2015 19:26:00 GMT
67
+ Set-Cookie:
68
+ - BrowserId=tRsiGzYzSMyKS0RqJbPpCw;Path=/;Domain=.salesforce.com;Expires=Mon,
69
+ 15-Jun-2015 19:26:00 GMT
70
+ Expires:
71
+ - Thu, 01 Jan 1970 00:00:00 GMT
72
+ Sforce-Limit-Info:
73
+ - api-usage=1/15000
74
+ Location:
75
+ - "/services/data/v26.0/sobjects/Contact/0031a000002JQEpAAO"
76
+ Content-Type:
77
+ - application/json;charset=UTF-8
78
+ Transfer-Encoding:
79
+ - chunked
80
+ body:
81
+ encoding: ASCII-8BIT
82
+ string: '{"id":"0031a000002JQEpAAO","success":true,"errors":[]}'
83
+ http_version:
84
+ recorded_at: Thu, 16 Apr 2015 19:26:00 GMT
85
+ - request:
86
+ method: post
87
+ uri: https://<host>/services/data/v26.0/sobjects/CustomObject__c
88
+ body:
89
+ encoding: UTF-8
90
+ string: '{"Friend__c":"0031a000002JQEpAAO"}'
91
+ headers:
92
+ User-Agent:
93
+ - Faraday v0.9.1
94
+ Content-Type:
95
+ - application/json
96
+ Authorization:
97
+ - OAuth 00D1a000000H3O9!AQ4AQPSOXZt5DLhfr69D_LCpoJzz_hcsgL5M3__5XXvOQMahZ5HtzzBveYd9BpdCGejgkgFYX6_ZfJkTvkaXqAWbAEpg555q
98
+ Accept-Encoding:
99
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
100
+ Accept:
101
+ - "*/*"
102
+ response:
103
+ status:
104
+ code: 201
105
+ message: Created
106
+ headers:
107
+ Date:
108
+ - Thu, 16 Apr 2015 19:26:01 GMT
109
+ Set-Cookie:
110
+ - BrowserId=y52TPTAuRN6fW06z37GcTQ;Path=/;Domain=.salesforce.com;Expires=Mon,
111
+ 15-Jun-2015 19:26:01 GMT
112
+ Expires:
113
+ - Thu, 01 Jan 1970 00:00:00 GMT
114
+ Sforce-Limit-Info:
115
+ - api-usage=2/15000
116
+ Location:
117
+ - "/services/data/v26.0/sobjects/CustomObject__c/a001a000001LoG1AAK"
118
+ Content-Type:
119
+ - application/json;charset=UTF-8
120
+ Transfer-Encoding:
121
+ - chunked
122
+ body:
123
+ encoding: ASCII-8BIT
124
+ string: '{"id":"a001a000001LoG1AAK","success":true,"errors":[]}'
125
+ http_version:
126
+ recorded_at: Thu, 16 Apr 2015 19:26:01 GMT
127
+ - request:
128
+ method: post
129
+ uri: https://<host>/services/data/v26.0/sobjects/CustomObjectDetail__c
130
+ body:
131
+ encoding: UTF-8
132
+ string: '{"CustomObject__c":"a001a000001LoG1AAK"}'
133
+ headers:
134
+ User-Agent:
135
+ - Faraday v0.9.1
136
+ Content-Type:
137
+ - application/json
138
+ Authorization:
139
+ - OAuth 00D1a000000H3O9!AQ4AQPSOXZt5DLhfr69D_LCpoJzz_hcsgL5M3__5XXvOQMahZ5HtzzBveYd9BpdCGejgkgFYX6_ZfJkTvkaXqAWbAEpg555q
140
+ Accept-Encoding:
141
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
142
+ Accept:
143
+ - "*/*"
144
+ response:
145
+ status:
146
+ code: 201
147
+ message: Created
148
+ headers:
149
+ Date:
150
+ - Thu, 16 Apr 2015 19:26:02 GMT
151
+ Set-Cookie:
152
+ - BrowserId=cOGQueDpQNSZiHpqWIIf-g;Path=/;Domain=.salesforce.com;Expires=Mon,
153
+ 15-Jun-2015 19:26:02 GMT
154
+ Expires:
155
+ - Thu, 01 Jan 1970 00:00:00 GMT
156
+ Sforce-Limit-Info:
157
+ - api-usage=2/15000
158
+ Location:
159
+ - "/services/data/v26.0/sobjects/CustomObjectDetail__c/a011a000000hKfnAAE"
160
+ Content-Type:
161
+ - application/json;charset=UTF-8
162
+ Transfer-Encoding:
163
+ - chunked
164
+ body:
165
+ encoding: ASCII-8BIT
166
+ string: '{"id":"a011a000000hKfnAAE","success":true,"errors":[]}'
167
+ http_version:
168
+ recorded_at: Thu, 16 Apr 2015 19:26:02 GMT
169
+ - request:
170
+ method: get
171
+ uri: https://<host>/services/data/v26.0/query?q=select%20Id,%20SystemModstamp,%20Email%20from%20Contact%20where%20Id%20=%20%270031a000002JQEpAAO%27
172
+ body:
173
+ encoding: US-ASCII
174
+ string: ''
175
+ headers:
176
+ User-Agent:
177
+ - Faraday v0.9.1
178
+ Authorization:
179
+ - OAuth 00D1a000000H3O9!AQ4AQPSOXZt5DLhfr69D_LCpoJzz_hcsgL5M3__5XXvOQMahZ5HtzzBveYd9BpdCGejgkgFYX6_ZfJkTvkaXqAWbAEpg555q
180
+ Accept-Encoding:
181
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
182
+ Accept:
183
+ - "*/*"
184
+ response:
185
+ status:
186
+ code: 200
187
+ message: OK
188
+ headers:
189
+ Date:
190
+ - Thu, 16 Apr 2015 19:26:03 GMT
191
+ Set-Cookie:
192
+ - BrowserId=7tPGpHZJQguC6IxoBKUgaw;Path=/;Domain=.salesforce.com;Expires=Mon,
193
+ 15-Jun-2015 19:26:03 GMT
194
+ Expires:
195
+ - Thu, 01 Jan 1970 00:00:00 GMT
196
+ Sforce-Limit-Info:
197
+ - api-usage=2/15000
198
+ Content-Type:
199
+ - application/json;charset=UTF-8
200
+ Transfer-Encoding:
201
+ - chunked
202
+ body:
203
+ encoding: ASCII-8BIT
204
+ string: '{"totalSize":1,"done":true,"records":[{"attributes":{"type":"Contact","url":"/services/data/v26.0/sobjects/Contact/0031a000002JQEpAAO"},"Id":"0031a000002JQEpAAO","SystemModstamp":"2015-04-16T19:26:00.000+0000","Email":"somebody@example.com"}]}'
205
+ http_version:
206
+ recorded_at: Thu, 16 Apr 2015 19:26:03 GMT
207
+ - request:
208
+ method: get
209
+ uri: https://<host>/services/data/v26.0/query?q=select%20Id,%20SystemModstamp,%20Name,%20Example_Field__c,%20Friend__c%20from%20CustomObject__c%20where%20Friend__c%20=%20%270031a000002JQEpAAO%27
210
+ body:
211
+ encoding: US-ASCII
212
+ string: ''
213
+ headers:
214
+ User-Agent:
215
+ - Faraday v0.9.1
216
+ Authorization:
217
+ - OAuth 00D1a000000H3O9!AQ4AQPSOXZt5DLhfr69D_LCpoJzz_hcsgL5M3__5XXvOQMahZ5HtzzBveYd9BpdCGejgkgFYX6_ZfJkTvkaXqAWbAEpg555q
218
+ Accept-Encoding:
219
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
220
+ Accept:
221
+ - "*/*"
222
+ response:
223
+ status:
224
+ code: 200
225
+ message: OK
226
+ headers:
227
+ Date:
228
+ - Thu, 16 Apr 2015 19:26:04 GMT
229
+ Set-Cookie:
230
+ - BrowserId=0GBZp5d6Q4W9oYJ2xCoqPA;Path=/;Domain=.salesforce.com;Expires=Mon,
231
+ 15-Jun-2015 19:26:04 GMT
232
+ Expires:
233
+ - Thu, 01 Jan 1970 00:00:00 GMT
234
+ Sforce-Limit-Info:
235
+ - api-usage=2/15000
236
+ Content-Type:
237
+ - application/json;charset=UTF-8
238
+ Transfer-Encoding:
239
+ - chunked
240
+ body:
241
+ encoding: ASCII-8BIT
242
+ string: '{"totalSize":1,"done":true,"records":[{"attributes":{"type":"CustomObject__c","url":"/services/data/v26.0/sobjects/CustomObject__c/a001a000001LoG1AAK"},"Id":"a001a000001LoG1AAK","SystemModstamp":"2015-04-16T19:26:01.000+0000","Name":"a001a000001LoG1","Example_Field__c":null,"Friend__c":"0031a000002JQEpAAO"}]}'
243
+ http_version:
244
+ recorded_at: Thu, 16 Apr 2015 19:26:04 GMT
245
+ - request:
246
+ method: get
247
+ uri: https://<host>/services/data/v26.0/query?q=select%20Id,%20SystemModstamp,%20Name,%20CustomObject__c%20from%20CustomObjectDetail__c%20where%20CustomObject__c%20=%20%27a001a000001LoG1AAK%27
248
+ body:
249
+ encoding: US-ASCII
250
+ string: ''
251
+ headers:
252
+ User-Agent:
253
+ - Faraday v0.9.1
254
+ Authorization:
255
+ - OAuth 00D1a000000H3O9!AQ4AQPSOXZt5DLhfr69D_LCpoJzz_hcsgL5M3__5XXvOQMahZ5HtzzBveYd9BpdCGejgkgFYX6_ZfJkTvkaXqAWbAEpg555q
256
+ Accept-Encoding:
257
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
258
+ Accept:
259
+ - "*/*"
260
+ response:
261
+ status:
262
+ code: 200
263
+ message: OK
264
+ headers:
265
+ Date:
266
+ - Thu, 16 Apr 2015 19:26:05 GMT
267
+ Set-Cookie:
268
+ - BrowserId=RScD8n0tQn6Ka40Q_zj2pw;Path=/;Domain=.salesforce.com;Expires=Mon,
269
+ 15-Jun-2015 19:26:05 GMT
270
+ Expires:
271
+ - Thu, 01 Jan 1970 00:00:00 GMT
272
+ Sforce-Limit-Info:
273
+ - api-usage=2/15000
274
+ Content-Type:
275
+ - application/json;charset=UTF-8
276
+ Transfer-Encoding:
277
+ - chunked
278
+ body:
279
+ encoding: ASCII-8BIT
280
+ string: '{"totalSize":1,"done":true,"records":[{"attributes":{"type":"CustomObjectDetail__c","url":"/services/data/v26.0/sobjects/CustomObjectDetail__c/a011a000000hKfnAAE"},"Id":"a011a000000hKfnAAE","SystemModstamp":"2015-04-16T19:26:02.000+0000","Name":"a011a000000hKfn","CustomObject__c":"a001a000001LoG1AAK"}]}'
281
+ http_version:
282
+ recorded_at: Thu, 16 Apr 2015 19:26:05 GMT
283
+ - request:
284
+ method: delete
285
+ uri: https://<host>/services/data/v26.0/sobjects/Contact/0031a000002JQEpAAO
286
+ body:
287
+ encoding: US-ASCII
288
+ string: ''
289
+ headers:
290
+ User-Agent:
291
+ - Faraday v0.9.1
292
+ Authorization:
293
+ - OAuth 00D1a000000H3O9!AQ4AQPSOXZt5DLhfr69D_LCpoJzz_hcsgL5M3__5XXvOQMahZ5HtzzBveYd9BpdCGejgkgFYX6_ZfJkTvkaXqAWbAEpg555q
294
+ Accept-Encoding:
295
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
296
+ Accept:
297
+ - "*/*"
298
+ response:
299
+ status:
300
+ code: 204
301
+ message: No Content
302
+ headers:
303
+ Date:
304
+ - Thu, 16 Apr 2015 19:26:06 GMT
305
+ Set-Cookie:
306
+ - BrowserId=NEH4ouAkSbmwL7ARdAXr7Q;Path=/;Domain=.salesforce.com;Expires=Mon,
307
+ 15-Jun-2015 19:26:06 GMT
308
+ Expires:
309
+ - Thu, 01 Jan 1970 00:00:00 GMT
310
+ Sforce-Limit-Info:
311
+ - api-usage=3/15000
312
+ body:
313
+ encoding: UTF-8
314
+ string: ''
315
+ http_version:
316
+ recorded_at: Thu, 16 Apr 2015 19:26:07 GMT
317
+ - request:
318
+ method: delete
319
+ uri: https://<host>/services/data/v26.0/sobjects/CustomObject__c/a001a000001LoG1AAK
320
+ body:
321
+ encoding: US-ASCII
322
+ string: ''
323
+ headers:
324
+ User-Agent:
325
+ - Faraday v0.9.1
326
+ Authorization:
327
+ - OAuth 00D1a000000H3O9!AQ4AQPSOXZt5DLhfr69D_LCpoJzz_hcsgL5M3__5XXvOQMahZ5HtzzBveYd9BpdCGejgkgFYX6_ZfJkTvkaXqAWbAEpg555q
328
+ Accept-Encoding:
329
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
330
+ Accept:
331
+ - "*/*"
332
+ response:
333
+ status:
334
+ code: 204
335
+ message: No Content
336
+ headers:
337
+ Date:
338
+ - Thu, 16 Apr 2015 19:26:08 GMT
339
+ Set-Cookie:
340
+ - BrowserId=JR1qDjLqR9Kbaw5KhQpGjA;Path=/;Domain=.salesforce.com;Expires=Mon,
341
+ 15-Jun-2015 19:26:08 GMT
342
+ Expires:
343
+ - Thu, 01 Jan 1970 00:00:00 GMT
344
+ Sforce-Limit-Info:
345
+ - api-usage=4/15000
346
+ body:
347
+ encoding: UTF-8
348
+ string: ''
349
+ http_version:
350
+ recorded_at: Thu, 16 Apr 2015 19:26:08 GMT
351
+ - request:
352
+ method: delete
353
+ uri: https://<host>/services/data/v26.0/sobjects/CustomObjectDetail__c/a011a000000hKfnAAE
354
+ body:
355
+ encoding: US-ASCII
356
+ string: ''
357
+ headers:
358
+ User-Agent:
359
+ - Faraday v0.9.1
360
+ Authorization:
361
+ - OAuth 00D1a000000H3O9!AQ4AQPSOXZt5DLhfr69D_LCpoJzz_hcsgL5M3__5XXvOQMahZ5HtzzBveYd9BpdCGejgkgFYX6_ZfJkTvkaXqAWbAEpg555q
362
+ Accept-Encoding:
363
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
364
+ Accept:
365
+ - "*/*"
366
+ response:
367
+ status:
368
+ code: 404
369
+ message: Not Found
370
+ headers:
371
+ Date:
372
+ - Thu, 16 Apr 2015 19:26:09 GMT
373
+ Set-Cookie:
374
+ - BrowserId=W0ivBZ6NTiSQA8J2bL7lgw;Path=/;Domain=.salesforce.com;Expires=Mon,
375
+ 15-Jun-2015 19:26:09 GMT
376
+ Expires:
377
+ - Thu, 01 Jan 1970 00:00:00 GMT
378
+ Sforce-Limit-Info:
379
+ - api-usage=4/15000
380
+ Content-Type:
381
+ - application/json;charset=UTF-8
382
+ Transfer-Encoding:
383
+ - chunked
384
+ body:
385
+ encoding: UTF-8
386
+ string: '[{"message":"entity is deleted","errorCode":"ENTITY_IS_DELETED","fields":[]}]'
387
+ http_version:
388
+ recorded_at: Thu, 16 Apr 2015 19:26:09 GMT
389
+ recorded_with: VCR 2.9.3
@@ -69,7 +69,7 @@ describe Restforce::DB::Associations::BelongsTo do
69
69
  describe "#build" do
70
70
  let(:database_record) { CustomObject.new }
71
71
  let(:salesforce_record) { mapping.salesforce_record_type.find(object_salesforce_id).record }
72
- let(:associated) { association.build(database_record, salesforce_record) }
72
+ let(:associated) { association.build(database_record, salesforce_record).first }
73
73
 
74
74
  it "returns an associated record, populated with the Salesforce attributes" do
75
75
  expect(associated.custom_object).to_equal database_record
@@ -71,8 +71,48 @@ describe Restforce::DB::Associations::HasOne do
71
71
  let(:associated) { association.build(database_record, salesforce_record) }
72
72
 
73
73
  it "returns an associated record, populated with the Salesforce attributes" do
74
- expect(associated.user).to_equal database_record
75
- expect(associated.salesforce_id).to_equal object_salesforce_id
74
+ object = associated.first
75
+ expect(object.user).to_equal database_record
76
+ expect(object.salesforce_id).to_equal object_salesforce_id
77
+ end
78
+
79
+ describe "and a nested association on the associated mapping" do
80
+ let(:nested_mapping) do
81
+ Restforce::DB::Mapping.new(Detail, "CustomObjectDetail__c").tap do |m|
82
+ m.fields = { name: "Name" }
83
+ m.associations << Restforce::DB::Associations::BelongsTo.new(
84
+ :custom_object,
85
+ through: "CustomObject__c",
86
+ )
87
+ end
88
+ end
89
+ let(:nested_association) do
90
+ Restforce::DB::Associations::HasMany.new(:details, through: "CustomObject__c")
91
+ end
92
+ let(:detail_salesforce_id) do
93
+ Salesforce.create!(
94
+ nested_mapping.salesforce_model,
95
+ "CustomObject__c" => object_salesforce_id,
96
+ )
97
+ end
98
+
99
+ before do
100
+ Restforce::DB::Registry << nested_mapping
101
+ mapping.associations << nested_association
102
+ end
103
+
104
+ it "recursively builds all associations" do
105
+ detail_salesforce_id
106
+ expect(associated.length).to_equal 2
107
+
108
+ object, detail = associated
109
+
110
+ expect(object).to_be_instance_of CustomObject
111
+ expect(object.user).to_equal database_record
112
+
113
+ expect(detail).to_be_instance_of Detail
114
+ expect(detail.custom_object).to_equal object
115
+ end
76
116
  end
77
117
  end
78
118
  end
@@ -60,9 +60,10 @@ describe Restforce::DB::RecordTypes::ActiveRecord do
60
60
 
61
61
  # Stub out the `#find` method on the record type
62
62
  def salesforce_record_type.find(id)
63
- Struct.new(:id, :last_update, :attributes).new(
63
+ Struct.new(:id, :last_update, :mapping, :attributes).new(
64
64
  id,
65
65
  Time.now,
66
+ Restforce::DB::Registry[User].first,
66
67
  email: "somebody@example.com",
67
68
  )
68
69
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restforce-db
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Horner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-04-14 00:00:00.000000000 Z
11
+ date: 2015-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -246,6 +246,7 @@ files:
246
246
  - test/cassettes/Restforce_DB_Associations_HasMany/with_an_inverse_mapping/_build/builds_a_number_of_associated_records_from_the_data_in_Salesforce.yml
247
247
  - test/cassettes/Restforce_DB_Associations_HasMany/with_an_inverse_mapping/_synced_for_/when_a_matching_associated_record_has_been_synchronized/returns_true.yml
248
248
  - test/cassettes/Restforce_DB_Associations_HasMany/with_an_inverse_mapping/_synced_for_/when_no_matching_associated_record_has_been_synchronized/returns_false.yml
249
+ - test/cassettes/Restforce_DB_Associations_HasOne/with_an_inverse_mapping/_build/and_a_nested_association_on_the_associated_mapping/recursively_builds_all_associations.yml
249
250
  - test/cassettes/Restforce_DB_Associations_HasOne/with_an_inverse_mapping/_build/returns_an_associated_record_populated_with_the_Salesforce_attributes.yml
250
251
  - test/cassettes/Restforce_DB_Associations_HasOne/with_an_inverse_mapping/_synced_for_/when_a_matching_associated_record_has_been_synchronized/returns_true.yml
251
252
  - test/cassettes/Restforce_DB_Associations_HasOne/with_an_inverse_mapping/_synced_for_/when_no_matching_associated_record_has_been_synchronized/returns_false.yml