activesalesforce 0.2.6 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
data/lib/asf_adapter.rb CHANGED
@@ -44,18 +44,25 @@ module ActiveRecord
44
44
 
45
45
  # Establishes a connection to the database that's used by all Active Record objects.
46
46
  def self.activesalesforce_connection(config) # :nodoc:
47
- puts "Using ActiveSalesforce connection!"
47
+ puts "\nUsing ActiveSalesforce connection\n"
48
48
 
49
49
  url = config[:url]
50
50
  sid = config[:sid]
51
+ binding = config[:binding] if config[:binding]
51
52
 
53
+ if binding
54
+ puts " via provided binding #{binding}\n"
55
+ #pp binding
56
+ end
57
+
52
58
  if sid
53
- connection = @@cache["sid=#{sid}"]
54
- unless connection
59
+ binding = @@cache["sid=#{sid}"] unless binding
60
+
61
+ unless binding
55
62
  puts "Establishing new connection for [sid='#{sid}']"
56
63
 
57
- connection = RForce::Binding.new(url, sid)
58
- @@cache["sid=#{sid}"] = connection
64
+ binding = RForce::Binding.new(url, sid)
65
+ @@cache["sid=#{sid}"] = binding
59
66
 
60
67
  puts "Created new connection for [sid='#{sid}']"
61
68
  end
@@ -71,19 +78,20 @@ module ActiveRecord
71
78
  username = config[:username]
72
79
  password = config[:password]
73
80
 
74
- connection = @@cache["#{url}.#{username}.#{password}"]
75
- unless connection
81
+ binding = @@cache["#{url}.#{username}.#{password}"] unless binding
82
+
83
+ unless binding
76
84
  puts "Establishing new connection for ['#{url}', '#{username}']"
77
85
 
78
- connection = RForce::Binding.new(url)
79
- connection.login(username, password).result
86
+ binding = RForce::Binding.new(url, sid)
87
+ binding.login(username, password).result
80
88
 
81
- @@cache["#{url}.#{username}.#{password}"] = connection
89
+ @@cache["#{url}.#{username}.#{password}"] = binding
82
90
 
83
91
  puts "Created new connection for ['#{url}', '#{username}']"
84
92
  end
85
93
 
86
- ConnectionAdapters::SalesforceAdapter.new(connection, logger, [url, username, password], config)
94
+ ConnectionAdapters::SalesforceAdapter.new(binding, logger, [url, username, password, sid], config)
87
95
  end
88
96
  end
89
97
  end
@@ -98,7 +106,7 @@ module ActiveRecord
98
106
 
99
107
  @fault = fault
100
108
 
101
- logger.debug("\nSalesforceError:\n message='#{message}'\n fault='#{fault}'\n\n")
109
+ logger.debug("\nSalesforceError:\n message='#{message}'\n fault='#{fault}'\n\n") if logger
102
110
  end
103
111
  end
104
112
 
@@ -290,8 +298,8 @@ module ActiveRecord
290
298
  values = values.scan(/(((NULL))|((TRUE))|((FALSE))|'(([^']|'')*)'),*/mi)
291
299
 
292
300
  values.map! { |v| v[7] }
293
-
294
- fields = get_fields(columns, names, values)
301
+
302
+ fields = get_fields(columns, names, values, :createable)
295
303
 
296
304
  sobject = create_sobject(entity_name, nil, fields)
297
305
 
@@ -314,7 +322,7 @@ module ActiveRecord
314
322
  values = match.scan(/=\s*(((NULL))|((TRUE))|((FALSE))|'(([^']|'')*)'),*/mi)
315
323
  values.map! { |v| v[7] }
316
324
 
317
- fields = get_fields(columns, names, values)
325
+ fields = get_fields(columns, names, values, :updateable)
318
326
 
319
327
  id = sql.match(/WHERE\s+id\s*=\s*'(\w+)'/i)[1]
320
328
 
@@ -341,13 +349,11 @@ module ActiveRecord
341
349
  ids_element = []
342
350
  ids.each { |id| ids_element << :ids << id }
343
351
 
