activesalesforce 0.1.1 → 0.1.2

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.
@@ -22,3 +22,21 @@
22
22
  =end
23
23
 
24
24
  require 'salesforce_connection_adapter'
25
+
26
+ module ActionView
27
+ module Helpers
28
+ # Provides a set of methods for making easy links and getting urls that depend on the controller and action. This means that
29
+ # you can use the same format for links in the views that you do in the controller. The different methods are even named
30
+ # synchronously, so link_to uses that same url as is generated by url_for, which again is the same url used for
31
+ # redirection in redirect_to.
32
+ module UrlHelper
33
+ def link_to_asf(active_record, column)
34
+ if column.reference_to
35
+ link_to(column.reference_to, { :action => 'show', :controller => column.reference_to.pluralize, :id => active_record.send(column.name) } )
36
+ else
37
+ active_record.send(column.name)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -28,12 +28,22 @@ require 'pp'
28
28
 
29
29
 
30
30
  module ActiveRecord
31
+ module StringHelper
32
+ def column_nameize(s)
33
+ #s ? s.first.downcase + s[1 .. s.length] : nil
34
+ s.underscore
35
+ end
36
+ end
37
+
31
38
  module ConnectionAdapters
32
39
  class SalesforceColumn < Column
33
- attr_reader :label, :readonly, :reference_to
40
+ include StringHelper
41
+
42
+ attr_reader :api_name, :label, :readonly, :reference_to
34
43
 
35
44
  def initialize(field)
36
- @name = field[:name]
45
+ @api_name = field[:name]
46
+ @name = column_nameize(@api_name)
37
47
  @type = get_type(field[:type])
38
48
  @limit = field[:length]
39
49
  @label = field[:label]
@@ -82,28 +92,34 @@ module ActiveRecord
82
92
  end
83
93
 
84
94
  class SalesforceRelationship
85
- attr_reader :name, :foreign_key, :label, :reference_to, :one_to_many, :cascade_delete
95
+ include StringHelper
96
+
97
+ attr_reader :name, :api_name, :foreign_key, :label, :reference_to, :one_to_many, :cascade_delete
86
98
 
87
99
  def initialize(source)
88
100
  if source[:childSObject]
89
101
  relationship = source
90
102
 
91
- @name = relationship[:relationshipName] ? relationship[:relationshipName] : relationship[:field].chop.chop
103
+ @api_name = relationship[:relationshipName] ? relationship[:relationshipName] : relationship[:field].chop.chop
92
104
  @one_to_many = relationship[:relationshipName] != nil
93
105
  @cascade_delete = relationship[:cascadeDelete] == "true"
94
106
  @reference_to = relationship[:childSObject]
95
107
  @label = @name
96
- @foreign_key = relationship[:field]
108
+ @foreign_key = column_nameize(relationship[:field])
97
109
  else
98
110
  field = source
99
111
 
100
- @name = field[:name].chop.chop
112
+ @api_name = field[:name].chop.chop
101
113
  @label = field[:label]
102
114
  @readonly = (field[:updateable] != "true" or field[:createable] != "true")
103
115
  @reference_to = field[:referenceTo]
104
116
  @one_to_many = false
105
117
  @cascade_delete = false
118
+ @foreign_key = column_nameize(field[:name])
106
119
  end
120
+
121
+ @name = column_nameize(@api_name)
122
+
107
123
  end
108
124
  end
109
125
 
data/lib/rforce.rb CHANGED
@@ -202,13 +202,16 @@ module RForce
202
202
  request = encode(request)
203
203
 
204
204
  headers = {
205
- 'Accept-Encoding' => 'gzip',
206
205
  'Connection' => 'Keep-Alive',
207
- 'Content-Encoding' => 'gzip',
208
206
  'Content-Type' => 'text/xml',
209
207
  'SOAPAction' => '""',
210
208
  'User-Agent' => 'ActiveSalesforce RForce'
211
209
  }
210
+
211
+ unless $DEBUG
212
+ headers['Accept-Encoding'] = 'gzip'
213
+ headers['Content-Encoding'] = 'gzip'
214
+ end
212
215
 
213
216
  #Send the request to the server and read the response.
214
217
  response = @server.post2(@url.path, request, headers)
