activesalesforce 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rforce.rb CHANGED
@@ -54,7 +54,7 @@ require_gem 'builder'
54
54
  # binding.create 'sObject {"xsi:type" => "Opportunity"}' => opportunity
55
55
  #
56
56
  module RForce
57
-
57
+
58
58
  #Allows indexing hashes like method calls: hash.key
59
59
  #to supplement the traditional way of indexing: hash[key]
60
60
  module FlashHash
@@ -62,65 +62,68 @@ module RForce
62
62
  self[method]
63
63
  end
64
64
  end
65
-
65
+
66
66
  #Turns an XML response from the server into a Ruby
67
67
  #object whose methods correspond to nested XML elements.
68
68
  class SoapResponse
69
69
  include FlashHash
70
-
70
+
71
71
  #Parses an XML string into structured data.
72
72
  def initialize(content)
73
73
  document = REXML::Document.new content
74
74
  node = REXML::XPath.first document, '//soapenv:Body'
75
75
  @parsed = SoapResponse.parse node
76
76
  end
77
-
77
+
78
78
  #Allows this object to act like a hash (and therefore
79
79
  #as a FlashHash via the include above).
80
80
  def [](symbol)
81
81
  @parsed[symbol]
82
82
  end
83
-
83
+
84
84
  #Digests an XML DOM node into nested Ruby types.
85
85
  def SoapResponse.parse(node)
86
86
  #Convert text nodes into simple strings.
87
87
  return node.text unless node.has_elements?
88
-
88
+
89
89
  #Convert nodes with children into FlashHashes.
90
90
  elements = {}
91
91
  class << elements
92
92
  include FlashHash
93
93
  end
94
-
94
+
95
95
  #Add all the element's children to the hash.
96
96
  node.each_element do |e|
97
97
  name = e.name.to_sym
98
98
 
99
99
  case elements[name]
100
100
  #The most common case: unique child element tags.
101
- when NilClass: elements[name] = parse(e)
101
+ when NilClass: elements[name] = parse(e)
102
102
 
103
103
  #Non-unique child elements become arrays:
104
104
 
105
105
  #We've already created the array: just
106
106
  #add the element.
107
- when Array: elements[name] << parse(e)
107
+ when Array: elements[name] << parse(e)
108
108
 
109
109
  #We haven't created the array yet: do so,
110
110
  #then put the existing element in, followed
111
111
  #by the new one.
112
- else
113
- elements[name] = [elements[name]]
114
- elements[name] << parse(e)
112
+ else
113
+ elements[name] = [elements[name]]
114
+ elements[name] << parse(e)
115
115
  end
116
116
  end
117
-
117
+
118
118
  return elements
119
119
  end
120
120
  end
121
-
121
+
122
122
  #Implements the connection to the SalesForce server.
123
123
  class Binding
124
+ DEFAULT_BATCH_SIZE = 10
125
+ attr_accessor :batch_size
126
+
124
127
  #Fill in the guts of this typical SOAP envelope
125
128
  #with the session ID and the body of the SOAP request.
126
129
  Envelope = <<-HERE
@@ -132,13 +135,16 @@ module RForce
132
135
  <SessionHeader>
133
136
  <sessionId>%s</sessionId>
134
137
  </SessionHeader>
138
+ <QueryOptions>
139
+ <batchSize>%d</batchSize>
140
+ </QueryOptions>
135
141
  </env:Header>
136
142
  <env:Body>
137
143
  %s
138
144
  </env:Body>
139
145
  </env:Envelope>
140
146
  HERE
141
-
147
+
142
148
  #Connect to the server securely.
143
149
  def initialize(url)
144
150
  @url = URI.parse(url)
@@ -146,21 +152,24 @@ module RForce
146
152
  @server.use_ssl = @url.scheme == 'https'
147
153
 
148
154
  # run ruby with -d to see SOAP wiredumps.
149
- @server.set_debug_output $stderr if $DEBUG
155
+ @server.set_debug_output $stderr #if $DEBUG
150
156
 
151
157
  @session_id = ''
158
+ @batch_size = DEFAULT_BATCH_SIZE
152
159
  end
153
-
160
+
161
+
154
162
  #Log in to the server and remember the session ID
155
163
  #returned to us by SalesForce.
156
164
  def login(user, pass)
157
165
  response = call_remote(:login, [:username, user, :password, pass])
158
-
166
+
159
167
  raise "Incorrect user name / password [#{response.fault}]" unless response.loginResponse
160
168
  @session_id = response.loginResponse.result.sessionId
169
+
161
170
  response
162
171
  end
163
-
172
+
164
173
  #Call a method on the remote server. Arguments can be
165
174
  #a hash or (if order is important) an array of alternating
166
175
  #keys and values.
@@ -169,56 +178,59 @@ module RForce
169
178
  expanded = ''
170
179
  @builder = Builder::XmlMarkup.new(:target => expanded)
171
180
  expand({method => args}, 'urn:partner.soap.sforce.com')
172
-
181
+
173
182
  #Fill in the blanks of the SOAP envelope with our
174
183
  #session ID and the expanded XML of our request.
175
- request = (Envelope % [@session_id, expanded])
176
-
184
+ request = (Envelope % [@session_id, @batch_size, expanded])
185
+
186
+ # reset the batch size for the next request
187
+ @batch_size = DEFAULT_BATCH_SIZE
188
+
177
189
  #Send the request to the server and read the response.
178
190
  response = @server.post2(@url.path, request, {'SOAPAction' => method.to_s, 'content-type' => 'text/xml'})
179
191
  SoapResponse.new(response.body)
180
192
  end
181
-
193
+
182
194
  #Turns method calls on this object into remote SOAP calls.
