activesalesforce 0.0.3 → 0.0.4

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 CHANGED
@@ -6,8 +6,24 @@
6
6
 
7
7
  == Getting started
8
8
 
9
- 1. TBD Add in info on editing scripts/generate and scripts/server + database.yml
10
-
9
+ 1. gem install activesalesforce
10
+ 2. if you have not already done so generate your initial rails app using "rails <myappname goes here>"
11
+ 3. edit config/environment.rb and add "require_gem 'activesalesforce'" to the end of the "Rails::Initializer.run do |config|" block, e.g.
12
+
13
+ Rails::Initializer.run do |config|
14
+ ...
15
+
16
+ require_gem 'activesalesforce'
17
+ end
18
+
19
+ 4. edit database.yml
20
+
21
+ adapter: salesforce
22
+ url: <salesforce api server url goes here>
23
+ username: <salesforce user name goes here>
24
+ password: <salesforce password goes here>
25
+
26
+ 5. proceed using standard Rails development techniques!
11
27
 
12
28
  == Description of contents
13
29
 
@@ -0,0 +1,24 @@
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 'salesforce_connection_adapter'
@@ -32,38 +32,37 @@ module ActiveRecord
32
32
  # This behavior can be turned off by setting <tt>ActiveRecord::Base.record_timestamps = false</tt>.
33
33
  # This behavior can use GMT by setting <tt>ActiveRecord::Base.timestamps_gmt = true</tt>
34
34
  module SalesforceRecord
35
- include SOAP, XSD
36
-
37
- NS1 = 'urn:partner.soap.sforce.com'
38
- NS2 = "urn:sobject.partner.soap.sforce.com"
39
35
 
40
36
  def self.append_features(base) # :nodoc:
41
37
  super
42
-
38
+
43
39
  base.class_eval do
44
40
  alias_method :create, :create_with_sforce_api
45
41
  alias_method :update, :update_with_sforce_api
46
42
  end
47
43
  end
48
-
44
+
49
45
  def create_with_sforce_api
50
46
  return if not @attributes.changed?
51
47
  puts "create_with_sforce_api creating #{self.class}"
52
- connection.create(:sObjects => create_sobject())
48
+ id = connection.create(:sObjects => create_sobject())
49
+ self.Id = id
50
+ @attributes.clear_changed!
53
51
  end
54
-
52
+
55
53
  def update_with_sforce_api
56
54
  return if not @attributes.changed?
57
55
  puts "update_with_sforce_api updating #{self.class}('#{self.Id}')"
58
56
  connection.update(:sObjects => create_sobject())
57
+ @attributes.clear_changed!
59
58
  end
60
59
 
61
60
  def create_sobject()
62
61
  fields = @attributes.changed_fields
63
-
62
+
64
63
  sobj = [ 'type { :xmlns => "urn:sobject.partner.soap.sforce.com" }', self.class.name ]
65
64
  sobj << 'Id { :xmlns => "urn:sobject.partner.soap.sforce.com" }' << self.Id if self.Id
66
-
65
+
67
66
  # now add any changed fields
68
67
  fieldValues = {}
69
68
  fields.each do |fieldName|
@@ -73,7 +72,7 @@ module ActiveRecord
73
72
 
74
73
  sobj
75
74
  end
76
-
75
+
77
76
  end
78
77
 
79
78
  class Base
@@ -83,16 +82,29 @@ module ActiveRecord
83
82
  default_timezone = :utc
84
83
 
85
84
  def after_initialize()
85
+ sfdcObjectName = self.class.table_name
86
86
  if not @attributes.is_a?(Salesforce::SObjectAttributes)
87
- # Insure that SObjectAttributes is always used for our atttributes
87
+ # Insure that SObjectAttributes is always used for our attributes
88
88
  originalAttributes = @attributes
89
89
 
90
- @attributes = Salesforce::SObjectAttributes.new(connection.columns_map(self.class.table_name))
90
+ @attributes = Salesforce::SObjectAttributes.new(connection.columns_map(sfdcObjectName))
91
91
 
92
92
  originalAttributes.each { |name, value| self[name] = value }
93
93
  end
