activesalesforce 0.4.8 → 0.4.9
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/asf_adapter.rb +5 -7
- data/lib/rforce.rb +64 -64
- data/test/unit/recorded_test_case.rb +1 -1
- metadata +3 -3
data/lib/asf_adapter.rb
CHANGED
@@ -352,9 +352,8 @@ module ActiveRecord
|
|
352
352
|
|
353
353
|
# Extract arrays of values
|
354
354
|
values = sql.match(/VALUES\s*\((.+)\)/i)[1]
|
355
|
-
values = values.scan(/(
|
356
|
-
|
357
|
-
values.map! { |v| v[7] }
|
355
|
+
values = values.scan(/(NULL|TRUE|FALSE|'(?:(?:[^']|'')*)'),*/mi).flatten
|
356
|
+
values.map! { |v| v.first == "'" ? v.slice(1, v.length - 2) : v == "NULL" ? nil : v }
|
358
357
|
|
359
358
|
fields = get_fields(columns, names, values, :createable)
|
360
359
|
|
@@ -376,11 +375,10 @@ module ActiveRecord
|
|
376
375
|
columns = entity_def.column_name_to_column
|
377
376
|
|
378
377
|
match = sql.match(/SET\s+(.+)\s+WHERE/mi)[1]
|
379
|
-
names = match.scan(/(\w+)\s*=\s*('|NULL|TRUE|FALSE)/i)
|
380
|
-
names.map! { |v| v[0] }
|
378
|
+
names = match.scan(/(\w+)\s*=\s*(?:'|NULL|TRUE|FALSE)/i).flatten
|
381
379
|
|
382
|
-
values = match.scan(/=\s*(
|
383
|
-
values.map! { |v| v
|
380
|
+
values = match.scan(/=\s*(NULL|TRUE|FALSE|'(?:(?:[^']|'')*)'),*/mi).flatten
|
381
|
+
values.map! { |v| v.first == "'" ? v.slice(1, v.length - 2) : v == "NULL" ? nil : v }
|
384
382
|
|
385
383
|
fields = get_fields(columns, names, values, :updateable)
|
386
384
|
|
data/lib/rforce.rb
CHANGED
@@ -59,7 +59,7 @@ require_gem 'builder'
|
|
59
59
|
|
60
60
|
|
61
61
|
module RForce
|
62
|
-
|
62
|
+
|
63
63
|
#Allows indexing hashes like method calls: hash.key
|
64
64
|
#to supplement the traditional way of indexing: hash[key]
|
65
65
|
module FlashHash
|
@@ -67,50 +67,50 @@ module RForce
|
|
67
67
|
self[method]
|
68
68
|
end
|
69
69
|
end
|
70
|
-
|
70
|
+
|
71
71
|
#Turns an XML response from the server into a Ruby
|
72
72
|
#object whose methods correspond to nested XML elements.
|
73
73
|
class SoapResponse
|
74
74
|
include FlashHash
|
75
|
-
|
75
|
+
|
76
76
|
#Parses an XML string into structured data.
|
77
77
|
def initialize(content)
|
78
78
|
document = REXML::Document.new content
|
79
79
|
node = REXML::XPath.first document, '//soapenv:Body'
|
80
80
|
@parsed = SoapResponse.parse node
|
81
81
|
end
|
82
|
-
|
82
|
+
|
83
83
|
#Allows this object to act like a hash (and therefore
|
84
84
|
#as a FlashHash via the include above).
|
85
85
|
def [](symbol)
|
86
86
|
@parsed[symbol]
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
#Digests an XML DOM node into nested Ruby types.
|
90
90
|
def SoapResponse.parse(node)
|
91
91
|
#Convert text nodes into simple strings.
|
92
92
|
return node.text unless node.has_elements?
|
93
|
-
|
93
|
+
|
94
94
|
#Convert nodes with children into FlashHashes.
|
95
95
|
elements = {}
|
96
96
|
class << elements
|
97
97
|
include FlashHash
|
98
98
|
end
|
99
|
-
|
99
|
+
|
100
100
|
#Add all the element's children to the hash.
|
101
101
|
node.each_element do |e|
|
102
102
|
name = e.name.to_sym
|
103
|
-
|
103
|
+
|
104
104
|
case elements[name]
|
105
105
|
#The most common case: unique child element tags.
|
106
106
|
when NilClass: elements[name] = parse(e)
|
107
|
-
|
107
|
+
|
108
108
|
#Non-unique child elements become arrays:
|
109
|
-
|
109
|
+
|
110
110
|
#We've already created the array: just
|
111
111
|
#add the element.
|
112
112
|
when Array: elements[name] << parse(e)
|
113
|
-
|
113
|
+
|
114
114
|
#We haven't created the array yet: do so,
|
115
115
|
#then put the existing element in, followed
|
116
116
|
#by the new one.
|
@@ -119,17 +119,17 @@ module RForce
|
|
119
119
|
elements[name] << parse(e)
|
120
120
|
end
|
121
121
|
end
|
122
|
-
|
122
|
+
|
123
123
|
return elements
|
124
124
|
end
|
125
125
|
end
|
126
|
-
|
127
|
-
|
126
|
+
|
127
|
+
|
128
128
|
#Implements the connection to the SalesForce server.
|
129
129
|
class Binding
|
130
130
|
DEFAULT_BATCH_SIZE = 10
|
131
131
|
attr_accessor :batch_size, :url, :assignment_rule_id, :use_default_rule, :update_mru
|
132
|
-
|
132
|
+
|
133
133
|
#Fill in the guts of this typical SOAP envelope
|
134
134
|
#with the session ID and the body of the SOAP request.
|
135
135
|
Envelope = <<-HERE
|
@@ -153,58 +153,58 @@ module RForce
|
|
153
153
|
</soap:Body>
|
154
154
|
</soap:Envelope>
|
155
155
|
HERE
|
156
|
-
|
156
|
+
|
157
157
|
AssignmentRuleHeaderUsingRuleId = '<partner:AssignmentRuleHeader soap:mustUnderstand="1"><partner:assignmentRuleId>%s</partner:assignmentRuleId></partner:AssignmentRuleHeader>'
|
158
158
|
AssignmentRuleHeaderUsingDefaultRule = '<partner:AssignmentRuleHeader soap:mustUnderstand="1"><partner:useDefaultRule>true</partner:useDefaultRule></partner:AssignmentRuleHeader>'
|
159
159
|
MruHeader = '<partner:MruHeader soap:mustUnderstand="1"><partner:updateMru>true</partner:updateMru></partner:MruHeader>'
|
160
|
-
|
160
|
+
|
161
161
|
#Connect to the server securely.
|
162
162
|
def initialize(url, sid)
|
163
163
|
init_server(url)
|
164
|
-
|
164
|
+
|
165
165
|
@session_id = sid
|
166
166
|
@batch_size = DEFAULT_BATCH_SIZE
|
167
167
|
end
|
168
|
-
|
169
|
-
|
168
|
+
|
169
|
+
|
170
170
|
def show_debug
|
171
171
|
$DEBUG or ENV['SHOWSOAP']
|
172
172
|
end
|
173
|
-
|
174
|
-
|
173
|
+
|
174
|
+
|
175
175
|
def init_server(url)
|
176
176
|
@url = URI.parse(url)
|
177
177
|
@server = Net::HTTP.new(@url.host, @url.port)
|
178
178
|
@server.use_ssl = @url.scheme == 'https'
|
179
179
|
@server.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
180
|
-
|
180
|
+
|
181
181
|
# run ruby with -d or env variable SHOWSOAP=true to see SOAP wiredumps.
|
182
182
|
@server.set_debug_output $stderr if show_debug
|
183
183
|
end
|
184
|
-
|
185
|
-
|
184
|
+
|
185
|
+
|
186
186
|
#Log in to the server and remember the session ID
|
187
187
|
#returned to us by SalesForce.
|
188
188
|
def login(user, password)
|
189
189
|
@user = user
|
190
190
|
@password = password
|
191
|
-
|
191
|
+
|
192
192
|
response = call_remote(:login, [:username, user, :password, password])
|
193
|
-
|
193
|
+
|
194
194
|
unless response.loginResponse
|
195
195
|
pp response
|
196
|
-
raise "Incorrect user name / password [#{response.fault}]"
|
196
|
+
raise "Incorrect user name / password [#{response.fault}]"
|
197
197
|
end
|
198
|
-
|
198
|
+
|
199
199
|
result = response[:loginResponse][:result]
|
200
200
|
@session_id = result[:sessionId]
|
201
|
-
|
201
|
+
|
202
202
|
init_server(result[:serverUrl])
|
203
|
-
|
203
|
+
|
204
204
|
response
|
205
205
|
end
|
206
|
-
|
207
|
-
|
206
|
+
|
207
|
+
|
208
208
|
#Call a method on the remote server. Arguments can be
|
209
209
|
#a hash or (if order is important) an array of alternating
|
210
210
|
#keys and values.
|
@@ -213,7 +213,7 @@ module RForce
|
|
213
213
|
expanded = ''
|
214
214
|
@builder = Builder::XmlMarkup.new(:target => expanded)
|
215
215
|
expand({method => args}, 'urn:partner.soap.sforce.com')
|
216
|
-
|
216
|
+
|
217
217
|
extra_headers = ""
|
218
218
|
extra_headers << (AssignmentRuleHeaderUsingRuleId % assignment_rule_id) if assignment_rule_id
|
219
219
|
extra_headers << AssignmentRuleHeaderUsingDefaultRule if use_default_rule
|
@@ -222,54 +222,54 @@ module RForce
|
|
222
222
|
#Fill in the blanks of the SOAP envelope with our
|
223
223
|
#session ID and the expanded XML of our request.
|
224
224
|
request = (Envelope % [@session_id, @batch_size, extra_headers, expanded])
|
225
|
-
|
225
|
+
|
226
226
|
# reset the batch size for the next request
|
227
227
|
@batch_size = DEFAULT_BATCH_SIZE
|
228
|
-
|
228
|
+
|
229
229
|
# gzip request
|
230
230
|
request = encode(request)
|
231
|
-
|
231
|
+
|
232
232
|
headers = {
|
233
233
|
'Connection' => 'Keep-Alive',
|
234
234
|
'Content-Type' => 'text/xml',
|
235
235
|
'SOAPAction' => '""',
|
236
236
|
'User-Agent' => 'ActiveSalesforce'
|
237
237
|
}
|
238
|
-
|
238
|
+
|
239
239
|
unless show_debug
|
240
240
|
headers['Accept-Encoding'] = 'gzip'
|
241
241
|
headers['Content-Encoding'] = 'gzip'
|
242
242
|
end
|
243
|
-
|
243
|
+
|
244
244
|
#Send the request to the server and read the response.
|
245
245
|
response = @server.post2(@url.path, request.lstrip, headers)
|
246
|
-
|
246
|
+
|
247
247
|
# decode if we have encoding
|
248
248
|
content = decode(response)
|
249
|
-
|
249
|
+
|
250
250
|
# Check to see if INVALID_SESSION_ID was raised and try to relogin in
|
251
|
-
if method != :login and @session_id and
|
251
|
+
if method != :login and @session_id and content =~ /sf:INVALID_SESSION_ID/
|
252
252
|
puts "\n\nSession timeout error - auto relogin activated"
|
253
|
-
|
253
|
+
|
254
254
|
login(@user, @password)
|
255
|
-
|
255
|
+
|
256
256
|
#Send the request to the server and read the response.
|
257
|
-
response = @server.post2(@url.path, request, headers)
|
258
|
-
|
257
|
+
response = @server.post2(@url.path, request.lstrip, headers)
|
258
|
+
|
259
259
|
content = decode(response)
|
260
260
|
end
|
261
|
-
|
261
|
+
|
262
262
|
SoapResponse.new(content)
|
263
263
|
end
|
264
|
-
|
265
|
-
|
264
|
+
|
265
|
+
|
266
266
|
# decode gzip
|
267
267
|
def decode(response)
|
268
268
|
encoding = response['Content-Encoding']
|
269
|
-
|
269
|
+
|
270
270
|
# return body if no encoding
|
271
271
|
if !encoding then return response.body end
|
272
|
-
|
272
|
+
|
273
273
|
# decode gzip
|
274
274
|
case encoding.strip
|
275
275
|
when 'gzip':
|
@@ -284,12 +284,12 @@ module RForce
|
|
284
284
|
response.body
|
285
285
|
end
|
286
286
|
end
|
287
|
-
|
288
|
-
|
287
|
+
|
288
|
+
|
289
289
|
# encode gzip
|
290
290
|
def encode(request)
|
291
291
|
return request if show_debug
|
292
|
-
|
292
|
+
|
293
293
|
begin
|
294
294
|
ostream = StringIO.new
|
295
295
|
gzw = Zlib::GzipWriter.new(ostream)
|
@@ -299,28 +299,28 @@ module RForce
|
|
299
299
|
gzw.close
|
300
300
|
end
|
301
301
|
end
|
302
|
-
|
303
|
-
|
302
|
+
|
303
|
+
|
304
304
|
#Turns method calls on this object into remote SOAP calls.
|
305
305
|
def method_missing(method, *args)
|
306
306
|
unless args.size == 1 && [Hash, Array].include?(args[0].class)
|
307
307
|
raise 'Expected 1 Hash or Array argument'
|
308
308
|
end
|
309
|
-
|
309
|
+
|
310
310
|
call_remote method, args[0]
|
311
311
|
end
|
312
|
-
|
313
|
-
|
312
|
+
|
313
|
+
|
314
314
|
#Expand Ruby data structures into XML.
|
315
315
|
def expand(args, xmlns = nil)
|
316
316
|
#Nest arrays: [:a, 1, :b, 2] => [[:a, 1], [:b, 2]]
|
317
317
|
if (args.class == Array)
|
318
318
|
args.each_index{|i| args[i, 2] = [args[i, 2]]}
|
319
319
|
end
|
320
|
-
|
320
|
+
|
321
321
|
args.each do |key, value|
|
322
322
|
attributes = xmlns ? {:xmlns => xmlns} : {}
|
323
|
-
|
323
|
+
|
324
324
|
#If the XML tag requires attributes,
|
325
325
|
#the tag name will contain a space
|
326
326
|
#followed by a string representation
|
@@ -330,16 +330,16 @@ module RForce
|
|
330
330
|
#becomes <sObject xsi:type="Opportunity>...</sObject>
|
331
331
|
if key.is_a? String
|
332
332
|
key, modifier = key.split(' ', 2)
|
333
|
-
|
333
|
+
|
334
334
|
attributes.merge!(eval(modifier)) if modifier
|
335
335
|
end
|
336
|
-
|
336
|
+
|
337
337
|
#Create an XML element and fill it with this
|
338
338
|
#value's sub-items.
|
339
339
|
case value
|
340
340
|
when Hash, Array
|
341
341
|
@builder.tag!(key, attributes) do expand value; end
|
342
|
-
|
342
|
+
|
343
343
|
when String
|
344
344
|
@builder.tag!(key, attributes) { @builder.text! value }
|
345
345
|
end
|
@@ -67,7 +67,7 @@ module Asf
|
|
67
67
|
action[:recording] = true if @recording
|
68
68
|
|
69
69
|
ActiveRecord::Base.logger = LOGGER
|
70
|
-
ActiveRecord::Base.
|
70
|
+
ActiveRecord::Base.clear_active_connections!
|
71
71
|
ActiveRecord::Base.reset_column_information_and_inheritable_attributes_for_all_subclasses
|
72
72
|
ActiveRecord::Base.establish_connection(action)
|
73
73
|
|
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.4.
|
7
|
-
date: 2006-
|
6
|
+
version: 0.4.9
|
7
|
+
date: 2006-04-05 00:00:00 -04:00
|
8
8
|
summary: ActiveSalesforce (ASF) is a Rails connection adapter that provides direct access to Salesforce.com hosted data and metadata via the ActiveRecord model layer. Objects, fields, and relationships are all auto surfaced as active record attributes and rels.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -103,5 +103,5 @@ dependencies:
|
|
103
103
|
requirements:
|
104
104
|
- - ">="
|
105
105
|
- !ruby/object:Gem::Version
|
106
|
-
version: 0.1.
|
106
|
+
version: 0.1.2
|
107
107
|
version:
|