Given the superclass in the code below, I want all subclasses to have some instance variable.
The code below does that, but fails to properly initialize that variable for all possible subclasses.
I opened the eigenclass of my superclass. Here is the code (also in rubyfiddle):
class SomeSuperClass
class << self
attr_accessor :variable
@variable = ': )' # This does't seem to have any effect
end
self.variable = 'This only works for the superclass'
end
class SubClass < SomeSuperClass; end
puts SomeSuperClass.variable # => 'This only works for the superclass'
puts SubClass.variable # => ''
SomeSuperClass.variable = 'I am the superclass'
SubClass.variable = 'I am the subclass'
puts SomeSuperClass.variable # => 'I am the superclass'
puts SubClass.variable # => 'I am the subclass'
I would like to have all possible sublcasses initialized. On the first two puts, only SomeSuperClass.variable
is initialized. I don't know how to initialize this variable for all possible subclasses. Any ideas?
The best solution I found is to lazy-initialize the variable, overriding the accessor, as in:
class SomeSuperClass
def self.variable
@variable ||= 'Hi'
end
end
The motivation:
I need all subclasses of a given class, lets call it Vigilant, be able to monitor some things happening on their direct subclasses. This information is stored on the class, and therefore have a different state for each one.
I can't use a class variable, since two classes A < B would be modifying the same variable. I can't access directly to the subclasses either, so I needed a way to give all subclasses of Vigilant the capacity for storing and retrieving the information about their subclasses.
By defining the accessors opening the eigen class, lets say:
A.singleton_class.instance_eval { attr_accessor :x }
All subclasses Bclass B < A; end
are now able to do B.x
, because a method (an accessor) was added to its superclass eigen class, and therefore can be found on the lookup.
And the first example shows that B.x is different from A.x
Now, what I'm really not understanding is where x is; the variable, not the accessors.
If I do B.instance_variables
it shows []
, same with B.singleton_class.instance_variables
I want all subclasses to have a variable on their singleton class / eigenclass.
Sorry, that is not what you are doing here:
puts SomeSuperClass.variable # => 'This only works for the superclass'
puts SubClass.variable # => '
Why would you think that writing
SomeSuperClass.variable
is equivalent to the pseudo code:
SomeSuperClassSingletonClass.variable
or the real code:
SomeSuperClass.singleton_class.variable
A class and it's singleton class are two different Classes.
In addition, this code:
class << self
attr_accessor :variable
@variable = ': )' # This does't seem to have any effect
end
does not create an accessor for that @variable, the same way that this code:
class Dog
attr_accessor :x
@x = 'hello'
end
puts Dog.x
...does not create an accessor for that @x variable:
--output:--
undefined method `x' for Dog:Class (NoMethodError)
What attr_accessor() does is this:
class Dog
def x
@x
end
def x=(val)
@x = val
end
#=====
@x = 'hello'
end
Those methods have nothing to do with the class instance variable @x, which was defined outside all the defs. @variables are looked up (or set) on whatever object is self at that instant. The only objects that can call those defs are instances of class Dog, and therefore x will be looked up (or set) on a Dog instance--not the Dog class.
Also note that when the line @x = 'hello'
executes, self is equal to the Dog class, therefore @x attaches itself to the Dog class.
I don't think you have a use case for setting an instance variable on a singleton class. Here is what it seems like you are trying to do:
class SomeSuperClass
class << self
attr_accessor :variable
end
self.variable = 'hello'
def self.inherited(subclass)
subclass.singleton_class.instance_eval do
attr_accessor :variable
end
subclass.variable = "Hi"
end
end
class SubClass < SomeSuperClass
end
puts SomeSuperClass.variable
puts SubClass.variable
--output:--
hello
Hi
That code creates what are known as class instance variables
. If you think you have a use case for singleton class instance variables
, let's hear it.