94
+
95
+ # Create relationships for any reference field
96
+ connection.columns(sfdcObjectName).each do |column|
97
+ if column.reference_to
98
+ referenceName = column.name.chop.chop
99
+
100
+ unless self.respond_to? referenceName.to_sym or column.reference_to == "Profile"
101
+ puts "Creating relationship from #{sfdcObjectName} to #{column.reference_to} for #{referenceName}"
102
+ self.class.belongs_to referenceName.to_sym, :class_name => column.reference_to, :foreign_key => column.name, :dependent => false
103
+ end
104
+ end
105
+ end
94
106
  end
95
-
107
+
96
108
  def self.table_name
97
109
  class_name_of_active_record_descendant(self)
98
110
  end
@@ -100,7 +112,7 @@ module ActiveRecord
100
112
  def self.primary_key
101
113
  "Id"
102
114
  end
103
-
115
+
104
116
  def self.construct_finder_sql(options)
105
117
  soql = "SELECT #{column_names.join(', ')} FROM #{table_name} "
106
118
  add_conditions!(soql, options[:conditions])
@@ -114,15 +126,19 @@ module ActiveRecord
114
126
  end
115
127
 
116
128
  def self.count(conditions = nil, joins = nil)
117
- soql = "SELECT Id FROM #{table_name} "
118
- add_conditions!(soql, conditions)
119
-
120
- count_by_sql(soql)
129
+ soql = "SELECT Id FROM #{table_name} "
130
+ add_conditions!(soql, conditions)
131
+
132
+ count_by_sql(soql)
121
133
  end
122
134
 
123
135
  def self.count_by_sql(soql)
124
136
  connection.select_all(soql, "#{name} Count").length
125
- end
126
-
137
+ end
138
+
139
+ def self.delete(ids)
140
+ connection.delete(ids)
141
+ end
142
+
127
143
  end
128
144
  end
@@ -32,63 +32,63 @@ require File.dirname(__FILE__) + '/salesforce_active_record'
32
32
  require File.dirname(__FILE__) + '/column_definition'
33
33
 
34
34
  ActiveRecord::Base.class_eval do
35
- include ActiveRecord::SalesforceRecord
35
+ include ActiveRecord::SalesforceRecord
36
36
  end
37
37
 
38
38
 
39
39
  module ActiveRecord
40
40
  class Base
41
41
  @@cache = {}
42
-
42
+
43
43
  # Establishes a connection to the database that's used by all Active Record objects.
44
44
  def self.salesforce_connection(config) # :nodoc:
45
45
  puts "Using Salesforce connection!"
46
-
46
+
47
47
  config = config.symbolize_keys
48
-
48
+
49
49
  url = config[:url].to_s
50
50
  username = config[:username].to_s
51
51
  password = config[:password].to_s
52
52
 
53
53
  connection = @@cache["#{url}.#{username}.#{password}"]
54
-
54
+
55
55
  if not connection
56
56
  connection = SalesforceLogin.new(url, username, password).proxy
57
57
  @@cache["#{url}.#{username}.#{password}"] = connection
58
58
  puts "Created new connection for [#{url}, #{username}]"
59
59
  end
60
-
61
- #puts "connected to Salesforce as #{connection.getUserInfo(nil).result['userFullName']}"
62
60
 
63
61
  ConnectionAdapters::SalesforceAdapter.new(connection, logger, [url, username, password], config)
64
62
  end
65
63
  end
66
64
 
67
-
65
+
68
66
  module ConnectionAdapters
69
67
  class SalesforceError < StandardError
70
68
  end
71
69
 
72
70
  class SalesforceAdapter < AbstractAdapter
73
-
71
+
74
72
  def initialize(connection, logger, connection_options, config)
75
73
  super(connection, logger)
76
74
 
77
75
  @connection_options, @config = connection_options, config
76
+
77
+ @columns_map = {}
78
78
  @columns_name_map = {}
79
79
  end
80
-
80
+
81
81
  def adapter_name #:nodoc:
82
82
  'Salesforce'
83
83
  end
84
-
84
+
85
85
  def supports_migrations? #:nodoc:
86
86
  false
87
87
  end
88
-
89
-
88
+
89
+
90
90
  # QUOTING ==================================================
91
-
91
+
92
92
  def quote(value, column = nil)
