salesforce_ar_sync 1.0.1 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -46,8 +46,10 @@ properly. Make sure each of the models you wish to sync are materialized.
46
46
  ````ruby
47
47
  $sf_client = Databasedotcom::Client.new("config/databasedotcom.yml")
48
48
  $sf_client.authenticate :username => <username>, :password => <password>
49
- $sf_client.sobject_module = Databasedotcom
50
- $sf_client.materialize "User"
49
+
50
+ module SalesforceArSync::SalesforceSync
51
+ SF_CLIENT = $sf_client
52
+ end
51
53
  ````
52
54
 
53
55
  ### Gem Installation
@@ -140,8 +142,7 @@ The model can have several options set:
140
142
  [__web_id_attribute_name__](#web_id_attribute_name)
141
143
  [__salesforce_sync_web_id__](#salesforce_sync_web_id)
142
144
  [__web_class_name__](#web_class_name)
143
- [__salesforce_object_name__](#salesforce_object_name)
144
- [__salesforce_object__](#salesforce_object)
145
+ [__salesforce_object_name__](#salesforce_object_name)
145
146
  [__except__](#except)
146
147
 
147
148
  #### <a id="salesforce_sync_enabled"></a>salesforce_sync_enabled
@@ -216,14 +217,6 @@ Optionally holds the name of a method which will return the name of the Salesfor
216
217
  :salesforce_object_name => :salesforce_object_name_method_name
217
218
  ````
218
219
 
219
- #### salesforce_object
220
- Optionally holds the name of a method which will retrieve a Salesforce object. Default implementation is called if no
221
- method is specified, defaults to nil.
222
-
223
- ````ruby
224
- :salesforce_object => :salesforce_object_method_name
225
- ````
226
-
227
220
  #### except
228
221
  Optionally holds the name of a method which can contain logic to determine if a record should be synced on save. If no
229
222
  method is given then only the salesforce_skip_sync attribute is used. Defaults to nil.
@@ -367,9 +360,9 @@ class Contact < ActiveRecord::Base
367
360
  attr_accessor :first_name, :last_name, :phone, :email, :last_login_time
368
361
 
369
362
  salesforce_syncable :sync_attributes => {:FirstName => :first_name, :LastName => :last_name, :Phone => :phone, :Email => :email},
370
- :salesforce_object => :custom_salesforce_object
363
+ :salesforce_object_name => :custom_salesforce_object_name
371
364
 
372
- def custom_salesforce_object
365
+ def custom_salesforce_object_name
373
366
  "CustomContact__c"
374
367
  end
375
368
  end
@@ -0,0 +1,31 @@
1
+ module SalesforceArSync
2
+ class SoapMessageController < ::ApplicationController
3
+ before_filter :validate_ip_ranges
4
+
5
+ def sync_object
6
+ delayed_soap_handler SalesforceArSync::SoapHandler::Base
7
+ end
8
+
9
+ def delete
10
+ delayed_soap_handler SalesforceArSync::SoapHandler::Delete
11
+ end
12
+
13
+ private
14
+
15
+ def delayed_soap_handler (klass, priority = 90)
16
+ begin
17
+ soap_handler = klass.new(SalesforceArSync.config["ORGANIZATION_ID"], params)
18
+ soap_handler.process_notifications(priority) if soap_handler.sobjects
19
+ render :xml => soap_handler.generate_response, :status => :created
20
+ rescue Exception => ex
21
+ render :xml => soap_handler.generate_response(ex), :status => :created
22
+ end
23
+ end
24
+
25
+ # to be used in a before_filter, checks ip ranges specified in configuration
26
+ # and renders a 404 unless the request matches
27
+ def validate_ip_ranges
28
+ raise ActionController::RoutingError.new('Not Found') unless SalesforceArSync::IPConstraint.new.matches?(request)
29
+ end
30
+ end
31
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,4 @@
1
+ SalesforceArSync::Engine.routes.draw do
2
+ match '/sf_soap/delete' => 'salesforce_ar_sync/soap_message#delete', :via => :post
3
+ match '/sf_soap/*klass' => 'salesforce_ar_sync/soap_message#sync_object', :via => :post
4
+ end
@@ -6,16 +6,12 @@ module SalesforceArSync
6
6
 
7
7
  #Load the configuration from the environment or a yaml file or disable it if no config present
8
8
  SalesforceArSync.config = Hash.new
9
-
10
9
  #load the config file if we have it
11
10
  if FileTest.exist?("#{Rails.root}/config/salesforce_ar_sync.yml")
12
- config = YAML.load_file("#{Rails.root}/config/salesforce_ar_sync.yml")
13
- config = config[Rails.env]
14
- if config['organization_id'].present? && config['ip_ranges'].present? && config['sync_enabled'].present?
15
- SalesforceArSync.config["ORGANIZATION_ID"] = config['organization_id']
16
- SalesforceArSync.config["SYNC_ENABLED"] = config['sync_enabled']
17
- SalesforceArSync.config["IP_RANGES"] = config['ip_ranges'].split(',').map{ |ip| ip.strip }
18
- end
11
+ config = YAML.load_file("#{Rails.root}/config/salesforce_ar_sync.yml")[Rails.env]
12
+ SalesforceArSync.config["ORGANIZATION_ID"] = config['organization_id']
13
+ SalesforceArSync.config["SYNC_ENABLED"] = config['sync_enabled']
14
+ SalesforceArSync.config["IP_RANGES"] = config['ip_ranges'].split(',').map{ |ip| ip.strip }
19
15
  end
20
16
 
21
17
  #if we have ENV flags prefer them
@@ -30,4 +26,4 @@ module SalesforceArSync
30
26
  end
31
27
  end
32
28
  end
33
- end
29
+ end
@@ -15,12 +15,11 @@ module SalesforceArSync
15
15
  self.salesforce_web_class_name = options.has_key?(:web_class_name) ? options[:web_class_name] : self.name
16
16
 
17
17
  self.salesforce_object_name_method = options.has_key?(:salesforce_object_name) ? options[:salesforce_object_name] : nil
18
- self.salesforce_object_method = options.has_key?(:salesforce_object) ? options[:salesforce_object] : nil
19
18
  self.salesforce_skip_sync_method = options.has_key?(:except) ? options[:except] : nil
20
19
 
21
20
  instance_eval do
22
21
  before_save :salesforce_sync
23
- after_create :sync_web_id
22
+ after_create :sync_web_id
24
23
 
25
24
  def salesforce_sync_web_id?
26
25
  self.salesforce_sync_web_id
@@ -35,13 +34,6 @@ module SalesforceArSync
35
34
  return self.class.name
36
35
  end
37
36
 
38
- # Calls a method, if provided, to retrieve an object from Salesforce. Calls the default implementation if
39
- # no custom method is specified
40
- def salesforce_object
41
- return send(self.class.salesforce_object_method) if self.class.salesforce_object_method.present?
42
- return send(:salesforce_object_default)
43
- end
44
-
45
37
  # Calls a method, if provided, to determine if a record should be synced to Salesforce.
46
38
  # The salesforce_skip_sync instance variable is also used.
47
39
  # The SALESFORCE_AR_SYNC_ENABLED flag overrides all the others if set to false
@@ -54,4 +46,4 @@ module SalesforceArSync
54
46
  end
55
47
  end
56
48
  end
57
- end
49
+ end
@@ -1,16 +1,13 @@
1
1
  module SalesforceArSync
2
2
  # simple object to be serialized when asynchronously sending data to Salesforce
3
- class SalesforceObjectSync < Struct.new(:web_object_name, :salesforce_object_name, :salesforce_id, :attributes)
3
+ class SalesforceObjectSync < Struct.new(:web_object_name, :salesforce_id, :attributes)
4
4
  def perform
5
- sf_object = "Databasedotcom::#{salesforce_object_name}".constantize.find_by_Id salesforce_id
6
-
7
- if sf_object
8
- sf_object.update_attributes(attributes)
9
- sf_object.reload
10
-
11
- web_object = "#{web_object_name}".constantize.find_by_salesforce_id salesforce_id
12
- web_object.update_attribute(:salesforce_updated_at, sf_object.SystemModstamp) unless web_object.nil?
5
+ web_object = "#{web_object_name}".constantize.find_by_salesforce_id salesforce_id
6
+ #object exists in salesforce if we call its system_mod_stamp
7
+ if (system_mod_stamp = web_object.system_mod_stamp)
8
+ web_object.salesforce_update_object(web_object.salesforce_attributes_to_update(true))
9
+ web_object.update_attribute(:salesforce_updated_at, system_mod_stamp)
13
10
  end
14
11
  end
15
12
  end
16
- end
13
+ end
@@ -39,11 +39,7 @@ module SalesforceArSync
39
39
 
40
40
  # Optionally holds the name of a method which will return the name of the Salesforce object to sync to
41
41
  attr_accessor :salesforce_object_name_method
42
-
43
- # Optionally holds the name of a method which will retrieve a Salesforce object. Default implementation is called
44
- # if no method is specified
45
- attr_accessor :salesforce_object_method
46
-
42
+
47
43
  # Optionally holds the name of a method which can contain logic to determine if a record should be synced on save.
48
44
  # If no method is given then only the salesforce_skip_sync attribute is used.
49
45
  attr_accessor :salesforce_skip_sync_method
@@ -116,16 +112,22 @@ module SalesforceArSync
116
112
  self.save!
117
113
  end
118
114
 
119
- # This method will fetch the salesforce object and go looking for it if it doesn't exist
120
- def salesforce_object_default
121
- return @sf_object if @sf_object.present?
122
-
123
- @sf_object = "Databasedotcom::#{self.salesforce_object_name}".constantize.send("find_by_#{self.class.salesforce_id_attribute_name.to_s}", salesforce_id) unless salesforce_id.nil?
115
+ # def salesforce_object_exists?
116
+ # return salesforce_object_exists_method if respond_to? salesforce_exists_method
117
+ # return salesforce_object_exists_default
118
+ # end
119
+
120
+
121
+ # Finds a salesforce record by its Id and returns nil or its SystemModstamp
122
+ def system_mod_stamp
123
+ 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")
125
+ end
124
126
 
125
- # Look up and link object by web id
126
- @sf_object ||= "Databasedotcom::#{self.salesforce_object_name}".constantize.send("find_by_#{self.class.salesforce_web_id_attribute_name.to_s}", id) if self.class.salesforce_sync_web_id? && !new_record?
127
127
 
128
- return @sf_object
128
+ def salesforce_object_exists?
129
+ return @exists_in_salesforce if @exists_in_salesforce
130
+ @exists_in_salesforce = !system_mod_stamp.nil?
129
131
  end
130
132
 
131
133
  # Checks if the passed in attribute should be updated in Salesforce.com
@@ -157,12 +159,14 @@ module SalesforceArSync
157
159
 
158
160
  def salesforce_create_object(attributes)
159
161
  attributes.merge!(self.class.salesforce_web_id_attribute_name.to_s => id) if self.class.salesforce_sync_web_id? && !new_record?
160
- @sf_object = "Databasedotcom::#{self.salesforce_object_name}".constantize.create(attributes)
162
+ result = SF_CLIENT.http_post("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}", attributes.to_json)
163
+ self.salesforce_id = JSON.parse(result.body)["id"]
164
+ @exists_in_salesforce = true
161
165
  end
162
166
 
163
167
  def salesforce_update_object(attributes)
164
168
  attributes.merge!(self.class.salesforce_web_id_attribute_name.to_s => id) if self.class.salesforce_sync_web_id? && !new_record?
165
- salesforce_object.update_attributes(attributes) if salesforce_object
169
+ SF_CLIENT.http_patch("/services/data/v#{SF_CLIENT.version}/sobjects/#{salesforce_object_name}/#{salesforce_id}", attributes.to_json)
166
170
  end
167
171
 
168
172
  # if attributes specified in the async_attributes array are the only attributes being modified, then sync the data
@@ -175,23 +179,24 @@ module SalesforceArSync
175
179
  # sync model data to Salesforce, adding any Salesforce validation errors to the models errors
176
180
  def salesforce_sync
177
181
  return if self.salesforce_skip_sync?
178
-
179
182
  if salesforce_perform_async_call?
180
- Delayed::Job.enqueue(SalesforceArSync::SalesforceObjectSync.new(self.class.salesforce_web_class_name, self.salesforce_object_name, salesforce_id, salesforce_attributes_to_update), :priority => 50)
183
+ Delayed::Job.enqueue(SalesforceArSync::SalesforceObjectSync.new(self.class.salesforce_web_class_name, salesforce_id, salesforce_attributes_to_update), :priority => 50)
181
184
  else
182
- salesforce_update_object(salesforce_attributes_to_update) if salesforce_attributes_to_update.present? && salesforce_object
183
- salesforce_create_object(salesforce_attributes_to_update(!new_record?)) if salesforce_id.nil? && salesforce_object.nil?
184
-
185
- self.salesforce_id = salesforce_object.send(self.class.salesforce_id_attribute_name) unless salesforce_object.nil?
185
+ if salesforce_object_exists?
186
+ salesforce_update_object(salesforce_attributes_to_update) if salesforce_attributes_to_update.present?
187
+ else
188
+ salesforce_create_object(salesforce_attributes_to_update(!new_record?)) if salesforce_id.nil?
189
+ end
186
190
  end
187
191
  rescue Exception => ex
188
192
  self.errors[:base] << ex.message
189
193
  return false
190
194
  end
191
-
192
- def sync_web_id
195
+
196
+ def sync_web_id
193
197
  return false if !self.class.salesforce_sync_web_id? || self.salesforce_skip_sync?
194
- salesforce_object.update_attribute(self.class.salesforce_web_id_attribute_name.to_s, id) if salesforce_id && salesforce_object
195
- end
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
199
+ end
200
+
196
201
  end
197
- end
202
+ end
@@ -1,3 +1,3 @@
1
1
  module SalesforceArSync
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: salesforce_ar_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2012-09-24 00:00:00.000000000 Z
16
+ date: 2013-04-24 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: rails
@@ -185,6 +185,8 @@ files:
185
185
  - lib/salesforce_ar_sync/soap_handler/delete.rb
186
186
  - lib/salesforce_ar_sync/version.rb
187
187
  - lib/salesforce_ar_sync.rb
188
+ - app/controllers/salesforce_ar_sync/soap_message_controller.rb
189
+ - config/routes.rb
188
190
  homepage: http://github.com/InfoTech/
189
191
  licenses: []
190
192
  post_install_message:
@@ -205,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
205
207
  version: '0'
206
208
  requirements: []
207
209
  rubyforge_project:
208
- rubygems_version: 1.8.24
210
+ rubygems_version: 1.8.23
209
211
  signing_key:
210
212
  specification_version: 3
211
213
  summary: ActiveRecord extension & rails engine for syncing data with Salesforce.com