344
- pp ids_element
345
-
346
352
  check_result(get_result(@connection.delete(ids_element), :delete))
347
353
  end
348
354
 
349
355
 
350
- def get_fields(columns, names, values)
356
+ def get_fields(columns, names, values, access_check)
351
357
  fields = {}
352
358
  names.each_with_index do | name, n |
353
359
  value = values[n]
@@ -359,7 +365,8 @@ module ActiveRecord
359
365
 
360
366
  value.gsub!(/''/, "'") if value.is_a? String
361
367
 
362
- fields[column.api_name] = value unless column.readonly or value.empty?
368
+ include_field = ((not value.empty?) and column.send(access_check))
369
+ fields[column.api_name] = value if include_field
363
370
  end
364
371
  end
365
372
 
@@ -404,15 +411,18 @@ module ActiveRecord
404
411
  custom = true
405
412
  end
406
413
 
407
- metadata.fields.each do |field|
414
+ metadata[:fields].each do |field|
408
415
  column = SalesforceColumn.new(field)
409
416
  cached_columns << column
410
417
 
411
418
  cached_relationships << SalesforceRelationship.new(field, column) if field[:type] =~ /reference/i
412
419
  end
413
420
 
414
- if metadata.childRelationships
415
- metadata.childRelationships.each do |relationship|
421
+ relationships = metadata[:childRelationships]
422
+ if relationships
423
+ relationships = [ relationships ] unless relationships.is_a? Array
424
+
425
+ relationships.each do |relationship|
416
426
  if relationship[:cascadeDelete] == "true"
417
427
  r = SalesforceRelationship.new(relationship)
418
428
  cached_relationships << r
@@ -443,28 +453,40 @@ module ActiveRecord
443
453
  referenceName = relationship.name
444
454
  unless self.respond_to? referenceName.to_sym or relationship.reference_to == "Profile"
445
455
  reference_to = relationship.reference_to
456
+ one_to_many = relationship.one_to_many
457
+ foreign_key = relationship.foreign_key
458
+
459
+ # DCHASMAN TODO Figure out how to handle polymorphic refs (e.g. Note.parent can refer to
460
+ # Account, Contact, Opportunity, Contract, Asset, Product2, <CustomObject1> ... <CustomObject(n)>
461
+ if reference_to.is_a? Array
462
+ puts " Skipping unsupported polymophic one-to-#{one_to_many ? 'many' : 'one' } relationship '#{referenceName}' from #{entity_name} to [#{relationship.reference_to.join(', ')}] using #{foreign_key}"
463
+ next
464
+ end
465
+
466
+ # Handle references to custom objects
467
+ if reference_to.match(/__c$/)
468
+ reference_to.chop!.chop!.chop!
469
+ reference_to.capitalize!
470
+ end
446
471
 
447
472
  begin
448
- reference_to.constantize
473
+ referenced_klass = reference_to.constantize
449
474
  rescue NameError => e
450
475
  # Automatically create a least a stub for the referenced entity
476
+ puts " Creating ActiveRecord stub for the referenced entity '#{reference_to}'"
477
+
451
478
  referenced_klass = klass.class_eval("::#{reference_to} = Class.new(ActiveRecord::Base)")
452
- referenced_klass.connection = klass.connection
453
- #configure_active_record(get_entity_def(reference_to))
454
479
 
455
- puts "Created ActiveRecord stub for the referenced entity '#{reference_to}'"
480
+ # configure_active_record(get_entity_def(reference_to))
456
481
  end
457
-
458
- one_to_many = relationship.one_to_many
459
- foreign_key = relationship.foreign_key
460
-
482
+
461
483
  if one_to_many
462
484
  klass.has_many referenceName.to_sym, :class_name => reference_to, :foreign_key => foreign_key, :dependent => false
463
485
  else
464
486
  klass.belongs_to referenceName.to_sym, :class_name => reference_to, :foreign_key => foreign_key, :dependent => false
465
487
  end
466
488
 