@@ -255,6 +258,8 @@ module RForce
255
258
 
256
259
  # encode gzip
257
260
  def encode(request)
261
+ return request if $DEBUG
262
+
258
263
  begin
259
264
  ostream = StringIO.new
260
265
  gzw = Zlib::GzipWriter.new(ostream)
@@ -27,14 +27,8 @@ require_gem 'rails', ">= 1.0.0"
27
27
  require 'thread'
28
28
 
29
29
  require File.dirname(__FILE__) + '/salesforce_login'
30
- require File.dirname(__FILE__) + '/sobject_attributes'
31
- require File.dirname(__FILE__) + '/salesforce_active_record'
32
30
  require File.dirname(__FILE__) + '/column_definition'
33
31
 
34
- ActiveRecord::Base.class_eval do
35
- include ActiveRecord::SalesforceRecord
36
- end
37
-
38
32
 
39
33
  module ActiveRecord
40
34
  class Base
@@ -75,6 +69,8 @@ module ActiveRecord
75
69
  end
76
70
 
77
71
  class SalesforceAdapter < AbstractAdapter
72
+ include StringHelper
73
+
78
74
  attr_accessor :batch_size
79
75
 
80
76
  def initialize(connection, logger, connection_options, config)
@@ -84,7 +80,7 @@ module ActiveRecord
84
80
 
85
81
  @columns_map = {}
86
82
  @columns_name_map = {}
87
-
83
+
88
84
  @relationships_map = {}
89
85
  end
90
86
 
@@ -109,7 +105,8 @@ module ActiveRecord
109
105
  end
110
106
 
111
107
  def quote_column_name(name) #:nodoc:
112
- "`#{name}`"
108
+ # Mark the column name to make it easier to find later
109
+ "@@#{name}"
113
110
  end
114
111
 
115
112
  def quote_string(string) #:nodoc:
@@ -138,45 +135,124 @@ module ActiveRecord
138
135
 
139
136
  # DATABASE STATEMENTS ======================================
140
137
 
141
- def select_all(soql, name = nil) #:nodoc:
138
+ def select_all(sql, name = nil) #:nodoc:
139
+ table_name = table_name_from_sql(sql)
140
+ entity_name = entity_name_from_table(table_name)
141
+ column_names = api_column_names(table_name)
142
+
143
+ soql = sql.sub(/SELECT \* FROM/, "SELECT #{column_names.join(', ')} FROM")
144
+
145
+ # Look for a LIMIT clause
146
+ soql.sub!(/LIMIT 1/, "")
147
+
148
+ # Fixup column references to use api names
149
+ columns = columns_map(table_name)
150
+ while soql =~ /@@(\w+)/
151
+ column = columns[$~[1]]
152
+ soql = $~.pre_match + column.api_name + $~.post_match
153
+ end
154
+
142
155
  log(soql, name)
143
156
 
144
157
  @connection.batch_size = @batch_size if @batch_size
145
158
  @batch_size = nil
146
159
 
147
160
  records = get_result(@connection.query(:queryString => soql), :query).records
148
-
161
+
149
162
  result = []
150
163
  return result unless records
151
164
 
152
165
  records = [ records ] unless records.is_a?(Array)
153
166
 
154
167
  records.each do |record|
155
- attributes = Salesforce::SObjectAttributes.new(columns_map(record[:type]), record)
156
- result << attributes
168
+ row = {}
169
+
170
+ record.each do |name, value|
171
+ name = column_nameize(name.to_s)
172
+
173
+ # Replace nil element with nil
174
+ value = nil if value.respond_to?(:xmlattr_nil) and value.xmlattr_nil
175
+
176
+ # Ids are returned in an array with 2 duplicate entries...
177
+ value = value[0] if name == "id"
178
+
179
+ row[name] = value
180
+ end
181
+
182
+ result << row
157
183
  end
158
184
 
159
185
  result
160
186
  end
161
187
 
162
188
  def select_one(sql, name = nil) #:nodoc:
189
+ @connection.batch_size = 1
163
190
  result = select_all(sql, name)
164
191
  result.nil? ? nil : result.first
165
192
  end
166
193
 
