Skip to content

Commit fbb97fc

Browse files
adam-bilyeaJames Haig
andauthored
Databasedotcom to Restforce (#32)
* Databasedotcom changed over to the Restorce gem. Haven't been able to fully test yet BUT that'll be better checked when the website is updated * Removed byebug * PR comments * remove delayed_job enqueue call (#33) * remove delayed_job enqueue call * .to_json attributes to allow activesupport to be able to serialize any datatype * Some PR changes and updates to the readme file Co-authored-by: James Haig <jhaig@infotech.com>
1 parent 03cf2a8 commit fbb97fc

File tree

7 files changed

+65
-56
lines changed

7 files changed

+65
-56
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@
2020
# Version 3.2.0
2121
* Adds support for Rails 5
2222
* Added ability to specify readonly fields for salesforce syncable models so those fields don't try to sync back to Salesforce
23-
* WebID syncing fixed
23+
* WebID syncing fixed
24+
# Version 4.0.0
25+
* Changed Databasedotcom over to Restforce

README.md

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# SalesforceArSync
22

33
SalesforceARSync allows you to sync models and fields with Salesforce through a combination of
4-
Outbound Messaging, SOAP and databasedotcom.
4+
Outbound Messaging, SOAP and restforce.
55

66
## Installation
77

@@ -10,8 +10,7 @@ Outbound Messaging, SOAP and databasedotcom.
1010
* Rails ~> 4.0
1111
* Salesforce.com instance
1212
* [Have your 18 character organization id ready](#finding-your-18-character-organization-id)
13-
* databasedotcom gem >= 1.3 installed and configured [see below](#databasedotcom)
14-
* delayed_job gem >= 3.0 installed and configured
13+
* restforce gem >= 5.0.4 installed and configured [see below](#restforce)
1514

1615
### Salesforce Setup
1716

@@ -38,14 +37,15 @@ Select the fields you wish to sync (Id and SystemModstamp are required).
3837

3938
*You need to do this for each object/model you want to sync.
4039

41-
### databasedotcom
40+
### restforce
4241

43-
Before using the salesforce_ar_sync gem you must ensure you have the databasedotcom gem installed and configured
42+
Before using the salesforce_ar_sync gem you must ensure you have the restforce gem installed and configured
4443
properly. Make sure each of the models you wish to sync are materialized.
4544

4645
````ruby
47-
$sf_client = Databasedotcom::Client.new("config/databasedotcom.yml")
48-
$sf_client.authenticate :username => <username>, :password => <password>
46+
# Note: You can name the config to anything you want
47+
$sf_client = Restforce::Client.new("config/salesforce.yml")
48+
$sf_client.authenticate username: <username>, password: <password>
4949

5050
module SalesforceArSync::SalesforceSync
5151
SF_CLIENT = $sf_client
@@ -94,7 +94,7 @@ Next you will need to tell the gem which models are syncable by adding _salesfor
9494
and specifying which attributes you would like to sync.
9595

9696
````ruby
97-
salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name}
97+
salesforce_syncable :sync_attributes => {FirstName: :first_name, LastName: :last_name}
9898
````
9999

100100
The first parameter in the _:sync_attributes_ hash is the Salesforce field name and the second is the model attribute
@@ -177,7 +177,7 @@ The model can have several options set:
177177
Model level option to enable disable the sync, defaults to true.
178178

179179
````ruby
180-
:salesforce_sync_enabled => false
180+
salesforce_sync_enabled: false
181181
````
182182

183183
#### sync_attributes
@@ -187,15 +187,15 @@ value, you should also implement a corresponding my_method_changed? to return if
187187
it will always be synced.
188188

189189
````ruby
190-
:sync_attributes => { :Email => :login, :FirstName => :first_name, :LastName => :last_name }
190+
sync_attributes: { Email: :login, FirstName: :first_name, LastName: :last_name }
191191
````
192192

193193
#### async_attributes
194194
An array of Salesforce attributes which should be synced asynchronously, defaults to an empty array. When an object is saved and only attributes contained in this array, the save to Salesforce will be queued and processed asyncronously.
195195
Use this carefully, nothing is done to ensure data integrity, if multiple jobs are queued for a single object there is no way to guarentee that they are processed in order, or that the save to Salesforce will succeed.
196196

197197
````ruby
198-
:async_attributes => ["Last_Login__c", "Login_Count__c"]
198+
async_attributes: ['Last_Login__c', 'Login_Count__c']
199199
````
200200

201201
Note: The model will fall back to synchronous sync if non-synchronous attributes are changed along with async
@@ -205,35 +205,35 @@ attributes
205205
A hash of default attributes that should be used when we are creating a new record, defaults to empty hash.
206206

207207
````ruby
208-
:default_attributes_for_create => {:password_change_required => true}
208+
default_attributes_for_create: { password_change_required: true }
209209
````
210210

211211
#### salesforce_id_attribute_name
212212
The "Id" attribute of the corresponding Salesforce object, defaults to _Id_.
213213

214214
````ruby
215-
:salesforce_id_attribute_name => :Id
215+
salesforce_id_attribute_name: :Id
216216
````
217217

218218
#### web_id_attribute_name
219219
The field name of the web id attribute in the Salesforce Object, defaults to _WebId__c_
220220

221221
````ruby
222-
:web_id_attribute_name => :WebId__c
222+
web_id_attribute_name: :WebId__c
223223
````
224224

225225
#### activerecord_web_id_attribute_name
226226
The field name of the web id attribute in the Active Record Object, defaults to id
227227

228228
````ruby
229-
:activerecord_web_id_attribute_name => :id
229+
activerecord_web_id_attribute_name: :id
230230
````
231231

232232
#### salesforce_sync_web_id
233233
Enable or disable sync of the web id, defaults to false. Use this if you have a need for the id field of the ActiveRecord model to by synced to Salesforce.
234234

235235
````ruby
236-
:salesforce_sync_web_id => false
236+
salesforce_sync_web_id: false
237237
````
238238

239239
#### additional_lookup_fields
@@ -250,37 +250,37 @@ different web object. Defaults to the model name. This would generally be used i
250250
into a larger SF object like Contact.
251251

252252
````ruby
253-
:web_class_name => 'Contact',
253+
web_class_name: 'Contact',
254254
````
255255

256256
#### salesforce_object_name
257257
Optionally holds the name of a method which will return the name of the Salesforce object to sync to, defaults to nil.
258258

259259
````ruby
260-
:salesforce_object_name => :salesforce_object_name_method_name
260+
salesforce_object_name: :salesforce_object_name_method_name
261261
````
262262

263263
#### except
264264
Optionally holds the name of a method which can contain logic to determine if a record should be synced on save. If no
265265
method is given then only the salesforce_skip_sync attribute is used. Defaults to nil.
266266

267267
````ruby
268-
:except => :except_method_name
268+
except: :except_method_name
269269
````
270270

271271
#### save_method
272272
Optionally holds the name of a method which contains custom logic for saving on sync. Defaults to `ActiveRecord::Base#save!`
273273

274274
````ruby
275-
:save_method => :save_method_name
275+
save_method: :save_method_name
276276
````
277277

278278
#### unscoped_updates
279279
Enable bypassing the default scope when searching for records to update. This is useful when using a
280280
soft deletion strategy that can respect SF undeletion.
281281

282282
````ruby
283-
:unscoped_updates => true
283+
unscoped_updates: true
284284
````
285285

286286
#### readonly_fields
@@ -319,7 +319,7 @@ class Contact < ActiveRecord::Base
319319
attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
320320
attr_accessor :first_name, :last_name, :phone, :email
321321

322-
salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email}
322+
salesforce_syncable sync_attributes: { FirstName: :first_name, LastName: :last_name, Phone: :phone, Email: :email }
323323
end
324324
```
325325

@@ -330,8 +330,8 @@ class Contact < ActiveRecord::Base
330330
attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
331331
attr_accessor :first_name, :last_name, :phone, :email
332332

333-
salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email},
334-
:salesforce_sync_enabled => false
333+
salesforce_syncable sync_attributes: {FirstName: :first_name, LastName: :last_name, Phone: :phone, Email: :email},
334+
salesforce_sync_enabled: false
335335
end
336336
```
337337

@@ -342,8 +342,8 @@ class Contact < ActiveRecord::Base
342342
attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
343343
attr_accessor :first_name, :last_name, :phone, :email
344344

345-
salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email},
346-
:except => :skip_sync?
345+
salesforce_syncable sync_attributes: {FirstName: :first_name, LastName: :last_name, Phone: :phone, Email: :email},
346+
except: :skip_sync?
347347

348348
def skip_sync?
349349
if first_name.blank?
@@ -367,8 +367,8 @@ class Contact < ActiveRecord::Base
367367
attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
368368
attr_accessor :first_name, :last_name, :phone, :email, :last_login_time
369369

370-
salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email, :Last_Login_Time__c => :last_login_time},
371-
:async_attributes => ["Last_Login_Time__c"]
370+
salesforce_syncable sync_attributes: { FirstName: :first_name, LastName: :last_name, Phone: :phone, Email: :email, Last_Login_Time__c: :last_login_time },
371+
async_attributes: ['Last_Login_Time__c']
372372
end
373373
```
374374

@@ -379,8 +379,8 @@ class Contact < ActiveRecord::Base
379379
attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
380380
attr_accessor :first_name, :last_name, :phone, :email, :last_login_time
381381

382-
salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email},
383-
:default_attributes_for_create => {:password_change_required => true}
382+
salesforce_syncable sync_attributes: { FirstName: :first_name, LastName: :last_name, Phone: :phone, Email: :email },
383+
default_attributes_for_create: { password_change_required: true }
384384
end
385385
```
386386

@@ -398,9 +398,9 @@ using the 18 digit Salesforce id, but maintain our ActiveRecord relationships.
398398
attributes :first_name, :last_name, :account_id
399399
attr_accessor :first_name, :last_name, :account_id
400400

401-
salesforce_syncable :sync_attributes => { :FirstName => :first_name,
402-
:LastName => :last_name,
403-
:AccountId => :salesforce_account_id }
401+
salesforce_syncable sync_attributes: { FirstName: :first_name,
402+
LastName: :last_name,
403+
AccountId: :salesforce_account_id }
404404

405405
def salesforce_account_id_changed?
406406
account_id_changed?
@@ -426,8 +426,8 @@ class Contact < ActiveRecord::Base
426426
attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
427427
attr_accessor :first_name, :last_name, :phone, :email, :last_login_time
428428

429-
salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email},
430-
:salesforce_object_name => :custom_salesforce_object_name
429+
salesforce_syncable sync_attributes: { FirstName: :first_name, LastName: :last_name, Phone: :phone, Email: :email },
430+
salesforce_object_name: :custom_salesforce_object_name
431431

