Reputation: 4768
Let's say I have code like this:
class TestClass
module ClassMethods
attr_accessor :number
def initialize
@number = 47
end
end
include ClassMethods
extend ClassMethods
end
TestClass.new.number
returns 47
, which is expected, but TestClass.number
returns nil
.
How can I initialize number
variable for both TestClass
class and TestClass
instances?
So far, I've done it like this:
class TestClass
module ClassMethods
attr_accessor :number
def initialize
@number = 47
end
end
include ClassMethods
extend ClassMethods
@number = 47
end
I do not like this approach because number
is initialized in two places.
Upvotes: 0
Views: 1627
Reputation: 110755
Suppose you had this:
class TestClass
module ClassMethods
attr_accessor :number
def initialize
@number = 47
end
end
include ClassMethods
extend ClassMethods
end
Let's get some information about this class:
TestClass.instance_variables #=> []
No surprise there.
tc = TestClass.new #=> #<TestClass:0x00000101098a10 @number=47>
p tc.instance_variables #=> [:@number]
tc.number #=> 47
tc.number = 11
All as expected. The instance variable @number
was created by initialize
and we can inspect and change its value with the accessor.
extend
made TestClass#initialize
a class method, but it has not been invoked. Let's invoke it to initialize the class instance variable @number
:
TestClass.initialize
#=> NoMethodError: private method `initialize' called for TestClass:Class
Ah, yes, initialize
is a private method.
TestClass.methods.include?(:initialize) #=> false
TestClass.private_methods.include?(:initialize) #=> true
We cannot invoke private class methods in the usual way. send
, however, works with private as well as public methods:
TestClass.send :initialize #=> 47
TestClass.instance_variables #=> [:@number]
TestClass.instance_variable_get(:@number) #=> 47
So now the class instance variable has been created set equal to 47
. Has it changed the value of the instance variable @number
?
tc.number #=> 11
It's not changed. Now let's change the value of the class instance variable and then see if the value of instance variable is affected:
TestClass.instance_variable_set(:@number, -5) #=> -5
tc.number #=> 11
If you'd like to add an accessor for the class instance variable @number
, add this line to the class or module:
Module.instance_eval("attr_accessor :number")
(For an explanation, see my answer here.)
Then test:
TestClass.number #=> -5
TestClass.number = 107
TestClass.number #=> 107
Upvotes: 1