167
- def create(sobject, name = nil) #:nodoc:
168
- check_result(get_result(@connection.create(sobject), :create))[:id]
169
- end
194
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
195
+ tokens = sql.scan(/[0-9A-Za-z._]+/)
196
+
197
+ # Convert sql to sobject
198
+ table_name = tokens[2]
199
+ entity_name = entity_name_from_table(table_name)
200
+ columns = columns_map(table_name)
201
+
202
+ # Extract array of [column_name, value] pairs
203
+ names = tokens[3 .. tokens.length / 2]
204
+ values = tokens[(tokens.length / 2) + 2 .. tokens.length]
205
+
206
+ fields = {}
207
+ names.length.times do | n |
208
+ name = names[n]
209
+ value = values[n]
210
+ column = columns[name]
211
+
212
+ fields[column.api_name] = value if not column.readonly and value != "NULL"
213
+ end
214
+
215
+ sobject = create_sobject(entity_name, nil, fields)
216
+
217
+ check_result(get_result(@connection.create(:sObjects => sobject), :create))[0][:id]
218
+ end
170
219
 
171
- def update(sobject, name = nil) #:nodoc:
172
- check_result(get_result(@connection.update(sobject), :update))
220
+ def update(sql, name = nil) #:nodoc:
221
+ # Convert sql to sobject
222
+ table_name = sql.match(/UPDATE (\w+) /)[1]
223
+ entity_name = entity_name_from_table(table_name)
224
+ columns = columns_map(table_name)
225
+
226
+ # Extract array of [column_name, value] pairs
227
+ raw_fields = sql.scan(/(\w+) = '([^']*)'/)
228
+
229
+ fields = {}
230
+ raw_fields.each do | name, value |
231
+ column = columns[name]
232
+ fields[column.api_name] = value if not column.readonly or name == "id"
233
+ end
234
+
235
+ id = fields["Id"].match(/[a-zA-Z0-9]+/)[0]
236
+ fields.delete("Id")
237
+
238
+ sobject = create_sobject(entity_name, id, fields)
239
+
240
+ check_result(get_result(@connection.update(:sObjects => sobject), :update))
173
241
  end
242
+
243
+ def delete(sql, name = nil)
244
+ # Extract the ids from the IN () clause
245
+ match = sql.match(/IN \(([^\)]*)\)/)
174
246
 
175
- def delete(ids)
176
- puts "Delete #{ids}"
177
- check_result(get_result(@connection.delete(:ids => ids), :delete))
178
- end
247
+ # If the IN clause was not found fall back to WHERE id = 'blah'
248
+ ids = match ? match[1].scan(/\w+/) : [ sql.match(/WHERE id = '([^\']*)'/)[1] ]
249
+
250
+ ids_element = []
251
+ ids.each { |id| ids_element << :ids << id }
179
252
 
253
+ check_result(get_result(@connection.delete(ids_element), :delete))
254
+ end
255
+
180
256
  def get_result(response, method)
181
257
  responseName = (method.to_s + "Response").to_sym
182
258
  finalResponse = response[responseName]
@@ -187,21 +263,28 @@ module ActiveRecord
187
263
  end
188
264
 
189
265
  def check_result(result)
190
- raise SalesforceError.new(result[:Errors], result[:Errors].Message) unless result[:success] == "true"
266
+ result = [ result ] unless result.is_a?(Array)
267
+
268
+ result.each do |r|
269
+ raise SalesforceError.new(r[:errors], r[:errors].Message) unless r[:success] == "true"
270
+ end
271
+
191
272
  result
192
273
  end
193
-
274
+
194
275
  def columns(table_name, name = nil)
195
- cached_columns = @columns_map[table_name]
276
+ entity_name = entity_name_from_table(table_name)
277
+
278
+ cached_columns = @columns_map[entity_name]
196
279
  return cached_columns if cached_columns
197
280
 
198
281
  cached_columns = []
199
- @columns_map[table_name] = cached_columns
200
-
282
+ @columns_map[entity_name] = cached_columns
283
+
201
284
  cached_relationships = []
202
- @relationships_map[table_name] = cached_relationships
203
-
204
- metadata = get_result(@connection.describeSObject(:sObjectType => table_name), :describeSObject)
285
+ @relationships_map[entity_name] = cached_relationships
286
+
287
+ metadata = get_result(@connection.describeSObject(:sObjectType => entity_name), :describeSObject)
205
288
 