432432
def custom_salesforce_object_name
433433
"CustomContact__c"
@@ -456,8 +456,8 @@ Syncing inbound deletes is enabled by default, but can be configured in the Rail
456456
This is done using the :sync_inbound_delete option, which can take either a boolean value, or the name of a method that returns a boolean value.
457457

458458
```ruby
459-
salesforce_syncable :sync_inbound_delete => :inbound_delete
460-
#:sync_inbound_delete => true
459+
salesforce_syncable sync_inbound_delete: :inbound_delete
460+
#sync_inbound_delete: true
461461
def inbound_delete
462462
return self.comments.count == 0
463463
end
@@ -468,8 +468,8 @@ Syncing outbound deletes to Salesforce is disabled by default, but can be config
468468
This is done using the :sync_outbound_delete option, which can take either a boolean value, or the name of a method that returns a boolean value.
469469

470470
```ruby
471-
salesforce_syncable :sync_outbound_delete => :outbound_delete
472-
#:sync_outbound_delete => false
471+
salesforce_syncable sync_outbound_delete: :outbound_delete
472+
#sync_outbound_delete: false
473473

474474
def outbound_delete
475475
return self.is_trial_user?

lib/salesforce_ar_sync/salesforce_object_sync.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
module SalesforceArSync
22
# simple object to be serialized when asynchronously sending data to Salesforce
3-
class SalesforceObjectSync < Struct.new(:web_object_name, :salesforce_id, :attributes)
4-
def perform
3+
class SalesforceObjectSync < ActiveJob::Base
4+
def perform(web_object_name, salesforce_id, attributes)
55
web_object = "#{web_object_name}".constantize.find_by_salesforce_id salesforce_id
6-
#object exists in salesforce if we call its system_mod_stamp
6+
# object exists in salesforce if we call its system_mod_stamp
77
if (system_mod_stamp = web_object.system_mod_stamp)
8-
web_object.salesforce_update_object(attributes)
8+
web_object.salesforce_update_object(JSON.parse(attributes))
99
web_object.update_attribute(:salesforce_updated_at, system_mod_stamp)
1010
end
1111
end