93
93
  if value.kind_of?(String) && column && column.type == :binary
94
94
  s = column.class.string_to_binary(value).unpack("H*")[0]
@@ -97,15 +97,15 @@ module ActiveRecord
97
97
  super
98
98
  end
99
99
  end
100
-
100
+
101
101
  def quote_column_name(name) #:nodoc:
102
102
  "`#{name}`"
103
103
  end
104
-
104
+
105
105
  def quote_string(string) #:nodoc:
106
106
  string
107
107
  end
108
-
108
+
109
109
  def quoted_true
110
110
  "TRUE"
111
111
  end
@@ -113,28 +113,28 @@ module ActiveRecord
113
113
  def quoted_false
114
114
  "FALSE"
115
115
  end
116
-
117
-
116
+
117
+
118
118
  # CONNECTION MANAGEMENT ====================================
119
-
119
+
120
120
  def active?
121
121
  true
122
122
  end
123
-
123
+
124
124
  def reconnect!
125
125
  connect
126
126
  end
127
-
128
-
127
+
128
+
129
129
  # DATABASE STATEMENTS ======================================
130
-
130
+
131
131
  def select_all(soql, name = nil) #:nodoc:
132
132
  log(soql, name)
133
133
 
134
134
  records = @connection.query(:queryString => soql).queryResponse.result.records
135
135
 
136
136
  records = [ records ] unless records.is_a?(Array)
137
-
137
+
138
138
  result = []
139
139
  records.each do |record|
140
140
  attributes = Salesforce::SObjectAttributes.new(columns_map(record[:type]), record)
@@ -143,46 +143,59 @@ module ActiveRecord
143
143
 
144
144
  result
145
145
  end
146
-
146
+
147
147
  def select_one(sql, name = nil) #:nodoc:
148
148
  result = select_all(sql, name)
149
149
  result.nil? ? nil : result.first
150
150
  end
151
-
151
+
152
152
  def create(sobject, name = nil) #:nodoc:
153
153
  result = @connection.create(sobject).createResponse.result
154
154
 
155
- raise SalesforceError, result.errors.message unless result.success == "true"
155
+ raise SalesforceError, result[:errors].message unless result[:success] == "true"
156
156
 
157
- # @connection.affected_rows
157
+ result[:id]
158
158
  end
159
159
 
160
160
  def update(sobject, name = nil) #:nodoc:
161
161
  result = @connection.update(sobject).updateResponse.result
162
-
163
- raise SalesforceError, result.errors.message unless result.success == "true"
162
+
163
+ raise SalesforceError, result[:errors].message unless result[:success] == "true"
164
164
 
165
165
  # @connection.affected_rows
166
166
  end
167
-
168
- alias_method :delete, :update
169
167
 
168
+ def delete(ids)
169
+ puts "Delete #{ids}"
170
+
171
+
172
+ result = @connection.delete(:ids => ids).deleteResponse.result
173
+
174
+ raise SalesforceError, result[:errors].message unless result[:success] == "true"
175
+ end
176
+
170
177
  def columns(table_name, name = nil)
171
- columns = []
178
+ cached_columns = @columns_map[table_name]
179
+ return cached_columns if cached_columns
180
+
181
+ puts "describeSObject(#{table_name})"
182
+
183
+ cached_columns = []
184
+ @columns_map[table_name] = cached_columns
172
185
 
173
186
  metadata = @connection.describeSObject(:sObjectType => table_name).describeSObjectResponse.result
174
187
 
175
188
  metadata.fields.each do |field|
176
- columns << SalesforceColumn.new(field)
189
+ cached_columns << SalesforceColumn.new(field)
177
190
  end
178
191
 
179
- columns
192
+ cached_columns
180
193
  end
181
-
194
+
182
195
  def columns_map(table_name, name = nil)
183
196
  columns_map = @columns_name_map[table_name]
184
197
  return columns_map if columns_map
185
-
198
+
186
199
  columns_map = {}
187
200
  @columns_name_map[table_name] = columns_map
188
201
 
@@ -190,23 +203,23 @@ module ActiveRecord
190
203
 
191
204
  columns_map
192
205
  end
193
-
206
+
194
207
  private
