function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
wycatswycats 

Update object with Ruby's SOAP

I'm building an adapter for Ruby's DataMapper for Salesforce. I've been able to use the WSDL with SOAP to make #query calls to Salesforce. However, I can't figure out how to use Ruby code to update an object (because Ruby's SOAP connector is not quite as snap-in as Java or C#'s connector).

The code I've been using to set up the connection is at: http://pastie.caboo.se/private/wcqjqv1icvshqpucsxupq

It's a little bit frustrating that Salesforce hung its Ruby hat on ActiveSalesforce, which is more a Rails/ActiveRecord solution than a general-purpose Ruby solution. Ideally, Salesforce would have built a low-level library to connect to its webservices, and then built ActiveSalesforce on top of it (the code for ASF is a mess, and includes a partial solution that someone was building prior to the release of ASF).

Anyhow, I'd appreciate any help someone could provide about making reliable #update calls back to the server for objects returned as a result of a #query call.
dchasmandchasman
ASF is not mostly a Rails connection adapter - it is very specifically a Rails connection adapter. What ASF has to do is very tricky and Rails did not make bridging from a very relational DB specific design to a web service based backend a trivial thing to do - a somewhat perverse undertaking that amounted to the only Relational-to-Object adapter I am aware of. That impedance mismatch resulted in the need to parse a canonical SQL like dialect and transform that into web service calls - hence the significant amount of regular expressions being used to slice and dice what Rails kicks out.

ASF has been developed 100% on my own personal time as a labor of love initiated entirely out of curiosity - not a business need. At the time I initiated the project I did the same search that you've been going through to find something better than just the vanilla Ruby soap stack and that is when I found RForce which handled a number of the salesforce API specific bits. I've been relatively happy with what RForce provides - but I suspect our needs are different - I was building a generic adapter from Rails to any salesforce api exposed object (Partner API access) and I suspect you are looking for something that provides a type specific layer on top of the Enterprise API. With that said I am surprised that RForce is not a workable solution for you - it handles updates w/out any issues that I am aware of.

I am not actively developing ASF at this time and for the past 14 months I have been attempting to recruit contributors that would like to give something back to the project. The result of that is a few maybes and a lot of I would love to but can't find the time. I know that the author of RForce has also faced similar frustrations when he has asked for the community to put some skin in the game (neither one of us uses Ruby or RoR in our day jobs anymore). Perhaps you would like to ante up and help improve either RForce /or ASF? I'd be happy to train up anyone willing to roll up their sleeves and get their hands dirty.


Message Edited by dchasman on 04-10-2008 10:28 AM
JasonRogersJasonRogers
I am using RForce for many integrations -- some trivial -- most non-trivial.  It works fine for us.
ykatzykatz
@dchasman: I somehow missed your update to this thread. I'm actually working quite heavily with Salesforce at the moment, and my employer (Engine Yard) would be happy to help improve the existing OSS tools around SalesForce.

Feel free to contact me at any of the following:

email: wycats@gmail.com
gtalk: wycats@gmail.com
IM: outlookeic
Phone: 718.877.1325

Thanks again,

-- Yehuda
Swivel SupportSwivel Support
I'm having the same problem as noted above.

RForce does not work for me.  I am unable to login at all using RForce, but I am able to login with the SOAP::wsdlDriver.

Running the code below correctly returns the lead object, but I can't update it. I get the following error: #<SOAP::Mapping::Object:0x2654ddc>: INVALID_TYPE: Must send a concrete entity type. (SOAP::FaultError)

Any help would be greatly appreciated!:-)

#!/usr/bin/env ruby
require 'rubygems'
require "soap/wsdlDriver"
require 'soap/header/simplehandler'
require 'fastercsv'

# http://dev.ctor.org/soap4r/browser/trunk/sample/wsdl/salesforce/client.rb?rev=1609
class SessionHeaderHandler < SOAP::Header::SimpleHandler
  HeaderName = XSD::QName.new('urn:enterprise.soap.sforce.com', 'SessionHeader')

  attr_accessor :sessionid

  def initialize
    super(HeaderName)
    @sessionid = nil
  end

  def on_simple_outbound
    if @sessionid
      {'sessionId' => @sessionid}
    else
      nil       # no header
    end
  end
end

class CallOptionsHandler < SOAP::Header::SimpleHandler
  HeaderName = XSD::QName.new('urn:enterprise.soap.sforce.com', 'CallOptions')

  attr_accessor :client

  def initialize
    super(HeaderName)
    @client = nil
  end

  def on_simple_outbound
    if @client
      {'client' => @client}
    else
      nil       # no header
    end
  end
end

class SForce

  def initialize
    @driver = SOAP::WSDLDriverFactory.new(File.dirname(__FILE__) + '/salesforce_ent.wsdl').create_rpc_driver
    @driver.options['protocol.http.ssl_config.verify_mode'] = OpenSSL::SSL::VERIFY_NONE
    login = @driver.login(:username => 'XXXXX', :password => 'YYYYY')
    @driver.endpoint_url = login.result.serverUrl
   
    sessionid_handler = SessionHeaderHandler.new
    sessionid_handler.sessionid = login.result.sessionId
    @driver.headerhandler << sessionid_handler
   
    calloptions_handler = CallOptionsHandler.new
    calloptions_handler.client = 'client'
    @driver.headerhandler << calloptions_handler
   
    @driver.wiredump_dev = STDOUT
  end

  def query(string)
    qr = @driver.query(:queryString => string)
    num_results = qr.result.size.to_i
 
    done = (num_results == 0)
   
    while not done
      done = qr.result.done
     
      if num_results == 1
        yield qr.result.records
      else
        qr.result.records.each do |record|
          yield record
        end
      end
     
      qr = @driver.queryMore(qr.result.queryLocator) unless done
    end
  end
 
  def update(sObject)
    res = @driver.update(:sObjects => [sObject])
  end
end

sforce = SForce.new

sforce.query('select Id, Email, Invite_only_user__c from lead where email = \'foo@bar.com\'') do |l|

  sforce.update(l)
end