lib/salesforce_ar_sync/salesforce_sync.rb

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ module ClassMethods
7070
# Lastly it will create a new record setting it's salesforce_id
7171
def salesforce_update(attributes = {})
7272
raise ArgumentError, "#{salesforce_id_attribute_name} parameter required" if attributes[salesforce_id_attribute_name].blank?
73+
7374
data_source = unscoped_updates ? unscoped : self
7475
object = data_source.find_by(salesforce_id: attributes[salesforce_id_attribute_name])
7576
object ||= data_source.find_by(activerecord_web_id_attribute_name => attributes[salesforce_web_id_attribute_name]) if salesforce_sync_web_id? && attributes[salesforce_web_id_attribute_name]
@@ -145,8 +146,8 @@ def salesforce_process_update(attributes = {})
145146

146147
# Finds a salesforce record by its Id and returns nil or its SystemModstamp
147148
def system_mod_stamp
148-
hash = JSON.parse(SF_CLIENT.http_get("/services/data/v#{SF_CLIENT.version}/query", q: "SELECT SystemModstamp FROM #{salesforce_object_name} WHERE Id = '#{salesforce_id}'").body)
149-
hash['records'].first.try(:[], 'SystemModstamp')
149+
sobject = SF_CLIENT.find(salesforce_object_name, salesforce_id)
150+
sobject.SystemModstamp
150151
end
151152

