Class variable, set if not defined

First, I am likely doing something very wrong and simply
misunderstanding how class variables work. I’m a Perl guy by default so
this is my first big project in Ruby.

I’m trying to store another (unrelated) instantiated class in them so
that each time I instantiate a subclass I don’t have to repeatedly
instantiate the unrelated classes. Not sure how else to say it, but
that certainly sounds like that sentence got away from me. Here is my
example (stripped down pseudo code so hopefully it still compiles…)

Here is what I WAS trying:
(it blows up)

class Importer
def initialize()
@@config = Config.new() if @@config.nil?
@@api = API.new() if @@api.nil?
@@util = Util.new() if @@util.nil?
@@db = DB.new() if @@db.nil?
@@dbh = @@db.dbh if @@dbh.nil?
end

def import()
    raise "this is abstract"
end

end

Then I have multiple sub classes such as:
class ItemDataImporter < Importer
def import()
# do stuff
end
end

I’ve tried this:
(It works but instantiates Config/API/Util and DB for every subclass I
instantiate)

class Importer
@@config = nil
@@api = nil
@@util = nil
@@db = nil
@@dbh = nil
@@pull_id= nil

def initialize()
    @@config     = Config.new()         if @@config.nil?
    @@api        = API.new()            if @@api.nil?
    @@util       = Util.new()           if @@util.nil?
    @@db         = DB.new()             if @@db.nil?
    @@dbh        = @@db.dbh             if @@dbh.nil?
end

def import()
    raise "this is abstract"
end

end

I’ve tried this as well (also blows up)

class Importer
@@config = Config.new() if @@config.nil?
@@api = API.new() if @@api.nil?
@@util = Util.new() if @@util.nil?
@@db = DB.new() if @@db.nil?
@@dbh = @@db.dbh if @@dbh.nil?

def import()
    raise "this is abstract"
end

end

Again, I’m certain it is just me either me misunderstanding the purpose
of class variables or misunderstanding something else entirely.

Any help would be mucho appreciated. Thanks in advance for fixing my
dumb.

Hi Jerry,

Please try to explain your goal. What are you trying to accomplish?

Ruby has two very different “class variables.”

There is the @@var_name version and the @var_name version.

No doubt a Google search will provide more info.

Lets say I have a script. import.rb


#!/usr/bin/ruby

require ‘lib/importer’

item_data_importer = ItemDataImporter.new()
price_importer = PriceImporter.new()

item_data_importer.import()
price_importer.import()

Where lib/importer is:

class Importer
def initialize()
@@config = Config.new() if @@config.nil?
@@api = API.new() if @@api.nil?
@@util = Util.new() if @@util.nil?
@@db = DB.new() if @@db.nil?
@@dbh = @@db.dbh if @@dbh.nil?
end

def import()
    raise "this is abstract"
end

end

Then I have multiple sub classes such as:
class ItemDataImporter < Importer
def import()
@@dbh[:items].insert(:item_id => 6, :name => ‘Item Name’);
end
end

class PriceImporter < Importer
def import()
@@dbh[:prices].insert(:price => 3.99, :item_id = 6 );
end
end

Imagine there being another 10 *Importer classes.

I was hoping to have @@dbh to be shared by both item_data_importer and
price_importer so I didn’t have to have a ton of DB connections. Or so
I didn’t have to have my @@config loaded into memory multiple times.

If I didn’t care about that, I could easily just use a single @ and it
would be just fine. I was just trying to be clever and more efficient
with resources. That and learn something new in the process. :slight_smile:

The problem is, it appears when I instantiate ItemDataImporter it
properly sets @@db, @@dbh, etc but when PriceImporter gets instantiated
it resets @@db and @@dbh (thus making a second connection to the db). I
was hoping that the first *Importer class that got instantiated would
set up the db connection and then the following *Importer classes that
got instantiated would be able to share it.

I spent quite a bit of time searching the web and couldn’t find how to
do this specific thing which was leading me to believe I am simply being
dumb. :slight_smile:

Does that make more sense?

Thank you Carlo,

I had tried something similar to that but not that exactly.

I was unaware that when declared in that scope, the variables are
created at parse time. This has just opened up a whole new realm of
awesome for me. :slight_smile:

Thank you so much for your help!

Jerry

Subject: Re: Class variable, set if not defined.
Date: dom 16 feb 14 07:53:45 +0100

Quoting Jerry Kilpatrick ([email protected]):

I was hoping to have @@dbh to be shared by both item_data_importer and
price_importer so I didn’t have to have a ton of DB connections. Or so
I didn’t have to have my @@config loaded into memory multiple times.

Did you try to have your importer as this:

class Importer
@@config=Config.new()
@@api=API.new()
@@util=Util.new()
@@db=DB.new()
@@dbh=@@db.dbh

def import()
raise “this is abstract”
end
end

Note that, in this case, the class variables are created when the file
is first parsed (i.e. at the time when ‘require lib/importer’ is
executed), not when the first object is created.

Carlo

I was hoping to have @@dbh to be shared by both item_data_importer and
price_importer so I didn’t have to have a ton of DB connections. Or so
I didn’t have to have my @@config loaded into memory multiple times.

If I didn’t care about that, I could easily just use a single @ and it
would be just fine. I was just trying to be clever and more efficient
with resources. That and learn something new in the process. :slight_smile:

If this is the real goal, I wouldn’t solve it using class variables or
class instance variables, I’d solve it using dependency injection.
Take the resources that your class depend on, and make responsibility
of other class the instantiation and handling of them to the
appropriate instances. In practice:

class Importer
def initialize(db)
@db = db
end

def import()
    raise "this is abstract"
end

end

class ItemDataImporter < Importer
def import()
@db[:items].insert(:item_id => 6, :name => ‘Item Name’);
end
end

Create dependencies

db = DB.new

Hand them out to the appropriate objects

item_data_importer = ItemDataImporter.new(db)
price_importer = PriceImporter.new(db)

item_data_importer.import()
price_importer.import()

Hope this helps,

Jesus.