Solution: Nested Classes & Variable Scope (Ruby)
Posted: December 20th, 2009 | Author: Spoofy | Filed under: Articles, Programming | Tags: bug, Programming, ruby, variable-scope | Comments OffI recently began work on a Twitter library for Ruby, some already exist -which I didn’t consider, I wanted to develop a library of my own design which took a very object-oriented approach to accessing Twitter, mostly for practice. Because I wanted an intuitive, and for the lack of a better word, ‘logical’ implementation, I decided to use nested classes.
Ruby nested classes are child classes which are defined within a parent class. Unbeknownst to me, Ruby has some interesting quirks when using nested classes. Take for example, a typical class:
class A
attr_accessor :var
def initialize
@var = "something interesting"
end
def tellme
puts @var
end
end
Nothing peculiar here, but lets say we want a child class – for all intents and purposes, a namespace, which doesnt inherit from its parent but does exist inside it:
class A
attr_accessor :var
def initialize
@var = "something interesting"
end
def say
puts @var
end
class B
def tellme
puts @var
end
end
end
What we’re doing here is creating a parent class, A; inside that class we have another class B which is a part-of class A, but is not an inherited child. This is an important distinction, because an inherited child would have their own instance variables – what we have here is a class, A which ‘owns’ B. In theory, B should have access to A’s variables.
Heres a usage example, which you might expect to work:
parent = A.new
parent.tellme
child = A::B.new
child.tellme
In theory, we should have the same output, ‘something interesting‘ from calling the tellme method on each class, but no. Instead the child will throw an exception, why? because the instance variable is not visible – it is outside its variable scope. To reiterate: nested classes are outside the variable scope of their parent class.
This is true of class variables and constants. Nested children of a parent class simply do not have access to their parents, unless you provide that functionality programmatically:
class A
attr_accessor :b, :var
def initialize
@var = "something interesting"
@b = B.new(self)
end
def say
puts @var
end
class B
def initialize(theparent)
@parent = theparent
end
def tellme
puts @parent.var
end
end
end
a = A.new
a.tellme
a.b.tellme
This is the solution I ended up using, hopefully this post will have helped you avoid some of the frustration I went through trying to figure it out.