152153
def salesforce_object_exists?
@@ -180,19 +181,20 @@ def is_boolean?(attribute)
180181

181182
def salesforce_create_object(attributes)
182183
attributes[self.class.salesforce_web_id_attribute_name.to_s] = id if self.class.salesforce_sync_web_id? && !new_record?
183-
result = SF_CLIENT.http_post("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}", format_attributes(attributes).to_json)
184-
self.salesforce_id = JSON.parse(result.body)['id']
184+
salesforce_id = SF_CLIENT.create(salesforce_object_name, format_attributes(attributes))
185+
self.salesforce_id = salesforce_id
185186
@exists_in_salesforce = true
186187
end
187188

188189
def salesforce_update_object(attributes)
189190
attributes[self.class.salesforce_web_id_attribute_name.to_s] = id if self.class.salesforce_sync_web_id? && !new_record?
190-
SF_CLIENT.http_patch("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}/#{salesforce_id}", format_attributes(attributes).to_json)
191+
192+
SF_CLIENT.update(salesforce_object_name, format_attributes(attributes).merge(Id: salesforce_id))
191193
end
192194

193195
def salesforce_delete_object
194196
if ar_sync_outbound_delete?
195-
SF_CLIENT.http_delete("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}/#{salesforce_id}")
197+
SF_CLIENT.destroy(salesforce_object_name, salesforce_id)
196198
end
197199
end
198200

@@ -217,7 +219,7 @@ def salesforce_perform_async_call?
217219
def salesforce_sync
218220
return if salesforce_skip_sync?
219221
if salesforce_perform_async_call?
220-
Delayed::Job.enqueue(SalesforceArSync::SalesforceObjectSync.new(self.class.salesforce_web_class_name, salesforce_id, salesforce_attributes_to_update), priority: 50)
222+
SalesforceArSync::SalesforceObjectSync.set(priority: 50).perform_later(self.class.salesforce_web_class_name, salesforce_id, salesforce_attributes_to_update.to_json)
221223
else
222224
if salesforce_object_exists?
223225
salesforce_update_object(salesforce_attributes_to_update) if salesforce_attributes_to_update.present?
@@ -227,12 +229,13 @@ def salesforce_sync
227229
end
228230
rescue Exception => ex
229231
errors[:base] << ex.message
230-
return false
232+
false
231233
end
232234

233235
def sync_web_id
234236
return false if !self.class.salesforce_sync_web_id? || SalesforceArSync.config['SYNC_ENABLED'] == false
235-
SF_CLIENT.http_patch("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}/#{salesforce_id}", { self.class.salesforce_web_id_attribute_name.to_s => get_activerecord_web_id }.to_json) if salesforce_id
237+
238+
SF_CLIENT.update(salesforce_object_name, Id: salesforce_id, self.class.salesforce_web_id_attribute_name.to_s => get_activerecord_web_id) if salesforce_id
236239
end
237240

238241
def get_activerecord_web_id
@@ -247,6 +250,10 @@ def get_activerecord_web_id
247250
def format_attributes(attributes)
248251
attributes.each do |k, v|
249252
attributes[k] = v.join(';') if v.is_a?(Array)
253+
254+
# Databasedotcom apparently did some other stuff to make sure this happened in the background
255+
# restforce does not so need to make sure this is called on anything that can call it
256+
attributes[k] = v.iso8601 if defined?(v.iso8601)
250257
end
251258
attributes
252259
end

lib/salesforce_ar_sync/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module SalesforceArSync
2-
VERSION = '3.2.0'
2+
VERSION = '4.0.0'
33
end

salesforce_ar_sync.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ Gem::Specification.new do |gem|
2424
gem.add_development_dependency 'vcr'
2525
gem.add_development_dependency 'webmock'
2626

27-
gem.add_runtime_dependency 'databasedotcom'
27+
gem.add_runtime_dependency 'restforce', '~> 5.0.5'
2828
end

spec/spec_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
require 'rails/all'
33
require 'salesforce_ar_sync'
44
require 'active_record'
5-
require 'databasedotcom'
5+
require 'restforce'
66
require 'rspec'
77
require 'ammeter/init'
88
require 'vcr'

0 commit comments

Comments
 (0)