195
-
196
- def select(sql, name = nil)
197
- puts "select(#{sql}, (#{name}))"
198
- @connection.query_with_result = true
199
- result = execute(sql, name)
200
- rows = []
201
- if @null_values_in_each_hash
202
- result.each_hash { |row| rows << row }
203
- else
204
- all_fields = result.fetch_fields.inject({}) { |fields, f| fields[f.name] = nil; fields }
205
- result.each_hash { |row| rows << all_fields.dup.update(row) }
206
- end
207
- result.free
208
- rows
208
+
209
+ def select(sql, name = nil)
210
+ puts "select(#{sql}, (#{name}))"
211
+ @connection.query_with_result = true
212
+ result = execute(sql, name)
213
+ rows = []
214
+ if @null_values_in_each_hash
215
+ result.each_hash { |row| rows << row }
216
+ else
217
+ all_fields = result.fetch_fields.inject({}) { |fields, f| fields[f.name] = nil; fields }
218
+ result.each_hash { |row| rows << all_fields.dup.update(row) }
209
219
  end
220
+ result.free
221
+ rows
222
+ end
210
223
  end
211
224
  end
212
225
  end
@@ -1,17 +1,14 @@
1
1
  require File.join(File.dirname(__FILE__), '../../config/boot')
2
-
3
- require_gem 'activesalesforce'
4
- require 'salesforce_connection_adapter'
5
-
6
2
  require File.dirname(__FILE__) + '/../test_helper'
7
3
 
8
-
4
+ require 'pp'
9
5
 
10
6
  class AccountTest < Test::Unit::TestCase
11
7
  def setup
12
8
  ActiveRecord::Base.allow_concurrency = true
13
9
  end
14
10
 
11
+
15
12
  def test_get_account
16
13
  accounts = Account.find(:all)
17
14
 
@@ -33,12 +30,37 @@ class AccountTest < Test::Unit::TestCase
33
30
 
34
31
  acme.save
35
32
  end
33
+
36
34
 
37
35
  def test_create_account
38
36
  dutchCo = Account.new
39
37
  dutchCo.Name = "DutchCo"
40
38
  dutchCo.Website = "www.dutchco.com"
41
39
  dutchCo.save
40
+
41
+ dutchCo2 = Account.new(:Name => "DutchCo2", :Website => "www.dutchco2.com")
42
+ dutchCo2.save
43
+
44
+ dutchCo3 = Account.create(:Name => "DutchCo3", :Website => "www.dutchco3.com")
45
+
46
+ accounts = Account.create([
47
+ { :Name => "DutchCo4", :Website => "www.dutchco4.com" },
48
+ { :Name => "DutchCo5", :Website => "www.dutchco5.com" }])
42
49
  end
43
-
50
+
51
+
52
+ def test_destroy_account
53
+ account = Account.create(:Name => "DutchADelete", :Website => "www.dutchcodelete.com")
54
+ account = Account.find_by_Id(account.Id)
55
+
56
+ pp account.Parent
57
+
58
+ puts "Getting CreatedBy"
59
+ createdBy = account.CreatedBy
60
+ createdBy = User.find_by_Id(account.CreatedById);
61
+ puts createdBy.Email
62
+
63
+ Account.delete(account.Id)
64
+ end
65
+
44
66
  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.0.3
7
- date: 2006-01-20 00:00:00 -05:00
6
+ version: 0.0.4
7
+ date: 2006-01-22 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
@@ -12,7 +12,7 @@ email: dchasman@salesforce.com
12
12
  homepage: http://rubyforge.org/projects/activesfdc/
13
13
  rubyforge_project:
14
14
  description:
15
- autorequire: salesforce_connection_adapter
15
+ autorequire: active_salesforce
16
16
  default_executable:
17
17
  bindir: bin
18
18
  has_rdoc: true
@@ -31,6 +31,7 @@ files:
31
31
  - lib/salesforce_login.rb
32
32
  - lib/salesforce_active_record.rb
33
33
  - lib/column_definition.rb
34
+ - lib/activesalesforce.rb
34
35
  - lib/rforce.rb
35
36
  - lib/sobject_attributes.rb
36
37
  - lib/salesforce_connection_adapter.rb