Re: How to keep persistent socket connections?

Pardee, Roy
Fri, 22 May 2009 17:01:04 -0000

Heh--you''re welcome.  But please don''t let me bully you into
dropping rails--I don''t understand your goals as well as you do. ;-)
I''m just trying to explain options.  I frankly don''t
understand rails well enough to know when/how objects live beyond individual
http requesst/response cycles.  My guess is that your socket server thingy needs
to be independent of that in order to handle incoming connections reliably.

I can say that it is possible to add class *methods* to a descendant of
AR::Base--frx, in the below find_by_fragment is a class method.

  class SpecificTherapeuticClass < ActiveRecord::Base
    has_many :ingredients
    belongs_to :general_therapeutic_class
    def self.find_by_fragment(frag)
      frag += "%"
      self.find(:all, :conditions => ["description like :frag",
{:frag => frag}])
    end
  end

I''m not totally sure I follow your code below, but one thing jumps
out--this line:

  cattr_accessor :connection, :cert_name, :cert

Defines *class* methods & vars for those attributes.  So you''d say,
e.g. ConnectionManager.cert = "/bibbity/bobbity/boo.pem" and that bit
of data is entirely independent of any particular instance of ConnectionManager.
But initialize is an *instance* method--it gets called in the context of a new
instance of ConnectionManager.  So the line "cert = File.read..." in
your initialize method is just setting a local var ''cert''
which will die as soon as the initialize call is over.  If you want to define
instance methods connection, cert_name & cert, you''d use the
(native ruby) attr_accessor method (and you may want to call it as self.cert,
just to make it totally clear to the interpreter that you intend to call a
method rather than set a local var).

Maybe that helps?

Cheers,

-Roy

-----Original Message-----
From: rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
[mailto:rubyonrails-talk@googlegroups.com] On Behalf Of Steve Hull
Sent: Thursday, May 21, 2009 5:06 PM
To: rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org
Subject: [Rails] Re: How to keep persistent socket connections?


Roy --

Thank you for that example!!  I guess I''ll give that a shot    :)

It still hurts my heart that this doesn''t appear to be possible with
Rails.  :(

One followup question, if I may:
Why is it that class variables don''t behave as I expect them to?  So
since it appears that adding class attributes (inheritable or not) to a
descendent of ActiveRecord is unsupported, I added a new Class (inheriting from
Object) and put it in /lib:

class ConnectionManager
  HOST = "the.host.com"
  PATH = ''/''
  PORT = 2195

  PASSPHRASE = "foobar"
  CACERT = File.expand_path(File.dirname(__FILE__) +
"certs/ca.the.host.com.crt")
  USERAGENT = ''Mozilla/5.0 (ConnectionManager Ruby on Rails
0.1)''
  cattr_accessor :connection, :cert_name, :cert
  self.cert_name = "my_pem_file.pem"

  def initialize
    super
    cert = File.read("config/#{cert_name}") if
File.exists?("config/#{cert_name}")
    puts "cert = #{cert.inspect}"
    if connection.nil?
      puts "Connecting now!!"
      ctx = OpenSSL::SSL::SSLContext.new
      ctx.key = OpenSSL::PKey::RSA.new(cert, PASSPHRASE)
      ctx.cert = OpenSSL::X509::Certificate.new(cert)


      s = TCPSocket.new(HOST, PORT)
      connection = OpenSSL::SSL::SSLSocket.new(s, ctx)
      connection.sync = true
      connection.connect
    end
  end
end

But when I try to use this in my class that inherits from ActiveRecord,
like this:

  def send
    ssl = cm.class.connection
    logger.info "ssl = #{ssl.inspect}"
    ssl.write(self.message_for_sending)

  rescue SocketError => error
    raise "Error while sending: #{error}"
  end

First I got an error and I realized that my ConnectionManager''s
initialize method wasn''t being called.  So I added a line to get an
instance of ConnectionManager, just so that initialize would be called:

  def send_notification
    cm = ConnectionManager.new
    ssl = cm.class.connection
    logger.info "ssl = #{ssl.inspect}"
    ssl.write(self.message_for_sending)

  rescue SocketError => error
    raise "Error while sending notifications: #{error}"
  end

I could tell from my log ("Connecting Now!!" appeared) that initialize
had been called.  But I''m still getting an error:

You have a nil object when you didn''t expect it! The error occurred
while evaluating nil.write

Clearly ConnectionManager.connection is returning null.  In Java, once
you''ve used a class, its static vars are instantiated and hold their
values.  It seems that in Ruby, the class''s static vars are
instantiated/sandboxed in each .rb file.  Am I correct here, or no?

Thanks again for the example code Roy!

-Steve
--
Posted via http://www.ruby-forum.com/.



GHC Confidentiality Statement

This message and any attached files might contain confidential information
protected by federal and state law. The information is intended only for the use
of the individual(s) or entities originally named as addressees. The improper
disclosure of such information may be subject to civil or criminal penalties. If
this message reached you in error, please contact the sender and destroy this
message. Disclosing, copying, forwarding, or distributing the information by
unauthorized individuals or entities is strictly prohibited by law.