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?

···

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

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

···

On Mon, Oct 25, 2010 at 3:58 PM, John Doe <bl4.929b@playker.info> wrote:

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?

-----
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

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

More info on inheritance and class variables:

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.

···

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

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