<p>A client of mine has asked me to integrate a 3rd party API into their Rails app.  The only problem is that the API uses SOAP.  Ruby has basically dropped SOAP in favor of REST.  They provide a Java adapter that apparently works with the Java-Ruby bridge, but we'd like to keep it all in Ruby, if possible.  I looked into soap4r, but it seems to have a slightly bad reputation.</p>

<p>So what's the best way to integrate SOAP calls into a Rails app?</p>

[{'answer_id': 40318, 'author': 'Jason Navarrete', 'author_id': 3920, 'author_profile': 'https://Stackoverflow.com/users/3920', 'pm_score': 2, 'selected': False, 'text': '<p>Try <strong>SOAP4R</strong></p>\n\n<ul>\n<li><a href="http://dev.ctor.org/soap4r" rel="nofollow noreferrer">SOAP4R</a></li>\n<li><a href="http://markthomas.org/2007/09/12/getting-started-with-soap4r/" rel="nofollow noreferrer">Getting Started with SOAP4R</a></li>\n</ul>\n\n<p>And I just heard about this on the Rails Envy Podcast (ep 31):</p>\n\n<ul>\n<li><a href="http://hideoustriumph.wordpress.com/2008/05/05/ws-deathstar-for-the-rest-of-us/" rel="nofollow noreferrer">WS-Deathstar SOAP walkthrough </a></li>\n</ul>\n'}, {'answer_id': 40961, 'author': 'Orion Edwards', 'author_id': 234, 'author_profile': 'https://Stackoverflow.com/users/234', 'pm_score': 6, 'selected': True, 'text': "<p>We used the built in <code>soap/wsdlDriver</code> class, which is actually SOAP4R.\nIt's dog slow, but really simple. The SOAP4R that you get from gems/etc is just an updated version of the same thing.</p>\n\n<p>Example code:</p>\n\n<pre><code>require 'soap/wsdlDriver'\n\nclient = SOAP::WSDLDriverFactory.new( 'http://example.com/service.wsdl' ).create_rpc_driver\nresult = client.doStuff();\n</code></pre>\n\n<p>That's about it</p>\n"}, {'answer_id': 1208200, 'author': 'Philippe Monnet', 'author_id': 23308, 'author_profile': 'https://Stackoverflow.com/users/23308', 'pm_score': 2, 'selected': False, 'text': '<p>Kent Sibilev from <a href="http://www.datanoise.com/" rel="nofollow noreferrer">Datanoise</a> had also ported the Rails ActionWebService library to Rails 2.1 (and above).\nThis allows you to expose your own Ruby-based SOAP services.\nHe even has a scaffold/test mode which allows you to test your services using a browser.</p>\n'}, {'answer_id': 2088376, 'author': 'phoet', 'author_id': 100731, 'author_profile': 'https://Stackoverflow.com/users/100731', 'pm_score': 4, 'selected': False, 'text': '<p>We switched from Handsoap to Savon.</p>\n\n<p>Here is a <a href="http://www.dzone.com/links/savon_handsoap_shootout_two_popular_ruby_soap_cli.html" rel="noreferrer">series of blog posts</a> comparing the two client libraries.</p>\n'}, {'answer_id': 2088452, 'author': 'rubiii', 'author_id': 141590, 'author_profile': 'https://Stackoverflow.com/users/141590', 'pm_score': 7, 'selected': False, 'text': '<p>I built <a href="http://savonrb.com" rel="noreferrer">Savon</a> to make interacting with SOAP webservices via Ruby as easy as possible.<br>\nI\'d recommend you check it out.</p>\n'}, {'answer_id': 2432222, 'author': 'Bruno Duyé', 'author_id': 276894, 'author_profile': 'https://Stackoverflow.com/users/276894', 'pm_score': 3, 'selected': False, 'text': '<p>I also recommend <a href="http://wiki.github.com/rubiii/savon" rel="nofollow noreferrer">Savon</a>. I spent too many hours trying to deal with Soap4R, without results. Big lack of functionality, no doc.</p>\n\n<p>Savon is the answer for me.</p>\n'}, {'answer_id': 6140364, 'author': 'ChrisW', 'author_id': 709569, 'author_profile': 'https://Stackoverflow.com/users/709569', 'pm_score': 2, 'selected': False, 'text': "<p>Just got my stuff working within 3 hours using Savon.</p>\n\n<p>The Getting Started documentation on Savon's homepage was really easy to follow - and actually matched what I was seeing (not always the case)</p>\n"}, {'answer_id': 26263023, 'author': 'Ecommerce-Technician', 'author_id': 4044067, 'author_profile': 'https://Stackoverflow.com/users/4044067', 'pm_score': 1, 'selected': False, 'text': '<p>I was having the same issue, switched to Savon and then just tested it on an open WSDL (I used <a href="http://www.webservicex.net/geoipservice.asmx?WSDL" rel="nofollow">http://www.webservicex.net/geoipservice.asmx?WSDL</a>) and so far so good!</p>\n\n<p><a href="https://github.com/savonrb/savon" rel="nofollow">https://github.com/savonrb/savon</a></p>\n'}, {'answer_id': 35152994, 'author': 'Raja', 'author_id': 3999718, 'author_profile': 'https://Stackoverflow.com/users/3999718', 'pm_score': 2, 'selected': False, 'text': '<p>I have used HTTP call like below to call a SOAP method,</p>\n\n<pre><code>require \'net/http\'\n\nclass MyHelper\n  def initialize(server, port, username, password)\n    @server = server\n    @port = port\n    @username = username\n    @password = password\n\n    puts "Initialised My Helper using #{@server}:#{@port} username=#{@username}"\n  end\n\n\n\n  def post_job(job_name)\n\n    puts "Posting job #{job_name} to update order service"\n\n    job_xml ="&lt;soapenv:Envelope xmlns:soapenv=\\"http://schemas.xmlsoap.org/soap/envelope/\\" xmlns:ns=\\"http://test.com/Test/CreateUpdateOrders/1.0\\"&gt;\n    &lt;soapenv:Header/&gt;\n    &lt;soapenv:Body&gt;\n       &lt;ns:CreateTestUpdateOrdersReq&gt;\n          &lt;ContractGroup&gt;ITE2&lt;/ContractGroup&gt;\n          &lt;ProductID&gt;topo&lt;/ProductID&gt;\n          &lt;PublicationReference&gt;#{job_name}&lt;/PublicationReference&gt;\n       &lt;/ns:CreateTestUpdateOrdersReq&gt;\n    &lt;/soapenv:Body&gt;\n &lt;/soapenv:Envelope&gt;"\n\n    @http = Net::HTTP.new(@server, @port)\n    puts "server: " + @server  + "port  : " + @port\n    request = Net::HTTP::Post.new((\'/XISOAPAdapter/MessageServlet?/Test/CreateUpdateOrders/1.0\'), initheader = {\'Content-Type\' =&gt; \'text/xml\'})\n    request.basic_auth(@username, @password)\n    request.body = job_xml\n    response = @http.request(request)\n\n    puts "request was made to server " + @server\n\n    validate_response(response, "post_job_to_pega_updateorder job", \'200\')\n\n  end\n\n\n\n  private \n\n  def validate_response(response, operation, required_code)\n    if response.code != required_code\n      raise "#{operation} operation failed. Response was [#{response.inspect} #{response.to_hash.inspect} #{response.body}]"\n    end\n  end\nend\n\n/*\ntest = MyHelper.new("mysvr.test.test.com","8102","myusername","mypassword")\ntest.post_job("test_201601281419")\n*/\n</code></pre>\n\n<p>Hope it helps. Cheers.</p>\n'}, {'answer_id': 37676319, 'author': 'Radu Rosu', 'author_id': 6082342, 'author_profile': 'https://Stackoverflow.com/users/6082342', 'pm_score': 2, 'selected': False, 'text': '<p>I have used SOAP in Ruby when i\'ve had to make a fake SOAP server for my acceptance tests. I don\'t know if this was the best way to approach the problem, but it worked for me. </p>\n\n<p>I have used Sinatra gem (I wrote about creating mocking endpoints with Sinatra <a href="http://intelligentbee.com/blog/2016/01/06/how-to-mock-endpoints-in-automated-acceptance-tests/" rel="nofollow">here</a>) for server and also <a href="https://rubygems.org/gems/nokogiri/versions/1.6.7.2" rel="nofollow">Nokogiri</a> for XML stuff (SOAP is working with XML).</p>\n\n<p>So, for the beginning I have create two files (e.g. config.rb and responses.rb) in which I have put the predefined answers that SOAP server will return. \nIn <em>config.rb</em> I have put the WSDL file, but as a string. </p>\n\n<pre><code>@@wsdl = \'&lt;wsdl:definitions name="StockQuote"\n         targetNamespace="http://example.com/stockquote.wsdl"\n         xmlns:tns="http://example.com/stockquote.wsdl"\n         xmlns:xsd1="http://example.com/stockquote.xsd"\n         xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"\n         xmlns="http://schemas.xmlsoap.org/wsdl/"&gt;\n         .......\n      &lt;/wsdl:definitions&gt;\'\n</code></pre>\n\n<p>In <em>responses.rb</em> I have put samples for responses that SOAP server will return for different scenarios.</p>\n\n<pre><code>@@login_failure = "&lt;s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"&gt;\n    &lt;s:Body&gt;\n        &lt;LoginResponse xmlns="http://tempuri.org/"&gt;\n            &lt;LoginResult xmlns:a="http://schemas.datacontract.org/2004/07/WEBMethodsObjects" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"&gt;\n                &lt;a:Error&gt;Invalid username and password&lt;/a:Error&gt;\n                &lt;a:ObjectInformation i:nil="true"/&gt;\n                &lt;a:Response&gt;false&lt;/a:Response&gt;\n            &lt;/LoginResult&gt;\n        &lt;/LoginResponse&gt;\n    &lt;/s:Body&gt;\n&lt;/s:Envelope&gt;"\n</code></pre>\n\n<p>So now let me show you how I have actually created the server.</p>\n\n<pre><code>require \'sinatra\'\nrequire \'json\'\nrequire \'nokogiri\'\nrequire_relative \'config/config.rb\'\nrequire_relative \'config/responses.rb\'\n\nafter do\n# cors\nheaders({\n    "Access-Control-Allow-Origin" =&gt; "*",\n    "Access-Control-Allow-Methods" =&gt; "POST",\n    "Access-Control-Allow-Headers" =&gt; "content-type",\n})\n\n# json\ncontent_type :json\nend\n\n#when accessing the /HaWebMethods route the server will return either the WSDL file, either and XSD (I don\'t know exactly how to explain this but it is a WSDL dependency)\nget "/HAWebMethods/" do\n  case request.query_string\n    when \'xsd=xsd0\'\n        status 200\n        body = @@xsd0\n    when \'wsdl\'\n        status 200\n        body = @@wsdl\n  end\nend\n\npost \'/HAWebMethods/soap\' do\nrequest_payload = request.body.read\nrequest_payload = Nokogiri::XML request_payload\nrequest_payload.remove_namespaces!\n\nif request_payload.css(\'Body\').text != \'\'\n    if request_payload.css(\'Login\').text != \'\'\n        if request_payload.css(\'email\').text == some username &amp;&amp; request_payload.css(\'password\').text == some password\n            status 200\n            body = @@login_success\n        else\n            status 200\n            body = @@login_failure\n        end\n    end\nend\nend\n</code></pre>\n\n<p>I hope you\'ll find this helpful!</p>\n'}]
['https://Stackoverflow.com/questions/40273', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/2884/']