salesforce_ar_sync 1.0.1 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
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