206
289
  metadata.fields.each do |field|
207
290
  column = SalesforceColumn.new(field)
@@ -209,54 +292,109 @@ module ActiveRecord
209
292
 
210
293
  cached_relationships << SalesforceRelationship.new(field) if field[:type] =~ /reference/i
211
294
  end
212
-
295
+
213
296
  if metadata.childRelationships
214
297
  metadata.childRelationships.each do |relationship|
215
- cached_relationships << SalesforceRelationship.new(relationship)
298
+
299
+ if relationship[:childSObject].casecmp(entity_name) == 0
300
+ r = SalesforceRelationship.new(relationship)
301
+ cached_relationships << r
302
+ end
216
303
  end
217
304
  end
218
-
305
+
306
+ configure_active_record entity_name
307
+
219
308
  cached_columns
220
309
  end
221
310
 
311
+ def configure_active_record(entity_name)
312
+ klass = entity_name.constantize
313
+
314
+ klass.table_name = entity_name
315
+ klass.pluralize_table_names = false
316
+ klass.set_inheritance_column nil
317
+ klass.lock_optimistically = false
318
+ klass.record_timestamps = false
319
+ klass.default_timezone = :utc
320
+
321
+ # Create relationships for any reference field
322
+ @relationships_map[entity_name].each do |relationship|
323
+ referenceName = relationship.name
324
+ unless self.respond_to? referenceName.to_sym or relationship.reference_to == "Profile"
325
+ one_to_many = relationship.one_to_many
326
+ foreign_key = relationship.foreign_key
327
+
328
+ if one_to_many
329
+ klass.has_many referenceName.to_sym, :class_name => relationship.reference_to, :foreign_key => foreign_key, :dependent => false
330
+ else
331
+ klass.belongs_to referenceName.to_sym, :class_name => relationship.reference_to, :foreign_key => foreign_key, :dependent => false
332
+ end
333
+
334
+ puts "Created one-to-#{one_to_many ? 'many' : 'one' } relationship '#{referenceName}' from #{entity_name} to #{relationship.reference_to} using #{foreign_key}"
335
+
336
+ end
337
+ end
338
+
339
+ end
340
+
222
341
  def relationships(table_name)
223
- cached_relationships = @relationships_map[table_name]
224
- return cached_relationships if cached_relationships
225
-
226
- # This will load column and relationship metadata
227
- columns(table_name)
228
-
229
- @relationships_map[table_name]
342
+ entity_name = entity_name_from_table(table_name)
343
+
344
+ cached_relationships = @relationships_map[entity_name]
345
+
346
+ unless cached_relationships
347
+ # This will load column and relationship metadata
348
+ columns(table_name)
349
+ end
350
+
351
+ @relationships_map[entity_name]
230
352
  end
231
353
 
232
354
  def columns_map(table_name, name = nil)
233
- columns_map = @columns_name_map[table_name]
234
- return columns_map if columns_map
355
+ entity_name = entity_name_from_table(table_name)
235
356
 
236
- columns_map = {}
237
- @columns_name_map[table_name] = columns_map
238
-
239
- columns(table_name).each { |column| columns_map[column.name] = column }
357
+ columns_map = @columns_name_map[entity_name]
358
+ unless columns_map
359
+ columns_map = {}
360
+ @columns_name_map[entity_name] = columns_map
361
+
362
+ columns(entity_name).each { |column| columns_map[column.name] = column }
363
+ end
240
364
 
241
365
  columns_map
242
366
  end
243
367
 
244
- private
368
+ def entity_name_from_table(table_name)
369
+ return table_name.singularize.camelize
370
+ end
371
+
245
372
 
246
- def select(sql, name = nil)
247
- puts "select(#{sql}, (#{name}))"
248
- @connection.query_with_result = true
249
- result = execute(sql, name)
250
- rows = []
251
- if @null_values_in_each_hash
252
- result.each_hash { |row| rows << row }
253
- else
254
- all_fields = result.fetch_fields.inject({}) { |fields, f| fields[f.name] = nil; fields }
255
- result.each_hash { |row| rows << all_fields.dup.update(row) }
373
+ def create_sobject(entity_name, id, fields)
374
+ sobj = [ 'type { :xmlns => "urn:sobject.partner.soap.sforce.com" }', entity_name ]
375
+ sobj << 'Id { :xmlns => "urn:sobject.partner.soap.sforce.com" }' << id if id
376
+
377
+ # now add any changed fields
378
+ fieldValues = {}
379
+ fields.each do | name, value |
380
+ sobj << name.to_sym << value if value
256
381
  end