183
195
  def method_missing(method, *args)
184
196
  unless args.size == 1 && [Hash, Array].include?(args[0].class)
185
197
  raise 'Expected 1 Hash or Array argument'
186
198
  end
187
-
199
+
188
200
  call_remote method, args[0]
189
201
  end
190
-
202
+
191
203
  #Expand Ruby data structures into XML.
192
204
  def expand(args, xmlns = nil)
193
205
  #Nest arrays: [:a, 1, :b, 2] => [[:a, 1], [:b, 2]]
194
206
  if (args.class == Array)
195
207
  args.each_index{|i| args[i, 2] = [args[i, 2]]}
196
208
  end
197
-
209
+
198
210
  args.each do |key, value|
199
211
  attributes = xmlns ? {:xmlns => xmlns} : {}
200
-
201
- #If the XML tag requires attributes,
202
- #the tag name will contain a space
203
- #followed by a string representation
204
- #of a hash of attributes.
205
- #
206
- #e.g. 'sObject {"xsi:type" => "Opportunity"}'
207
- #becomes <sObject xsi:type="Opportunity>...</sObject>
212
+
213
+ #If the XML tag requires attributes,
214
+ #the tag name will contain a space
215
+ #followed by a string representation
216
+ #of a hash of attributes.
217
+ #
218
+ #e.g. 'sObject {"xsi:type" => "Opportunity"}'
219
+ #becomes <sObject xsi:type="Opportunity>...</sObject>
208
220
  if key.is_a? String
209
- key, modifier = key.split(' ', 2)
210
-
211
- attributes.merge!(eval(modifier)) if modifier
221
+ key, modifier = key.split(' ', 2)
222
+
223
+ attributes.merge!(eval(modifier)) if modifier
212
224
  end
213
225
 
214
226
  #Create an XML element and fill it with this
215
227
  #value's sub-items.
216
228
  case value
217
- when Hash, Array
218
- @builder.tag!(key, attributes) do expand value; end
229
+ when Hash, Array
230
+ @builder.tag!(key, attributes) do expand value; end
219
231
 
220
- when String
221
- @builder.tag!(key, attributes) { @builder.text! value }
232
+ when String
233
+ @builder.tag!(key, attributes) { @builder.text! value }
222
234
  end
223
235
  end
224
236
  end
@@ -133,6 +133,7 @@ module ActiveRecord
133
133
  end
134
134
 
135
135
  def self.count_by_sql(soql)
136
+ connection.batch_size = 1
136
137
  connection.select_all(soql, "#{name} Count").length
137
138
  end
138
139
 
@@ -68,6 +68,7 @@ module ActiveRecord
68
68
  end
69
69
 
70
70
  class SalesforceAdapter < AbstractAdapter
71
+ attr_accessor :batch_size
71
72
 
72
73
  def initialize(connection, logger, connection_options, config)
73
74
  super(connection, logger)
@@ -131,7 +132,10 @@ module ActiveRecord
131
132
  def select_all(soql, name = nil) #:nodoc:
132
133
  log(soql, name)
133
134
 
134
- records = @connection.query(:queryString => soql).queryResponse.result.records
135
+ @connection.batch_size = @batch_size if @batch_size
136
+ @batch_size = nil
137
+
138
+ records = get_result(@connection.query(:queryString => soql), :query).records
135
139
 
136
140
  records = [ records ] unless records.is_a?(Array)
137
141
 
@@ -150,30 +154,31 @@ module ActiveRecord
150
154
  end
151
155
 
152
156
  def create(sobject, name = nil) #:nodoc:
153
- result = @connection.create(sobject).createResponse.result
154
-
155
- raise SalesforceError, result[:errors].message unless result[:success] == "true"
156
-
157
- result[:id]
157
+ check_result(get_result(@connection.create(sobject), :create))[:id]
158
158
  end
159
159
 
160
160
  def update(sobject, name = nil) #:nodoc:
161
- result = @connection.update(sobject).updateResponse.result
162
-
163
- raise SalesforceError, result[:errors].message unless result[:success] == "true"
164
-
165
- # @connection.affected_rows
161
+ check_result(get_result(@connection.update(sobject), :update))
166
162
  end
167
163
 
168
164
  def delete(ids)
169
165
  puts "Delete #{ids}"
166
+ check_result(get_result(@connection.delete(:ids => ids), :delete))
167
+ end
170
168
 
169
+ def get_result(response, method)
170
+ responseName = (method.to_s + "Response").to_sym
171
+ finalResponse = response[responseName]
172
+ raise SalesforceError, response.fault unless finalResponse
171
173
 
172
- result = @connection.delete(:ids => ids).deleteResponse.result
173
-
174
+ result = finalResponse[:result]
175
+ end
176
+
177
+ def check_result(result)
174
178
  raise SalesforceError, result[:errors].message unless result[:success] == "true"
179
+ result
175
180
  end
176
-
181
+
177
182
  def columns(table_name, name = nil)
178
183
  cached_columns = @columns_map[table_name]
179
184
  return cached_columns if cached_columns
@@ -182,8 +187,8 @@ module ActiveRecord
182
187
 
183
188
  cached_columns = []
184
189
  @columns_map[table_name] = cached_columns
185
-
186
- metadata = @connection.describeSObject(:sObjectType => table_name).describeSObjectResponse.result
190
+
191
+ metadata = get_result(@connection.describeSObject(:sObjectType => table_name), :describeSObject)
187
192
 
188
193
  metadata.fields.each do |field|
189
194
  cached_columns << SalesforceColumn.new(field)
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.4
7
- date: 2006-01-22 00:00:00 -05:00
6
+ version: 0.0.5
7
+ date: 2006-01-23 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