Need help with class variables and inheritance

Hi,

Suppose i wrote something like this:

class TestClass
@@testmethod = lambda { puts “inside testmethod” }
def method_missing(name, *args)
eval("@@#{name}").call(*args)
end
end

irb(main):002:0> TestClass.new.testmethod
inside testmethod

It works fine but if I have more classes i don’t want to duplicate the
same method_missing. I thought i could write this:

class TestSuperClass
def method_missing(name, *args)
eval("@@#{name}").call(*args)
end
end
class TestSubClass < TestSuperClass
@@testmethod = lambda { puts “inside testmethod” }
end

but then i get this error:

irb(main):003:0> TestSubClass.new.testmethod
NameError: (eval):1:in `method_missing’: uninitialized class variable
@@testmethod in TestSuperClass

Is there any way to get it to work?

On Mon, Oct 25, 2010 at 3:58 PM, John D. [email protected] wrote:

end
Is there any way to get it to work?
The @@variables are shared among subclasses, not superclasses. So you
could define the variable in the superclass:

class TestSuperClass
@@testmethod = 1
def method_missing(name, *args)
eval(“@@#{name}”).call(*args)
end
end

class TestSubClass < TestSuperClass
@@testmethod = lambda { puts “inside testmethod” }
end

TestSubClass.new.testmethod


or, you could use an instance variable, since I’m not sure class
variables are really useful in Ruby:

class TestSuperClass
def method_missing(name, *args)
eval(“@#{name}”).call(*args)
end
end

class TestSubClass < TestSuperClass
def initialize
@testmethod = lambda { puts “inside testmethod” }
end
end

TestSubClass.new.testmethod

On 10/25/2010 05:19 PM, Leslie V. wrote:

The @@variables are shared among subclasses, not superclasses. So you
could define the variable in the superclass:

class TestSuperClass
@@testmethod = 1

Yes but if i have 2 subclasses with 2 methods in each then i will need
to define 4 variables in the superclass.

@@method0 = @@method1 = @@method2 = @@method3 = 1

And every time i want to add a method in a subclass i will need to go to
the superclass code and add another one. That’s the sort of duplication
i would like to avoid.

or, you could use an instance variable, since I’m not sure class
variables are really useful in Ruby:

class TestSubClass < TestSuperClass
def initialize
@testmethod = lambda { puts “inside testmethod” }
end
end

If you put something in initialize then when you change the code and
reload it in a running program (or running irb) you need to recreate all
existing objects. With class variables there is no need to.

… but more importantly, why do you want to do this? There’s likely
to be a better way.

I know it’s nothing useful but this is how i learn. I want to make an
alternative to the def keyword. For example, i want to have a way of
defining methods which take arguments in reverse.

defbackward(:mymethod) do |x,y,z|
[x,y,z]
end
irb(main):004:0> mymethod(1,2,3)
=> [3, 2, 1]

I thought i would use method_missing and this leads to the problem i
showed in the first post.

More info on inheritance and class variables:

Right, this explains a lot. I ended up with:

class TestSuperClass
def self.testdefhash
@testdefhash
end
def self.testdef(name, &block)
(@testdefhash ||= {})[name] = block
end
def method_missing(name, *args)
self.class.testdefhash[name].call(*args)
end
end
class TestSubClassA < TestSuperClass
testdef(:testmethod) { puts “inside testmethod a” }
testdef(:anothermethod) { puts “inside anothermethod a” }
end
class TestSubClassB < TestSuperClass
testdef(:testmethod) { puts “inside testmethod b” }
testdef(:anothermethod) { puts “inside anothermethod b” }
end

irb(main):002:0> TestSubClassA.new.testmethod
inside testmethod a
irb(main):003:0> TestSubClassB.new.anothermethod
inside anothermethod b

So far it works. Thanks for help.