257
- result.free
258
- rows
382
+
383
+ sobj
384
+ end
385
+
386
+ def table_name_from_sql(sql)
387
+ sql.match(/FROM (\w+) /)[1]
388
+ end
389
+
390
+ def column_names(table_name)
391
+ columns(table_name).map { |column| column.name }
392
+ end
393
+
394
+ def api_column_names(table_name)
395
+ columns(table_name).map { |column| column.api_name }
259
396
  end
260
397
  end
398
+
261
399
  end
262
400
  end
@@ -8,85 +8,102 @@ class AccountTest < Test::Unit::TestCase
8
8
  ActiveRecord::Base.allow_concurrency = true
9
9
  end
10
10
 
11
-
11
+ if false
12
+ def test_create_account
13
+ dutchCo = Account.new
14
+ dutchCo.name = "DutchCo"
15
+ dutchCo.website = "www.dutchco.com"
16
+ dutchCo.save
17
+
18
+ dutchCo2 = Account.new(:name => "DutchCo2", :website => "www.dutchco2.com")
19
+ dutchCo2.save
20
+
21
+ dutchCo3 = Account.create(:name => "DutchCo3", :website => "www.dutchco3.com")
22
+
23
+ accounts = Account.create([
24
+ { :name => "DutchCo4", :website => "www.dutchco4.com" },
25
+ { :name => "DutchCo5", :website => "www.dutchco5.com" }])
26
+ end
27
+
28
+ def test_create_a_contact
29
+ contact = Contact.find_by_id("0033000000B1LKpAAN")
30
+ contact.first_name = "DutchieBoy"
31
+ contact.save
32
+ end
33
+
34
+
35
+ def test_create_a_contact
36
+ contact = Contact.new
37
+ end
38
+
39
+
12
40
  def test_get_a_case_comment
13
- comment = CaseComment.find_by_ParentId('500300000011inJAAQ')
41
+ comment = CaseComment.find_by_parent_id('500300000011inJAAQ')
14
42
  end
15
43
 
16
-
44
+
17
45
  def test_one_to_many_relationship
18
- contact = Contact.find_by_Id('0033000000B1LKrAAN')
46
+ contact = Contact.find_by_id('0033000000B1LKrAAN')
19
47
 
20
- cases = contact.Cases
48
+ cases = contact.cases
21
49
 
22
50
  cases.each do |c|
23
- puts "Case('#{c.Id}', '#{c.Subject}')"
24
-
25
- comments = c.CaseComments
51
+ puts "Case('#{c.id}', '#{c.subject}')"
26
52
 
53
+ comments = c.case_comments
27
54
  comments.each do |comment|
28
- puts " CaseComment('#{comment.Id}', '#{comment.CommentBody}')"
55
+ puts " CaseComment('#{comment.id}', '#{comment.comment_body}')"
29
56
  end
30
-
31
57
  end
32
58
  end
33
-
34
-
59
+
35
60
  def test_get_account
36
61
  accounts = Account.find(:all)
37
-
38
- #accounts.each { |account| puts "#{account.Name}, #{account.Id}, #{account.LastModifiedById}" }
39
-
40
- acme = Account.find(:first, :conditions => ["Name = 'Acme'"])
41
-
42
- acme = Account.find_by_Id(acme.Id)
43
-
44
- acme = Account.find_by_Name_and_LastModifiedById('salesforce.com', acme.LastModifiedById)
62
+
63
+ accounts.each { |account| puts "#{account.name}, #{account.id}, #{account.last_modified_by_id}" }
64
+
65
+ acme = Account.find(:first, :conditions => ["name = 'Acme'"])
66
+
67
+ acme = Account.find_by_id(acme.id)
68
+
69
+ acme = Account.find_by_name_and_last_modified_by_id('salesforce.com', acme.last_modified_by_id)
45
70
  end
