More elegant replacement for global variables

(Forrest) #1

I'm playing around with a ruby app using XML-RPC. Currently I'm using
global variables so I can create multiple objects without having to log
in for each and every object.

class Base
  def initialize(user, pass)
    $user, $pass = user, pass
  end

  def login()
    $client = XMLRPC::Client.new(...)
    $sessionid = $client.call("xmlrpc.method", $user, $pass)
    return $sessionid
  end
end

Derived classes look like this:

class Object1 < Base
  def initialize()
    if not $sessionid then self.login end
  end
end

This way I can log in once with the first object created without having
to log in each time. However, this seems inelegant to me. Is there a
more elegant, or just more rubyish, way of doing this? Also, I'd have
potential problems if I wanted to log on as multiple users.

Would it make more sense just to pass around an instance of the Base object:

class Object2
  def initialize(xmlrpc_client)
    if not xmlrpc_client.sessionid then xmlrpc_client.login
  end

  def call_xmlrpc_method(method, params)
    results = xmlrpc_client.call(method, params)
    return results
  end
end

xmlrpc_client = Base.new
o2 = Object2.new(xmlrpc_client)

Pro: no global variables. Con: coupling between Object2 and Base classes.
--Michael

#2

Constants are a kind of global variable. Classes get free constants.
You can use something like the Singeton pattern:

module XmlRpc
  def self.login(user, pass)
    @user = user
    @client = = XMLRPC::Client.new(...)
    @session_id = @client.call("xmlrpc.method", $user, $pass)
  end
  def self.user() @user or raise "Not initialized" end
  def self.client() @user or raise "Not initialized" end
  def self.session_id() @session_id or raise "Not initialized" end
end

Then you can login globally like so:
  XmlRpc.login("me", "my_password")

Then elsewhere you can use:
  client = XmlRpc.client
  session_id = XmlRpc.session_id
(Which will throw an exception if you're not logged in yet)

What do you think? It's still global.

Of course another idea is to use a single global, a Hash or OpenStruct,
for these related data points: $xmlrpc[:session_id]

Cheers,
Dave

(7rans) #3

Michael wrote:

I'm playing around with a ruby app using XML-RPC. Currently I'm using
global variables so I can create multiple objects without having to log
in for each and every object.

class Base
  def initialize(user, pass)
    $user, $pass = user, pass
  end

  def login()
    $client = XMLRPC::Client.new(...)
    $sessionid = $client.call("xmlrpc.method", $user, $pass)
    return $sessionid
  end
end

Derived classes look like this:

class Object1 < Base
  def initialize()
    if not $sessionid then self.login end
  end
end

This way I can log in once with the first object created without having
to log in each time. However, this seems inelegant to me. Is there a
more elegant, or just more rubyish, way of doing this? Also, I'd have
potential problems if I wanted to log on as multiple users.

Would it make more sense just to pass around an instance of the Base object:

class Object2
  def initialize(xmlrpc_client)
    if not xmlrpc_client.sessionid then xmlrpc_client.login
  end

  def call_xmlrpc_method(method, params)
    results = xmlrpc_client.call(method, params)
    return results
  end
end

xmlrpc_client = Base.new
o2 = Object2.new(xmlrpc_client)

Pro: no global variables. Con: coupling between Object2 and Base classes.
--Michael

Avoid globals by using container classes/modules. In your cace you
might use the base class itself.

   class Base
     class << self
       attr_accessor :user, :pass
       attr_reader :client, :sessionid
       def login(user=nil,pass=nil)
         @user = user if user
         @pass = pass if pass
         @client = XMLRPC::Client.new(...)
    @sessionid = @client.call("xmlrpc.method", @user, @pass)
         return @sessionid
       end
     end

     def initialize
       if not self.class.sessionid then self.class.login end
     end
   end

   class Object1 < Base
     def initialize()
       super
     end
   end

Then use

   Base.login('name','password')

or

   Base.user = 'name'
   Base.pass = 'password'
   Base.login

And go from there. Hope I understood the intent of your code correctly.

T.

(Robert) #4

Michael wrote:

I'm playing around with a ruby app using XML-RPC. Currently I'm using
global variables so I can create multiple objects without having to
log
in for each and every object.

class Base
def initialize(user, pass)
$user, $pass = user, pass
end

def login()
$client = XMLRPC::Client.new(...)
$sessionid = $client.call("xmlrpc.method", $user, $pass)
return $sessionid
end
end

Derived classes look like this:

class Object1 < Base
def initialize()
if not $sessionid then self.login end
end
end

This way I can log in once with the first object created without
having
to log in each time. However, this seems inelegant to me. Is there a
more elegant, or just more rubyish, way of doing this? Also, I'd have
potential problems if I wanted to log on as multiple users.

Would it make more sense just to pass around an instance of the Base
object:

class Object2
def initialize(xmlrpc_client)
if not xmlrpc_client.sessionid then xmlrpc_client.login
end

def call_xmlrpc_method(method, params)
results = xmlrpc_client.call(method, params)
return results
end
end

xmlrpc_client = Base.new
o2 = Object2.new(xmlrpc_client)

Pro: no global variables. Con: coupling between Object2 and Base
classes. --Michael

Although your scenario is not fully clear to me, I'd have a session
instance that stores all session related information. Also I would not
have your classes (Object1) inherit the session class but instead pass it
around.

Kind regards

    robert