Tag Archives: SOAP

Birth and death of an object in Ruby

Last week I played with a SOAP API with savon.rb to develop a newsletter system.

This API require authentication with a two-way handshake. At first you send a login, a password and a key and it gave you back a token. When you ended operation you should send a “close connection” command.

This is a popular way but when you use it in a model seems a bit tricky so I decided to hide this method inside a custom class.

First I create ApiWrapper class that wrap SOAP operation:

class ApiWrapper

  def initialize
    @client = Savon::Client.new APP_CONFIG[:wsdl]
  end

  def open_connection
    response = do_soap_request("openApiConnection", {
      :login => APP_CONFIG[:login],
      :pwd => APP_CONFIG[:pwd],
      :key => APP_CONFIG[:key]
    })
    @token = response[:token]
  end

  def close_connection
    response = do_soap_request("closeApiConnection", {
      :token => @token
    })
  end

  def do_soap_request(method, arguments)
    response = @client.request(:api, method) do
      soap.body = arguments
    end
    response.to_hash
  end

end

Then use the ApiWrapper object inside the model:

class Newsletter

  def initialize
    @api = ApiWrapper.new
  end

  def send
    # do something
  end

end

Now we can use the @api object for communicate with the API. I want to hide the authentication process, first attempt can be to incapsulate operation inside each method:

def send
  @api.open_connection
  @api.do_soap_request("send", arguments)
  @api.close_connection
end

It works but the webservice il very slow, opening and closing connection require about 20 seconds and for hundred operation is too much time.

Another way is to encapsulate authentication process inside object initialization and finalization. For this we need something similar to a C++ constructor and destructor method. Ruby has got the initialize method similar to constructor but it does not have a standard destructor. Luckily we can emulate it.

The ObjectSpace module enable you to define block that is executed when the object is destroyed using the define_finalizer method.

ObjectSpace.define_finalizer(self, Proc.new{@api.close_connection})

In the initialize method we can open the connection and define this finalizer. We can rewite the method as follow:

def initialize
  @api = ApiWrapper.new
  @api.open_connection
  ObjectSpace.define_finalizer(self, Proc.new{@api.close_connection})
end

Now we can use the object with easier and more efficient connection management.

> n = Newsletter.new # implicit connection open
> n.send # connection is still open
> n.send # connection is still open
> n.send # connection is still open
> quit # implicit connection close

These methods are very useful (also) to mask unnecessary complexity and make the data access more readable.