46
-
47
-
71
+
48
72
  def test_update_account
49
73
  acme = Account.new
50
- acme.Name = "Acme"
74
+ acme.name = "Acme"
51
75
  acme.save
52
76
 
53
- acme = Account.find_by_Name('Acme')
54
-
55
- acme.Website = "http://www.dutchforce.com/#{Time.now}.jpg"
56
- acme.LastModifiedDate = Time.now
77
+ acme = Account.find_by_name('Acme')
78
+
79
+ acme.website = "http://www.dutchforce.com/#{Time.now}.jpg"
80
+ acme.last_modified_date = Time.now
57
81
 
58
82
  acme.save
59
83
  end
60
-
61
-
62
- def test_create_account
63
- dutchCo = Account.new
64
- dutchCo.Name = "DutchCo"
65
- dutchCo.Website = "www.dutchco.com"
66
- dutchCo.save
67
-
68
- dutchCo2 = Account.new(:Name => "DutchCo2", :Website => "www.dutchco2.com")
69
- dutchCo2.save
70
-
71
- dutchCo3 = Account.create(:Name => "DutchCo3", :Website => "www.dutchco3.com")
72
-
73
- accounts = Account.create([
74
- { :Name => "DutchCo4", :Website => "www.dutchco4.com" },
75
- { :Name => "DutchCo5", :Website => "www.dutchco5.com" }])
76
- end
77
-
84
+ end
85
+
78
86
  def test_destroy_account
79
- account = Account.create(:Name => "DutchADelete", :Website => "www.dutchcodelete.com")
80
- account = Account.find_by_Id(account.Id)
87
+ Account.new
81
88
 
82
- pp account.Parent
89
+ account = Account.create(:name => "DutchADelete", :website => "www.dutchcodelete.com")
90
+ account2 = Account.create(:name => "DutchADelete2", :website => "www.dutchcodelete2.com")
91
+
92
+ #pp account
93
+
94
+ account = Account.find_by_id(account.id)
95
+
96
+ pp account.parent
83
97
 
84
98
  puts "Getting CreatedBy"
85
- createdBy = account.CreatedBy
86
- createdBy = User.find_by_Id(account.CreatedById);
87
- puts createdBy.Email
88
-
89
- Account.delete(account.Id)
99
+ createdBy = account.created_by
100
+ createdBy = User.find_by_id(account.created_by_id);
101
+ puts createdBy.email
102
+
103
+ Account.delete([account.id, account2.id])
104
+
105
+ account3 = Account.create(:name => "DutchADelete3", :website => "www.dutchcodelete3.com")
106
+ account3.destroy
90
107
  end
91
-
108
+
92
109
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: activesalesforce
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.1.1
7
- date: 2006-01-30 00:00:00 -05:00
6
+ version: 0.1.2
7
+ date: 2006-02-02 00:00:00 -05:00
8
8
  summary: ActiveSalesforce is an extension to the Rails Framework that allows for the dynamic creation and management of ActiveRecord objects through the use of Salesforce meta-data and uses a Salesforce.com organization as the backing store.
9
9
  require_paths:
10
10
  - lib
@@ -29,14 +29,11 @@ authors:
29
29
  - Doug Chasman
30
30
  files:
31
31
  - lib/salesforce_login.rb
32
- - lib/salesforce_active_record.rb
33
32
  - lib/column_definition.rb
34
33
  - lib/active_salesforce.rb
35
34
  - lib/rforce.rb
36
- - lib/sobject_attributes.rb
37
35
  - lib/salesforce_connection_adapter.rb
38
36
  - test/unit
39
- - test/unit/sobject_attributes_test.rb
40
37
  - test/unit/account_test.rb
41
38
  - README
42
39
  test_files: []