467
- puts "Created one-to-#{one_to_many ? 'many' : 'one' } relationship '#{referenceName}' from #{entity_name} to #{relationship.reference_to} using #{foreign_key}"
489
+ puts " Created one-to-#{one_to_many ? 'many' : 'one' } relationship '#{referenceName}' from #{entity_name} to #{relationship.reference_to} using #{foreign_key}"
468
490
 
469
491
  end
470
492
  end
@@ -38,7 +38,7 @@ module ActiveRecord
38
38
  class SalesforceColumn < Column
39
39
  include StringHelper
40
40
 
41
- attr_reader :api_name, :custom, :label, :readonly, :reference_to
41
+ attr_reader :api_name, :custom, :label, :createable, :updateable, :reference_to
42
42
 
43
43
  def initialize(field)
44
44
  @api_name = field[:name]
@@ -51,7 +51,8 @@ module ActiveRecord
51
51
  @text = [:string, :text].include? @type
52
52
  @number = [:float, :integer].include? @type
53
53
 
54
- @readonly = (field[:updateable] != "true" or field[:createable] != "true")
54
+ @createable = field[:createable] == "true"
55
+ @updateable = field[:updateable] == "true"
55
56
 
56
57
  if field[:type] =~ /reference/i
57
58
  @reference_to = field[:referenceTo]
@@ -0,0 +1,76 @@
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 'yaml'
25
+ require File.dirname(__FILE__) + '/rforce'
26
+
27
+
28
+ class MockBinding < RForce::Binding
29
+ attr_reader :recorded_responses
30
+
31
+ #Connect to the server securely.
32
+ def initialize(url, sid, recording)
33
+ @recording = recording
34
+ @recorded_responses = {}
35
+
36
+ super(url, sid) if @recording
37
+ end
38
+
39
+
40
+ def save(f)
41
+ YAML.dump(@recorded_responses, f)
42
+ end
43
+
44
+
45
+ def load(f)
46
+ @recorded_responses = YAML.load(f)
47
+ end
48
+
49
+
50
+ #Call a method on the remote server. Arguments can be
51
+ #a hash or (if order is important) an array of alternating
52
+ #keys and values.
53
+ def call_remote(method, args)
54
+ # Star-out any passwords
55
+ safe_args = args.inject([]) {|memo, v| memo << (memo.last == :password ? "*" * v.length : v) }
56
+ key = "#{method}(#{safe_args.join(':')})"
57
+
58
+ if @recording
59
+ response = super(method, args)
60
+ @recorded_responses[key] = response
61
+ else
62
+ response = @recorded_responses[key]
63
+
64
+ unless response
65
+ @recorded_responses.each do |request, reponse|
66
+ #pp request
67
+ end
68
+
69
+ raise "Unable to find matching response for recorded request '#{key}'"
70
+ end
71
+ end
72
+
73
+ response
74
+ end
75
+
76
+ end
@@ -45,12 +45,28 @@ module ActiveRecord
45
45
  if source[:childSObject]
46
46
  relationship = source
47
47
 
48
- @api_name = relationship[:relationshipName] ? relationship[:relationshipName] : relationship[:field].chop.chop
49
- @one_to_many = relationship[:relationshipName] != nil
48
+ if relationship[:relationshipName]
49
+ @api_name = relationship[:relationshipName]
50
+ @one_to_many = true
51
+ @label = @api_name
52
+ @name = column_nameize(@api_name)
53
+ @custom = false
54
+ else
55
+ @api_name = relationship[:field]
56
+ @one_to_many = relationship[:cascadeDelete] == "true"
57
+ @label = relationship[:childSObject].pluralize
58
+ @custom = relationship[:childSObject].match(/__c$/)
59
+
60
+ name = relationship[:childSObject]
61
+ name.chop!.chop!.chop! if custom
62
+
63
+ @name = column_nameize(name.pluralize)
64
+ end
65
+
50
66
  @reference_to = relationship[:childSObject]
51
- @label = @name
67
+
52
68
  @foreign_key = column_nameize(relationship[:field])
53
- @custom = false
69
+ @foreign_key.chop!.chop! << "id__c" if @foreign_key.match(/__c$/)
54
70
  else
55
71
  field = source
56
72
 
