salesforce_ar_sync 1.1.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b19087f9160aa21606e6de401c9813e693fb306d
4
+ data.tar.gz: 7bded65098335e5f1be42efa9bca7a8219c9e419
5
+ SHA512:
6
+ metadata.gz: 5a7d57027cd4067238f8992cfffda917b6dee7654cef6068e4e21c86f27f829fdb65e20e20497558581691165d4b1a02c814a989ba0c788ce310da3879f24b66
7
+ data.tar.gz: 36219dd2e81e11afcfb77f73ba098c0f6dc427b9a9a7e764fa420c467eea15e2c5fc04877d79717119da6e0fad14675711e2a6dc37289d7ea0d2a59a75da3120
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # SalesforceArSync
2
2
 
3
- SalesforceARSync allows you to sync models and fields with Salesforce through a combination of
3
+ SalesforceARSync allows you to sync models and fields with Salesforce through a combination of
4
4
  Outbound Messaging, SOAP and databasedotcom.
5
5
 
6
6
  ## Installation
7
7
 
8
8
  ### Requirements
9
9
 
10
- * Rails >= 3.1
10
+ * Rails ~> 4.0
11
11
  * Salesforce.com instance
12
12
  * [Have your 18 character organization id ready](#finding-your-18-character-organization-id)
13
13
  * databasedotcom gem >= 1.3 installed and configured [see below](#databasedotcom)
@@ -15,32 +15,32 @@ Outbound Messaging, SOAP and databasedotcom.
15
15
 
16
16
  ### Salesforce Setup
17
17
 
18
- Before you can start syncing your data several things must be completed in Salesforce.
18
+ Before you can start syncing your data several things must be completed in Salesforce.
19
19
 
20
20
  #### 1. Setup Remote Access
21
- Create a new Remote Access Application entry by going to
21
+ Create a new Remote Access Application entry by going to
22
22
 
23
23
  Setup -> Develop -> Remote Access
24
-
24
+
25
25
  You can use http://localhost/nothing for the _Callback URL_
26
26
 
27
27
  #### 2. Setup Outbound Messaging
28
- Each model you wish to sync requires a workflow to trigger outbound messaging. You can set the worflow
29
- to trigger on the specific fields you wish to update.
28
+ Each model you wish to sync requires a workflow to trigger outbound messaging. You can set the worflow
29
+ to trigger on the specific fields you wish to update.
30
30
 
31
31
  Setup -> Create -> Workflow & Approvals -> Worflow Rules
32
-
33
- Click _New Rule_, select the object (model) you wish to sync and click _Next_, give the rule a name, select
34
- _Every time a record is created or edited_ and set a rule on the field(s) you want to sync ( a formula checking
32
+
33
+ Click _New Rule_, select the object (model) you wish to sync and click _Next_, give the rule a name, select
34
+ _Every time a record is created or edited_ and set a rule on the field(s) you want to sync ( a formula checking
35
35
  if the fields have changed is recommended). Click _Save & Next_, in the _Add Worflow Action_ dropdown select
36
- _New Outbound Message_. Enter a name and set the _Endpoint URL_ to be http://yoursite.com/integration/sf_soap/model_name.
37
- Select the fields you wish to sync (Id and SystemModstamp are required).
36
+ _New Outbound Message_. Enter a name and set the _Endpoint URL_ to be http://yoursite.com/integration/sf_soap/model_name.
37
+ Select the fields you wish to sync (Id and SystemModstamp are required).
38
38
 
39
39
  *You need to do this for each object/model you want to sync.
40
40
 
41
41
  ### databasedotcom
42
42
 
43
- Before using the salesforce_ar_sync gem you must ensure you have the databasedotcom gem installed and configured
43
+ Before using the salesforce_ar_sync gem you must ensure you have the databasedotcom gem installed and configured
44
44
  properly. Make sure each of the models you wish to sync are materialized.
45
45
 
46
46
  ````ruby
@@ -65,17 +65,17 @@ And then execute:
65
65
  Or install it yourself as:
66
66
 
67
67
  $ gem install salesforce_ar_sync
68
-
68
+
69
69
  ### Application Setup
70
70
 
71
- Before using the gem you must complete the setup of your rails app.
71
+ Before using the gem you must complete the setup of your rails app.
72
72
 
73
73
  The gem needs to know your 18 character organization id, it can be stored in a YAML file or in the ENV class.
74
74
 
75
75
  To create the yaml file run
76
76
 
77
77
  $ rails generate salesforce_ar_sync:configuration <organization id>
78
-
78
+
79
79
  Next you will need to decide which models you want to sync. For each model you must create a migration and run them
80
80
 
81
81
  $ rails generate salesforce_ar_sync:migrations <models> --migrate
@@ -87,7 +87,7 @@ To mount the engine add the following line to your routes.rb file
87
87
  You can change '/integration' to whatever you want, all of the engine routes will be based off of this path. Running
88
88
 
89
89
  $ rake routes | grep salesforce_ar_sync
90
-
90
+
91
91
  will show you all of the gems routes, make sure you point your outbound messages at these urls.
92
92
 
93
93
  Next you will need to tell the gem which models are syncable by adding _salesforce_syncable_ to your model class
@@ -97,7 +97,7 @@ and specifying which attributes you would like to sync.
97
97
  salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name}
98
98
  ````
99
99
 
100
- The first parameter in the _:sync_attributes_ hash is the Salesforce field name and the second is the model attribute
100
+ The first parameter in the _:sync_attributes_ hash is the Salesforce field name and the second is the model attribute
101
101
  name.
102
102
 
103
103
  ## Usage
@@ -115,18 +115,18 @@ The options available to configure are
115
115
  To generate a YAML file
116
116
 
117
117
  $ rails generate salesforce_ar_sync:configuration
118
-
118
+
119
119
  Or with an organization id
120
-
120
+
121
121
  $ rails generate salesforce_ar_sync:configuration 123456789123456789
122
-
122
+
123
123
  which will create a template salesforce_ar_sync.yml in /config that looks like the following
124
124
 
125
125
  organization_id: <organization id> #18 character organization_id
126
126
  sync_enabled: true
127
127
  namespace_prefix:
128
128
 
129
-
129
+
130
130
  To use the ENV variable you must pass environemnt variables to rails via the _export_ command in bash or before the
131
131
  initializer loads the ENV settings.
132
132
 
@@ -137,16 +137,27 @@ initializer loads the ENV settings.
137
137
  ### Model Options
138
138
  The model can have several options set:
139
139
 
140
- [__salesforce_sync_enabled__](#salesforce_sync_enabled)
141
- [__sync_attributes__](#sync_attributes)
142
- [__async_attributes__](#async_attributes)
143
- [__default_attributes_for_create__](#default_attributes_for_create)
144
- [__salesforce_id_attribute_name__](#salesforce_id_attribute_name)
145
- [__web_id_attribute_name__](#web_id_attribute_name)
146
- [__salesforce_sync_web_id__](#salesforce_sync_web_id)
147
- [__web_class_name__](#web_class_name)
148
- [__salesforce_object_name__](#salesforce_object_name)
149
- [__except__](#except)
140
+ [__salesforce_sync_enabled__](#salesforce_sync_enabled)
141
+
142
+ [__sync_attributes__](#sync_attributes)
143
+
144
+ [__async_attributes__](#async_attributes)
145
+
146
+ [__default_attributes_for_create__](#default_attributes_for_create)
147
+
148
+ [__salesforce_id_attribute_name__](#salesforce_id_attribute_name)
149
+
150
+ [__web_id_attribute_name__](#web_id_attribute_name)
151
+
152
+ [__activerecord_web_id_attribute_name__](#activerecord_web_id_attribute_name)
153
+
154
+ [__salesforce_sync_web_id__](#salesforce_sync_web_id)
155
+
156
+ [__web_class_name__](#web_class_name)
157
+
158
+ [__salesforce_object_name__](#salesforce_object_name)
159
+
160
+ [__except__](#except)
150
161
 
151
162
  #### <a id="salesforce_sync_enabled"></a>salesforce_sync_enabled
152
163
  Model level option to enable disable the sync, defaults to true.
@@ -154,11 +165,11 @@ Model level option to enable disable the sync, defaults to true.
154
165
  ````ruby
155
166
  :salesforce_sync_enabled => false
156
167
  ````
157
-
168
+
158
169
  #### sync_attributes
159
- Hash mapping of Salesforce attributes to web attributes, defaults to empty hash.
160
- "Web" attributes can be actual method names to return a custom value.If you are providing a method name to return a
161
- value, you should also implement a corresponding my_method_changed? to return if the value has changed. Otherwise
170
+ Hash mapping of Salesforce attributes to web attributes, defaults to empty hash.
171
+ "Web" attributes can be actual method names to return a custom value.If you are providing a method name to return a
172
+ value, you should also implement a corresponding my_method_changed? to return if the value has changed. Otherwise
162
173
  it will always be synced.
163
174
 
164
175
  ````ruby
@@ -166,16 +177,16 @@ it will always be synced.
166
177
  ````
167
178
 
168
179
  #### async_attributes
169
- 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.
180
+ 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.
170
181
  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.
171
182
 
172
183
  ````ruby
173
184
  :async_attributes => ["Last_Login__c", "Login_Count__c"]
174
185
  ````
175
186
 
176
- Note: The model will fall back to synchronous sync if non-synchronous attributes are changed along with async
187
+ Note: The model will fall back to synchronous sync if non-synchronous attributes are changed along with async
177
188
  attributes
178
-
189
+
179
190
  #### default_attributes_for_create
180
191
  A hash of default attributes that should be used when we are creating a new record, defaults to empty hash.
181
192
 
@@ -197,6 +208,13 @@ The field name of the web id attribute in the Salesforce Object, defaults to _We
197
208
  :web_id_attribute_name => :WebId__c
198
209
  ````
199
210
 
211
+ #### activerecord_web_id_attribute_name
212
+ The field name of the web id attribute in the Active Record Object, defaults to id
213
+
214
+ ````ruby
215
+ :activerecord_web_id_attribute_name => :id
216
+ ````
217
+
200
218
  #### salesforce_sync_web_id
201
219
  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.
202
220
 
@@ -205,10 +223,10 @@ Enable or disable sync of the web id, defaults to false. Use this if you have a
205
223
  ````
206
224
 
207
225
  #### web_class_name
208
- The name of the Web Objects class. A custom value can be provided if you wish to sync to a SF object and back to a
209
- different web object. Defaults to the model name. This would generally be used if you wanted to flatten a web object
226
+ The name of the Web Objects class. A custom value can be provided if you wish to sync to a SF object and back to a
227
+ different web object. Defaults to the model name. This would generally be used if you wanted to flatten a web object
210
228
  into a larger SF object like Contact.
211
-
229
+
212
230
  ````ruby
213
231
  :web_class_name => 'Contact',
214
232
  ````
@@ -221,7 +239,7 @@ Optionally holds the name of a method which will return the name of the Salesfor
221
239
  ````
222
240
 
223
241
  #### except
224
- Optionally holds the name of a method which can contain logic to determine if a record should be synced on save. If no
242
+ Optionally holds the name of a method which can contain logic to determine if a record should be synced on save. If no
225
243
  method is given then only the salesforce_skip_sync attribute is used. Defaults to nil.
226
244
 
227
245
  ````ruby
@@ -230,9 +248,9 @@ method is given then only the salesforce_skip_sync attribute is used. Defaults t
230
248
 
231
249
  ### Stopping the Sync
232
250
 
233
- Stopping the gem from syncing can be done on three levels.
251
+ Stopping the gem from syncing can be done on three levels.
234
252
 
235
- * The global level before the app starts via the .yml file, ENV variables or after the app starts with the gem's
253
+ * The global level before the app starts via the .yml file, ENV variables or after the app starts with the gem's
236
254
  configuration variable _SALESFORCE_AR_SYNC_CONFIG["SYNC_ENABLED"]_
237
255
  * The model level by setting the _:salesforce_sync_enabled => false_ or _:except => :method_name_
238
256
  * The instance level by setting _:salesforce_skip_sync => true_ in the instance
@@ -254,7 +272,7 @@ end
254
272
  class Contact < ActiveRecord::Base
255
273
  attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
256
274
  attr_accessor :first_name, :last_name, :phone, :email
257
-
275
+
258
276
  salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email}
259
277
  end
260
278
  ```
@@ -265,7 +283,7 @@ end
265
283
  class Contact < ActiveRecord::Base
266
284
  attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
267
285
  attr_accessor :first_name, :last_name, :phone, :email
268
-
286
+
269
287
  salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email},
270
288
  :salesforce_sync_enabled => false
271
289
  end
@@ -277,10 +295,10 @@ end
277
295
  class Contact < ActiveRecord::Base
278
296
  attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
279
297
  attr_accessor :first_name, :last_name, :phone, :email
280
-
298
+
281
299
  salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email},
282
300
  :except => :skip_sync?
283
-
301
+
284
302
  def skip_sync?
285
303
  if first_name.blank?
286
304
  return true
@@ -302,7 +320,7 @@ customer.salesforce_skip_sync = true
302
320
  class Contact < ActiveRecord::Base
303
321
  attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
304
322
  attr_accessor :first_name, :last_name, :phone, :email, :last_login_time
305
-
323
+
306
324
  salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email, :Last_Login_Time__c => :last_login_time},
307
325
  :async_attributes => ["Last_Login_Time__c"]
308
326
  end
@@ -314,7 +332,7 @@ end
314
332
  class Contact < ActiveRecord::Base
315
333
  attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
316
334
  attr_accessor :first_name, :last_name, :phone, :email, :last_login_time
317
-
335
+
318
336
  salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email},
319
337
  :default_attributes_for_create => {:password_change_required => true}
320
338
  end
@@ -348,11 +366,11 @@ using the 18 digit Salesforce id, but maintain our ActiveRecord relationships.
348
366
  end
349
367
 
350
368
  def salesforce_account_id=(account_id)
351
- self.account = nil and return if account_id.nil?
369
+ self.account = nil if account_id.nil? and return
352
370
  self.account = Account.find_or_create_by_salesforce_id(account_id)
353
371
  end
354
372
  end
355
-
373
+
356
374
  ```
357
375
 
358
376
  ### Defining a Custom Salesforce Object
@@ -361,7 +379,7 @@ using the 18 digit Salesforce id, but maintain our ActiveRecord relationships.
361
379
  class Contact < ActiveRecord::Base
362
380
  attributes :first_name, :last_name, :phone, :email, :last_login_time, :salesforce_id, :salesforce_updated_at
363
381
  attr_accessor :first_name, :last_name, :phone, :email, :last_login_time
364
-
382
+
365
383
  salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email},
366
384
  :salesforce_object_name => :custom_salesforce_object_name
367
385
 
@@ -372,6 +390,7 @@ end
372
390
  ```
373
391
 
374
392
  ## Deletes
393
+ ### Inbound Deletes
375
394
  In order to handle the delete of objects coming from Salesforce, a bit of code is necessary because an Outbound Message cannot be triggered when
376
395
  an object is deleted. To work around this you will need to create a new Custom Object in your Salesforce environment:
377
396
 
@@ -387,6 +406,30 @@ Object_Type__c will hold the name of the Rails Model that the Salesforce object
387
406
  If you trigger a record to be written to this object whenever another object is deleted, and configure an Outbound Message to send to the /sf_soap/delete action
388
407
  whenever a Deleted_Object__c record is created, the corresponding record will be removed from your Rails app.
389
408
 
409
+ Syncing inbound deletes is enabled by default, but can be configured in the Rails Model.
410
+ 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.
411
+
412
+ ```ruby
413
+ salesforce_syncable :sync_inbound_delete => :inbound_delete
414
+ #:sync_inbound_delete => true
415
+ def inbound_delete
416
+ return self.comments.count == 0
417
+ end
418
+ ```
419
+
420
+ ### Outbound Deletes
421
+ Syncing outbound deletes to Salesforce is disabled by default, but can be configured in the Rails Model.
422
+ 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.
423
+
424
+ ```ruby
425
+ salesforce_syncable :sync_outbound_delete => :outbound_delete
426
+ #:sync_outbound_delete => false
427
+
428
+ def outbound_delete
429
+ return self.is_trial_user?
430
+ end
431
+ ```
432
+
390
433
  ## Errors
391
434
 
392
435
  ### Outbound Message Errors
@@ -396,8 +439,8 @@ If the SOAP handler encounters an error it will be recorded in the log of the ou
396
439
  Setup -> Monitoring -> Outbound Messages
397
440
 
398
441
  ## <a id="orga_id"></a>Finding your 18 Character Organization ID
399
- Your 15 character organization id can be found in _Setup -> Company Profile -> Company Information_. You must convert
400
- it to an 18 character id by running it through the tool located here:
442
+ Your 15 character organization id can be found in _Setup -> Company Profile -> Company Information_. You must convert
443
+ it to an 18 character id by running it through the tool located here:
401
444
  http://cloudjedi.wordpress.com/no-fuss-salesforce-id-converter/ or by installing the Force.com Utility Belt for Chrome.
402
445
 
403
446
  ## Contributing
@@ -17,7 +17,7 @@ module SalesforceArSync
17
17
 
18
18
  def create_migrations
19
19
  models.each do |model|
20
- create_migration(model)
20
+ create_ar_sync_migration(model)
21
21
  end
22
22
 
23
23
  if options[:migrate] == "yes"
@@ -41,7 +41,7 @@ module SalesforceArSync
41
41
  @prev_migration_nr.to_s
42
42
  end
43
43
 
44
- def create_migration(model_name)
44
+ def create_ar_sync_migration(model_name)
45
45
  #we can't load all the models in so let's assume it follows the standard nameing convention
46
46
  @table_name = model_name.tableize
47
47
  @model_name = model_name
@@ -1,6 +1,6 @@
1
1
  module SalesforceArSync
2
2
  module Extenders
3
- module SalesforceSyncable
3
+ module SalesforceSyncable
4
4
  def salesforce_syncable(options = {})
5
5
  require 'salesforce_ar_sync/salesforce_sync'
6
6
  include SalesforceArSync::SalesforceSync
@@ -11,21 +11,26 @@ module SalesforceArSync
11
11
  self.salesforce_default_attributes_for_create = options.has_key?(:default_attributes_for_create) ? options[:default_attributes_for_create] : {}
12
12
  self.salesforce_id_attribute_name = options.has_key?(:salesforce_id_attribute_name) ? options[:salesforce_id_attribute_name] : :Id
13
13
  self.salesforce_web_id_attribute_name = options.has_key?(:web_id_attribute_name) ? options[:web_id_attribute_name] : :WebId__c
14
+ self.activerecord_web_id_attribute_name = options.has_key?(:activerecord_web_id_attribute_name) ? options[:activerecord_web_id_attribute_name] : :id
14
15
  self.salesforce_sync_web_id = options.has_key?(:salesforce_sync_web_id) ? options[:salesforce_sync_web_id] : false
15
16
  self.salesforce_web_class_name = options.has_key?(:web_class_name) ? options[:web_class_name] : self.name
16
-
17
+
18
+ self.sync_inbound_delete = options.has_key?(:sync_inbound_delete) ? options[:sync_inbound_delete] : true
19
+ self.sync_outbound_delete = options.has_key?(:sync_outbound_delete) ? options[:sync_outbound_delete] : false
20
+
17
21
  self.salesforce_object_name_method = options.has_key?(:salesforce_object_name) ? options[:salesforce_object_name] : nil
18
22
  self.salesforce_skip_sync_method = options.has_key?(:except) ? options[:except] : nil
19
-
23
+
20
24
  instance_eval do
21
25
  before_save :salesforce_sync
22
26
  after_create :sync_web_id
23
-
27
+ after_commit :salesforce_delete_object, on: :destroy
28
+
24
29
  def salesforce_sync_web_id?
25
30
  self.salesforce_sync_web_id
26
31
  end
27
32
  end
28
-
33
+
29
34
  class_eval do
30
35
  # Calls a method if provided to return the name of the Salesforce object the model is syncing to.
31
36
  # If no method is provided, defaults to the class name
@@ -33,8 +38,8 @@ module SalesforceArSync
33
38
  return send(self.class.salesforce_object_name_method) if self.class.salesforce_object_name_method.present?
34
39
  return self.class.name
35
40
  end
36
-
37
- # Calls a method, if provided, to determine if a record should be synced to Salesforce.
41
+
42
+ # Calls a method, if provided, to determine if a record should be synced to Salesforce.
38
43
  # The salesforce_skip_sync instance variable is also used.
39
44
  # The SALESFORCE_AR_SYNC_ENABLED flag overrides all the others if set to false
40
45
  def salesforce_skip_sync?
@@ -0,0 +1,7 @@
1
+ module SalesforceArSync
2
+ class Railtie < Rails::Railtie
3
+ initializer 'salesforce_ar_sync.insert_middleware' do |app|
4
+ app.config.middleware.insert_after ActionDispatch::ParamsParser, ActionDispatch::XmlParamsParser
5
+ end
6
+ end
7
+ end
@@ -1,11 +1,11 @@
1
1
  require 'active_support/concern'
2
2
 
3
3
  module SalesforceArSync
4
- module SalesforceSync
4
+ module SalesforceSync
5
5
  extend ActiveSupport::Concern
6
-
7
- module ClassMethods
8
- # Optionally holds the value to determine if salesforce syncing is enabled. Defaults to true. If set
6
+
7
+ module ClassMethods
8
+ # Optionally holds the value to determine if salesforce syncing is enabled. Defaults to true. If set
9
9
  # to false syncing will be disabled for the class
10
10
  attr_accessor :salesforce_sync_enabled
11
11
 
@@ -14,15 +14,15 @@ module SalesforceArSync
14
14
  # { :Email => :login, :FirstName => :first_name, :LastName => :last_name }
15
15
  #
16
16
  # "Web" attributes can be actual method names to return a custom value
17
- # If you are providing a method name to return a value, you should also implement a corresponding my_method_changed? to
17
+ # If you are providing a method name to return a value, you should also implement a corresponding my_method_changed? to
18
18
  # return if the value has changed. Otherwise it will always be synced.
19
19
  attr_accessor :salesforce_sync_attribute_mapping
20
-
20
+
21
21
  # Returns an array of Salesforce attributes which should be synced asynchronously
22
22
  # Example: ["Last_Login_Date__c", "Login_Count__c" ]
23
23
  # Note: The model will fall back to synchronous sync if non-synchronous attributes are changed along with async attributes
24
24
  attr_accessor :salesforce_async_attributes
25
-
25
+
26
26
  # Returns a hash of default attributes that should be used when we are creating a new record
27
27
  attr_accessor :salesforce_default_attributes_for_create
28
28
 
@@ -31,19 +31,25 @@ module SalesforceArSync
31
31
 
32
32
  # Returns the name of the Web Objects class. A custom value can be provided if you wish
33
33
  # to sync to a SF object and back to a different web object. This would generally be used
34
- # if you wanted to flatten a web object into a larger SF object like Contact
34
+ # if you wanted to flatten a web object into a larger SF object like Contact
35
35
  attr_accessor :salesforce_web_class_name
36
36
 
37
- attr_accessor :salesforce_web_id_attribute_name
37
+ # Specify whether or not we sync deletes inbound from salesforce or outbound from this app
38
+ # Accepts either a true/false or a symbol to a method to be called
39
+ attr_accessor :sync_inbound_delete
40
+ attr_accessor :sync_outbound_delete
41
+
42
+ attr_accessor :salesforce_web_id_attribute_name
38
43
  attr_accessor :salesforce_sync_web_id
39
-
44
+ attr_accessor :activerecord_web_id_attribute_name
45
+
40
46
  # Optionally holds the name of a method which will return the name of the Salesforce object to sync to
41
47
  attr_accessor :salesforce_object_name_method
42
48
 
43
49
  # Optionally holds the name of a method which can contain logic to determine if a record should be synced on save.
44
50
  # If no method is given then only the salesforce_skip_sync attribute is used.
45
51
  attr_accessor :salesforce_skip_sync_method
46
-
52
+
47
53
  # Accepts values from an outbound message hash and will either update an existing record OR create a new record
48
54
  # Firstly attempts to find an object by the salesforce_id attribute
49
55
  # Secondly attempts to look an object up by it's ID (WebId__c in outbound message)
@@ -51,9 +57,9 @@ module SalesforceArSync
51
57
  def salesforce_update(attributes={})
52
58
  raise ArgumentError, "#{salesforce_id_attribute_name} parameter required" if attributes[salesforce_id_attribute_name].blank?
53
59
 
54
- object = self.find_by_salesforce_id attributes[salesforce_id_attribute_name]
55
- object ||= self.find_by_id attributes[salesforce_web_id_attribute_name] if salesforce_sync_web_id? && attributes[salesforce_web_id_attribute_name]
56
-
60
+ object = self.find_by(salesforce_id: attributes[salesforce_id_attribute_name])
61
+ object ||= self.find_by(activerecord_web_id_attribute_name => attributes[salesforce_web_id_attribute_name]) if salesforce_sync_web_id? && attributes[salesforce_web_id_attribute_name]
62
+
57
63
  if object.nil?
58
64
  object = self.new
59
65
  salesforce_default_attributes_for_create.merge(:salesforce_id => attributes[salesforce_id_attribute_name]).each_pair do |k, v|
@@ -68,7 +74,7 @@ module SalesforceArSync
68
74
  # if this instance variable is set to true, the salesforce_sync method will return without attempting
69
75
  # to sync data to Salesforce
70
76
  attr_accessor :salesforce_skip_sync
71
-
77
+
72
78
  # Salesforce completely excludes any empty/null fields from Outbound Messages
73
79
  # We initialize all declared attributes as nil before mapping the values from the message
74
80
  def salesforce_empty_attributes
@@ -81,8 +87,8 @@ module SalesforceArSync
81
87
 
82
88
  # An internal method used to get a hash of values that we are going to set from a Salesforce outbound message hash
83
89
  def salesforce_attributes_to_set(attributes = {})
84
- {}.tap do |hash|
85
- # loop through the hash of attributes from the outbound message, and compare to our sf mappings and
90
+ {}.tap do |hash|
91
+ # loop through the hash of attributes from the outbound message, and compare to our sf mappings and
86
92
  # create a reversed hash of value's and key's to pass to update_attributes
87
93
  attributes.each do |key, value|
88
94
  # make sure our sync_mapping contains the salesforce attribute AND that our object has a setter for it
@@ -106,9 +112,9 @@ module SalesforceArSync
106
112
  attributes_to_update.each_pair do |k, v|
107
113
  self.send("#{k}=", v)
108
114
  end
109
-
115
+
110
116
  # we don't want to keep going in a endless loop. SF has just updated these values.
111
- self.salesforce_skip_sync = true
117
+ self.salesforce_skip_sync = true
112
118
  self.save!
113
119
  end
114
120
 
@@ -116,12 +122,12 @@ module SalesforceArSync
116
122
  # return salesforce_object_exists_method if respond_to? salesforce_exists_method
117
123
  # return salesforce_object_exists_default
118
124
  # end
119
-
120
-
125
+
126
+
121
127
  # Finds a salesforce record by its Id and returns nil or its SystemModstamp
122
128
  def system_mod_stamp
123
129
  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)
124
- hash["records"].first.try(:[], "SystemModstamp")
130
+ hash["records"].first.try(:[], "SystemModstamp")
125
131
  end
126
132
 
127
133
 
@@ -137,7 +143,7 @@ module SalesforceArSync
137
143
 
138
144
  # create a hash of updates to send to salesforce
139
145
  def salesforce_attributes_to_update(include_all = false)
140
- {}.tap do |hash|
146
+ {}.tap do |hash|
141
147
  self.class.salesforce_sync_attribute_mapping.each do |key, value|
142
148
  if self.respond_to?(value)
143
149
 
@@ -150,7 +156,7 @@ module SalesforceArSync
150
156
  hash[key] = attribute_value if include_all || salesforce_should_update_attribute?(value)
151
157
  end
152
158
  end
153
- end
159
+ end
154
160
  end
155
161
 
156
162
  def is_boolean?(attribute)
@@ -159,17 +165,33 @@ module SalesforceArSync
159
165
 
160
166
  def salesforce_create_object(attributes)
161
167
  attributes.merge!(self.class.salesforce_web_id_attribute_name.to_s => id) if self.class.salesforce_sync_web_id? && !new_record?
162
- result = SF_CLIENT.http_post("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}", attributes.to_json)
168
+ result = SF_CLIENT.http_post("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}", format_attributes(attributes).to_json)
163
169
  self.salesforce_id = JSON.parse(result.body)["id"]
164
170
  @exists_in_salesforce = true
165
171
  end
166
172
 
167
173
  def salesforce_update_object(attributes)
168
174
  attributes.merge!(self.class.salesforce_web_id_attribute_name.to_s => id) if self.class.salesforce_sync_web_id? && !new_record?
169
- SF_CLIENT.http_patch("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}/#{salesforce_id}", attributes.to_json)
175
+ SF_CLIENT.http_patch("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}/#{salesforce_id}", format_attributes(attributes).to_json)
170
176
  end
171
177
 
172
- # if attributes specified in the async_attributes array are the only attributes being modified, then sync the data
178
+ def salesforce_delete_object
179
+ if self.ar_sync_outbound_delete?
180
+ SF_CLIENT.http_delete("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}/#{salesforce_id}")
181
+ end
182
+ end
183
+
184
+ # Check to see if the user passed in a true/false, if so return that, if not then they passed int a symbol to a method
185
+ # We then call the method and use its value instead
186
+ def ar_sync_inbound_delete?
187
+ [true,false].include?(self.class.sync_inbound_delete) ? self.class.sync_inbound_delete : send(self.class.sync_inbound_delete)
188
+ end
189
+
190
+ def ar_sync_outbound_delete?
191
+ [true,false].include?(self.class.sync_outbound_delete) ? self.class.sync_outbound_delete : send(self.class.sync_outbound_delete)
192
+ end
193
+
194
+ # if attributes specified in the async_attributes array are the only attributes being modified, then sync the data
173
195
  # via delayed_job
174
196
  def salesforce_perform_async_call?
175
197
  return false if salesforce_attributes_to_update.empty? || self.class.salesforce_async_attributes.empty?
@@ -185,18 +207,33 @@ module SalesforceArSync
185
207
  if salesforce_object_exists?
186
208
  salesforce_update_object(salesforce_attributes_to_update) if salesforce_attributes_to_update.present?
187
209
  else
188
- salesforce_create_object(salesforce_attributes_to_update(!new_record?)) if salesforce_id.nil?
210
+ salesforce_create_object(salesforce_attributes_to_update(!new_record?)) if salesforce_id.nil?
189
211
  end
190
212
  end
191
213
  rescue Exception => ex
192
214
  self.errors[:base] << ex.message
193
215
  return false
194
216
  end
195
-
196
- def sync_web_id
217
+
218
+ def sync_web_id
197
219
  return false if !self.class.salesforce_sync_web_id? || self.salesforce_skip_sync?
198
- 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 => id }.to_json) if salesforce_id
220
+ 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
221
+ end
222
+
223
+ def get_activerecord_web_id
224
+ self.send(self.class.activerecord_web_id_attribute_name)
199
225
  end
200
226
 
227
+ private
228
+
229
+ # Multi-picklists in the SF Rest API are expected to be in a semicolon separated list
230
+ # This method converts all attribute that are arrays into these values
231
+ # Eg. ["A","B","C"] => "A;B;C"
232
+ def format_attributes(attributes)
233
+ attributes.each do |k,v|
234
+ attributes[k] = v.join(";") if v.is_a?(Array)
235
+ end
236
+ return attributes
237
+ end
201
238
  end
202
239
  end
@@ -12,7 +12,7 @@ module SalesforceArSync
12
12
  raise ArgumentError, "Object_Type__c parameter required" if hash[namespaced(:Object_Type__c)].blank?
13
13
 
14
14
  object = hash[namespaced(:Object_Type__c)].constantize.find_by_salesforce_id(hash[namespaced(:Object_Id__c)])
15
- object.destroy if object
15
+ object.destroy if object && object.ar_sync_inbound_delete?
16
16
  end
17
17
  end
18
18
  end
@@ -1,3 +1,3 @@
1
1
  module SalesforceArSync
2
- VERSION = "1.1.2"
2
+ VERSION = "2.0.0"
3
3
  end
@@ -5,11 +5,12 @@ require 'salesforce_ar_sync/salesforce_object_sync'
5
5
  require 'salesforce_ar_sync/soap_handler/base'
6
6
  require 'salesforce_ar_sync/soap_handler/delete'
7
7
  require 'salesforce_ar_sync/ip_constraint'
8
+ require 'salesforce_ar_sync/railtie'
8
9
 
9
10
  module SalesforceArSync
10
11
  mattr_accessor :app_root
11
12
  mattr_accessor :config
12
-
13
+
13
14
  def self.setup
14
15
  yield self
15
16
  end
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: salesforce_ar_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
5
- prerelease:
4
+ version: 2.0.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Michael Halliday
@@ -13,150 +12,132 @@ authors:
13
12
  autorequire:
14
13
  bindir: bin
15
14
  cert_chain: []
16
- date: 2013-04-26 00:00:00.000000000 Z
15
+ date: 2015-06-09 00:00:00.000000000 Z
17
16
  dependencies:
18
17
  - !ruby/object:Gem::Dependency
19
18
  name: rails
20
19
  requirement: !ruby/object:Gem::Requirement
21
- none: false
22
20
  requirements:
23
- - - ! '>='
21
+ - - "~>"
24
22
  - !ruby/object:Gem::Version
25
- version: 3.1.0
23
+ version: '4.0'
26
24
  type: :runtime
27
25
  prerelease: false
28
26
  version_requirements: !ruby/object:Gem::Requirement
29
- none: false
30
27
  requirements:
31
- - - ! '>='
28
+ - - "~>"
32
29
  - !ruby/object:Gem::Version
33
- version: 3.1.0
30
+ version: '4.0'
34
31
  - !ruby/object:Gem::Dependency
35
- name: rake
32
+ name: actionpack-xml_parser
36
33
  requirement: !ruby/object:Gem::Requirement
37
- none: false
38
34
  requirements:
39
- - - ! '>='
35
+ - - ">="
40
36
  - !ruby/object:Gem::Version
41
37
  version: '0'
42
- type: :development
38
+ type: :runtime
43
39
  prerelease: false
44
40
  version_requirements: !ruby/object:Gem::Requirement
45
- none: false
46
41
  requirements:
47
- - - ! '>='
42
+ - - ">="
48
43
  - !ruby/object:Gem::Version
49
44
  version: '0'
50
45
  - !ruby/object:Gem::Dependency
51
- name: rspec
46
+ name: rake
52
47
  requirement: !ruby/object:Gem::Requirement
53
- none: false
54
48
  requirements:
55
- - - ! '>='
49
+ - - ">="
56
50
  - !ruby/object:Gem::Version
57
51
  version: '0'
58
52
  type: :development
59
53
  prerelease: false
60
54
  version_requirements: !ruby/object:Gem::Requirement
61
- none: false
62
55
  requirements:
63
- - - ! '>='
56
+ - - ">="
64
57
  - !ruby/object:Gem::Version
65
58
  version: '0'
66
59
  - !ruby/object:Gem::Dependency
67
- name: webmock
60
+ name: rspec
68
61
  requirement: !ruby/object:Gem::Requirement
69
- none: false
70
62
  requirements:
71
- - - ! '>='
63
+ - - ">="
72
64
  - !ruby/object:Gem::Version
73
65
  version: '0'
74
66
  type: :development
75
67
  prerelease: false
76
68
  version_requirements: !ruby/object:Gem::Requirement
77
- none: false
78
69
  requirements:
79
- - - ! '>='
70
+ - - ">="
80
71
  - !ruby/object:Gem::Version
81
72
  version: '0'
82
73
  - !ruby/object:Gem::Dependency
83
- name: vcr
74
+ name: webmock
84
75
  requirement: !ruby/object:Gem::Requirement
85
- none: false
86
76
  requirements:
87
- - - ! '>='
77
+ - - ">="
88
78
  - !ruby/object:Gem::Version
89
79
  version: '0'
90
80
  type: :development
91
81
  prerelease: false
92
82
  version_requirements: !ruby/object:Gem::Requirement
93
- none: false
94
83
  requirements:
95
- - - ! '>='
84
+ - - ">="
96
85
  - !ruby/object:Gem::Version
97
86
  version: '0'
98
87
  - !ruby/object:Gem::Dependency
99
- name: ammeter
88
+ name: vcr
100
89
  requirement: !ruby/object:Gem::Requirement
101
- none: false
102
90
  requirements:
103
- - - ~>
91
+ - - ">="
104
92
  - !ruby/object:Gem::Version
105
- version: 0.2.8
93
+ version: '0'
106
94
  type: :development
107
95
  prerelease: false
108
96
  version_requirements: !ruby/object:Gem::Requirement
109
- none: false
110
97
  requirements:
111
- - - ~>
98
+ - - ">="
112
99
  - !ruby/object:Gem::Version
113
- version: 0.2.8
100
+ version: '0'
114
101
  - !ruby/object:Gem::Dependency
115
- name: webmock
102
+ name: ammeter
116
103
  requirement: !ruby/object:Gem::Requirement
117
- none: false
118
104
  requirements:
119
- - - ! '>='
105
+ - - "~>"
120
106
  - !ruby/object:Gem::Version
121
- version: '0'
107
+ version: 1.1.2
122
108
  type: :development
123
109
  prerelease: false
124
110
  version_requirements: !ruby/object:Gem::Requirement
125
- none: false
126
111
  requirements:
127
- - - ! '>='
112
+ - - "~>"
128
113
  - !ruby/object:Gem::Version
129
- version: '0'
114
+ version: 1.1.2
130
115
  - !ruby/object:Gem::Dependency
131
- name: supermodel
116
+ name: sqlite3
132
117
  requirement: !ruby/object:Gem::Requirement
133
- none: false
134
118
  requirements:
135
- - - ! '>='
119
+ - - ">="
136
120
  - !ruby/object:Gem::Version
137
121
  version: '0'
138
122
  type: :development
139
123
  prerelease: false
140
124
  version_requirements: !ruby/object:Gem::Requirement
141
- none: false
142
125
  requirements:
143
- - - ! '>='
126
+ - - ">="
144
127
  - !ruby/object:Gem::Version
145
128
  version: '0'
146
129
  - !ruby/object:Gem::Dependency
147
130
  name: databasedotcom
148
131
  requirement: !ruby/object:Gem::Requirement
149
- none: false
150
132
  requirements:
151
- - - ! '>='
133
+ - - ">="
152
134
  - !ruby/object:Gem::Version
153
135
  version: '0'
154
136
  type: :runtime
155
137
  prerelease: false
156
138
  version_requirements: !ruby/object:Gem::Requirement
157
- none: false
158
139
  requirements:
159
- - - ! '>='
140
+ - - ">="
160
141
  - !ruby/object:Gem::Version
161
142
  version: '0'
162
143
  description: ActiveRecord extension & rails engine for syncing data with Salesforce.com
@@ -165,50 +146,50 @@ email:
165
146
  - nneufeld@infotech.com
166
147
  - acoates@infotech.com
167
148
  - dnoonan@infotech.com
168
- - lnediger@infotech.com.com
149
+ - lnediger@infotech.com
169
150
  executables: []
170
151
  extensions: []
171
152
  extra_rdoc_files: []
172
153
  files:
173
- - README.md
174
154
  - LICENSE
155
+ - README.md
156
+ - app/controllers/salesforce_ar_sync/soap_message_controller.rb
157
+ - config/routes.rb
175
158
  - lib/generators/salesforce_ar_sync/configuration/configuration_generator.rb
176
159
  - lib/generators/salesforce_ar_sync/configuration/templates/salesforce_ar_sync.yml
177
160
  - lib/generators/salesforce_ar_sync/migrations/migrations_generator.rb
178
161
  - lib/generators/salesforce_ar_sync/migrations/templates/migration.rb
162
+ - lib/salesforce_ar_sync.rb
179
163
  - lib/salesforce_ar_sync/engine.rb
180
164
  - lib/salesforce_ar_sync/extenders/salesforce_syncable.rb
181
165
  - lib/salesforce_ar_sync/ip_constraint.rb
166
+ - lib/salesforce_ar_sync/railtie.rb
182
167
  - lib/salesforce_ar_sync/salesforce_object_sync.rb
183
168
  - lib/salesforce_ar_sync/salesforce_sync.rb
184
169
  - lib/salesforce_ar_sync/soap_handler/base.rb
185
170
  - lib/salesforce_ar_sync/soap_handler/delete.rb
186
171
  - lib/salesforce_ar_sync/version.rb
187
- - lib/salesforce_ar_sync.rb
188
- - app/controllers/salesforce_ar_sync/soap_message_controller.rb
189
- - config/routes.rb
190
172
  homepage: http://github.com/InfoTech/
191
173
  licenses: []
174
+ metadata: {}
192
175
  post_install_message:
193
176
  rdoc_options: []
194
177
  require_paths:
195
178
  - lib
196
179
  required_ruby_version: !ruby/object:Gem::Requirement
197
- none: false
198
180
  requirements:
199
- - - ! '>='
181
+ - - ">="
200
182
  - !ruby/object:Gem::Version
201
183
  version: '0'
202
184
  required_rubygems_version: !ruby/object:Gem::Requirement
203
- none: false
204
185
  requirements:
205
- - - ! '>='
186
+ - - ">="
206
187
  - !ruby/object:Gem::Version
207
188
  version: '0'
208
189
  requirements: []
209
190
  rubyforge_project:
210
- rubygems_version: 1.8.23
191
+ rubygems_version: 2.4.7
211
192
  signing_key:
212
- specification_version: 3
193
+ specification_version: 4
213
194
  summary: ActiveRecord extension & rails engine for syncing data with Salesforce.com
214
195
  test_files: []