@@ -1,151 +0,0 @@
1
- =begin
2
- ActiveSalesforce
3
- Copyright (c) 2006 Doug Chasman
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
22
- =end
23
-
24
- require File.dirname(__FILE__) + '/sobject_attributes'
25
-
26
-
27
- module ActiveRecord
28
- # Active Records will automatically record creation and/or update timestamps of database objects
29
- # if fields of the names created_at/created_on or updated_at/updated_on are present. This module is
30
- # automatically included, so you don't need to do that manually.
31
- #
32
- # This behavior can be turned off by setting <tt>ActiveRecord::Base.record_timestamps = false</tt>.
33
- # This behavior can use GMT by setting <tt>ActiveRecord::Base.timestamps_gmt = true</tt>
34
- module SalesforceRecord
35
-
36
- def self.append_features(base) # :nodoc:
37
- super
38
-
39
- base.class_eval do
40
- alias_method :create, :create_with_sforce_api
41
- alias_method :update, :update_with_sforce_api
42
- end
43
- end
44
-
45
- def create_with_sforce_api
46
- return if not @attributes.changed?
47
- puts "create_with_sforce_api creating #{self.class}"
48
- id = connection.create(:sObjects => create_sobject())
49
- self.Id = id
50
- @attributes.clear_changed!
51
- end
52
-
53
- def update_with_sforce_api
54
- return if not @attributes.changed?
55
- puts "update_with_sforce_api updating #{self.class}('#{self.Id}')"
56
- connection.update(:sObjects => create_sobject())
57
- @attributes.clear_changed!
58
- end
59
-
60
- def create_sobject()
61
- fields = @attributes.changed_fields
62
-
63
- sobj = [ 'type { :xmlns => "urn:sobject.partner.soap.sforce.com" }', self.class.table_name ]
64
- sobj << 'Id { :xmlns => "urn:sobject.partner.soap.sforce.com" }' << self.Id if self.Id
65
-
66
- # now add any changed fields
67
- fieldValues = {}
68
- fields.each do |fieldName|
69
- value = @attributes[fieldName]
70
- sobj << fieldName.to_sym << value if value
71
- end
72
-
73
- sobj
74
- end
75
-
76
- end
77
-
78
- class Base
79
- set_inheritance_column nil
80
- lock_optimistically = false
81
- record_timestamps = false
82
- default_timezone = :utc
83
-
84
- def after_initialize()
85
- sfdcObjectName = self.class.table_name
86
- if not @attributes.is_a?(Salesforce::SObjectAttributes)
87
- # Insure that SObjectAttributes is always used for our attributes
88
- originalAttributes = @attributes
89
-
90
- @attributes = Salesforce::SObjectAttributes.new(connection.columns_map(sfdcObjectName))
91
-
92
- originalAttributes.each { |name, value| self[name] = value }
93
- end
94
-
95
- # Create relationships for any reference field
96
- connection.relationships(sfdcObjectName).each do |relationship|
97
- referenceName = relationship.name
98
- unless self.respond_to? referenceName.to_sym or relationship.reference_to == "Profile"
99
- one_to_many = relationship.one_to_many
100
-
101
- puts "Creating one-to-#{one_to_many ? 'many' : 'one' } relationship '#{referenceName}' from #{sfdcObjectName} to #{relationship.reference_to}"
102
-
103
- if one_to_many
104
- self.class.has_many referenceName.to_sym, :class_name => relationship.reference_to, :foreign_key => relationship.foreign_key, :dependent => false
105
- else
106
- self.class.belongs_to referenceName.to_sym, :class_name => relationship.reference_to, :foreign_key => relationship.name, :dependent => false
107
- end
108
- end
109
- end
110
- end
111
-
112
- def self.table_name
113
- # Undo weird camilization that messes with custom object names
114
- name = self.name
115
- name.last(6) == "Custom" ? name.first(name.length - 6) << "__c" : name
116
- end
117
-
118
- def self.primary_key
119
- "Id"
120
- end
121
-
122
- def self.construct_finder_sql(options)
123
- soql = "SELECT #{column_names.join(', ')} FROM #{table_name} "
124
- add_conditions!(soql, options[:conditions])
125
- soql
126
- end
127
-
128
- def self.construct_conditions_from_arguments(attribute_names, arguments)
129
- conditions = []
130
- attribute_names.each_with_index { |name, idx| conditions << "#{name} #{attribute_condition(arguments[idx])} " }
131
- [ conditions.join(" AND "), *arguments[0...attribute_names.length] ]
132
- end
133
-
134
- def self.count(conditions = nil, joins = nil)
135
- soql = "SELECT Id FROM #{table_name} "
136
- add_conditions!(soql, conditions)
137
-
138
- count_by_sql(soql)
139
- end
140
-
141
- def self.count_by_sql(soql)
142
- connection.batch_size = 1
143
- connection.select_all(soql, "#{name} Count").length
144
- end
145
-
146
- def self.delete(ids)
147
- connection.delete(ids)
148
- end
149
-
150
- end
151
- end
@@ -1,132 +0,0 @@
1
- =begin
2
- ActiveSalesforce
3
- Copyright (c) 2006 Doug Chasman
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
22
- =end
23
-
24
- require 'set'
25
-
26
-
27
- module Salesforce
28
-
29
- class SObjectAttributes
30
- include Enumerable
31
-
32
- def initialize(columns, record = nil)
33
- @columns = columns
34
- @values = {}
35
-
36
- if record
37
- record.each do |name, value|
38
- # Replace nil element with nil
39
- value = nil if value.respond_to?(:xmlattr_nil) and value.xmlattr_nil
40
-
41
- # Ids are returned in an array with 2 duplicate entries...
42
- value = value[0] if name == :Id
43
-
44
- self[name.to_s] = value
45
- end
46
- else
47
- columns.values.each { |column| self[column.name] = nil }
48
- end
49
-
50
- clear_changed!
51
- end
52
-
53
- def [](key)
54
- @values[key].freeze
55
- end
56
-
57
- def []=(key, value)
58
- column = @columns[key]
59
- return unless column
60
-
61
- value = nil if value == ""
62
-
63
- return if @values[key] == value and @values.include?(key)
64
-
65
- originalClass = value.class
66
- originalValue = value
67
-
68
- if value
69
- # Convert strings representation of dates and datetimes to date and time objects
70
- case column.type
71
- when :date
72
- value = value.is_a?(Date) ? value : Date.parse(value)
73
- when :datetime
74
- value = value.is_a?(Time) ? value : Time.parse(value)
75
- else
76
- value = column.type_cast(value)
77
- end
78
- end
79
-
80
- @values[key] = value
81
-
82
- #puts "setting #{key} = #{value} [#{originalValue}] (#{originalClass}, #{value.class})"
83
-
84
- if not column.readonly
85
- @changed = Set.new unless @changed
86
- @changed.add(key)
87
- end
88
- end
89
-
90
- def include?(key)
91
- @values.include?(key)
92
- end
93
-
94
- def has_key?(key)
95
- @values.has_key?(key)
96
- end
97
-
98
- def length
99
- @values.length
100
- end
101
-
102
- def keys
103
- @values.keys
104
- end
105
-
106
- def clear
107
- @values.clear
108
- clear_changed
109
- end
110
-
111
- def clear_changed!
112
- @changed = nil
113
- end
114
-
115
- def changed?
116
- @changed != nil
117
- end
118
-
119
- def changed_fields
120
- @changed
121
- end
122
-
123
-
124
- # Enumerable support
125
-
126
- def each(&block)
127
- @values.each(&block)
128
- end
129
-
130
- end
131
-
132
- end
@@ -1,38 +0,0 @@
1
- puts "Yahoo"
2
-
3
- require 'test/unit'
4
- require File.dirname(__FILE__) + '/../../src/sobject_attributes'
5
-
6
- class SobjectAttributesTest < Test::Unit::TestCase
7
-
8
- def setup
9
- @attributes = Salesforce::SObjectAttributes.new
10
- end
11
-
12
- def test_add_values()
13
- assert((not @attributes.changed?))
14
-
15
- @attributes['name'] = 'value'
16
- assert(@attributes.changed?)
17
-
18
- assert_equal('value', @attributes['name'])
19
-
20
- assert_equal(Set.new('name'), @attributes.changed_fields)
21
-
22
- @attributes.clear_changed!
23
- assert((not @attributes.changed?))
24
-
25
- assert_equal('value', @attributes['name'])
26
- end
27
-
28
- def test_enumeration
29
- 10.times { |n| @attributes["name_#{n}"] = "value_#{n}" }
30
-
31
- assert_equal(10, @attributes.length)
32
-
33
- 5.times { |n| @attributes["name_#{n + 10}"] = "value_#{n + 10}" }
34
-
35
- @attributes.each { |name, value| assert_equal(name[/_\d/], value[/_\d/]) }
36
- end
37
-
38
- end