@@ -60,15 +76,12 @@ module ActiveRecord
60
76
  @api_name = @api_name.chop.chop unless @custom
61
77
 
62
78
  @label = field[:label]
63
- @readonly = (field[:updateable] != "true" or field[:createable] != "true")
64
79
  @reference_to = field[:referenceTo]
65
80
  @one_to_many = false
66
81
 
67
82
  @foreign_key = column.name
83
+ @name = column_nameize(@api_name)
68
84
  end
69
-
70
- @name = column_nameize(@api_name)
71
-
72
85
  end
73
86
  end
74
87
 
data/lib/rforce.rb CHANGED
@@ -150,7 +150,7 @@ module RForce
150
150
 
151
151
 
152
152
  #Connect to the server securely.
153
- def initialize(url, sid = nil)
153
+ def initialize(url, sid)
154
154
  init_server(url)
155
155
 
156
156
  @session_id = sid
@@ -184,10 +184,10 @@ module RForce
184
184
 
185
185
  raise "Incorrect user name / password [#{response.fault}]" unless response.loginResponse
186
186
 
187
- result = response.loginResponse.result
188
- @session_id = result.sessionId
187
+ result = response[:loginResponse][:result]
188
+ @session_id = result[:sessionId]
189
189
 
190
- init_server(result.serverUrl)
190
+ init_server(result[:serverUrl])
191
191
 
192
192
  response
193
193
  end
@@ -0,0 +1,99 @@
1
+ require 'rubygems'
2
+
3
+ #require_gem 'activesalesforce', '>= 0.2.6'
4
+ require 'activesalesforce'
5
+
6
+ require 'recorded_test_case'
7
+ require 'pp'
8
+
9
+
10
+ class Contact < ActiveRecord::Base
11
+ end
12
+
13
+
14
+
15
+ module Asf
16
+ module UnitTests
17
+
18
+ class BasicTest < Test::Unit::TestCase
19
+ include RecordedTestCase
20
+
21
+ attr_reader :contact
22
+
23
+ def initialize(test_method_name)
24
+ super(test_method_name)
25
+
26
+ #force_recording :test_get_created_by_from_contact
27
+ end
28
+
29
+ def setup
30
+ puts "\nStarting test '#{self.class.name.gsub('::', '')}.#{method_name}'"
31
+
32
+ super
33
+
34
+ @contact = Contact.new
35
+ contact.first_name = 'DutchTestFirstName'
36
+ contact.last_name = 'DutchTestLastName'
37
+ contact.home_phone = '555-555-1212'
38
+ contact.save
39
+
40
+ contact.reload
41
+ end
42
+
43
+ def teardown
44
+ contact.destroy if contact
45
+
46
+ super
47
+ end
48
+
49
+ def test_count_contacts
50
+ assert_equal 27, Contact.count
51
+ end
52
+
53
+ def test_create_a_contact
54
+ contact.id
55
+ end
56
+
57
+ def test_save_a_contact
58
+ contact.id
59
+ end
60
+
61
+ def test_find_a_contact
62
+ c = Contact.find(contact.id)
63
+ assert_equal contact.id, c.id
64
+ end
65
+
66
+ def test_find_a_contact_by_id
67
+ c = Contact.find_by_id(contact.id)
68
+ assert_equal contact.id, c.id
69
+ end
70
+
71
+ def test_find_a_contact_by_first_name
72
+ c = Contact.find_by_first_name('DutchTestFirstName')
73
+ assert_equal contact.id, c.id
74
+ end
75
+
76
+ def test_read_all_content_columns
77
+ Contact.content_columns.each { |column| contact.send(column.name) }
78
+ end
79
+
80
+ def test_get_created_by_from_contact
81
+ user = contact.created_by
82
+ assert_equal contact.created_by_id, user.id
83
+ end
84
+
85
+ def test_add_notes_to_contact
86
+ n1 = Note.new(:title => "My Title", :body => "My Body")
87
+ n2 = Note.new(:title => "My Title 2", :body => "My Body 2")
88
+
89
+ contact.notes << n1
90
+ contact.notes << n2
91
+
92
+ n1.save
93
+ n2.save
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+ end