Dynamically added instance method can not access class variable [duplicate] - ruby

This question already has an answer here:
Ruby looks for class variable in the Object instead of specific class
(1 answer)
Closed 6 years ago.
(Question already posted at Ruby Forum, but did not evoke any answer there).
This is my code:
class MC
def initialize
#x = 5
##y = 6
end
def f
puts #x
puts ##y
end
end
m = MC.new
m.f
m.f produces the expected output without an error:
5
6
But this:
def m.g
puts #x
puts ##y
end
m.g
produces:
5
warning: class variable access from toplevel
NameError: uninitialized class variable ##y in Object
Why can I access ##y from f, but not from g?
Mentioning of toplevel and Object in the warning and the error message is puzzling to me.
#x is printed as 5, so its environment is MC. This excludes the possibility that #x and ##y in the definition of m.g refer to the toplevel environment (Object) instead of MC.
Why did I get the error message?

All variants below work:
def m.g; puts self.class.send(:class_eval, '##y') end
def m.g; puts self.class.class_variable_get(:##y) end
class << m; def g; puts self.class.send(:class_eval, '##y') end end
class << m; puts class_variable_get(:##y) end
But these fail:
def m.g; puts ##y; end
class << m; puts class_eval('##y') end
I would consider this being a ruby parser glitch.

You do not create g in the class MC but in m's singleton class (a.k.a. eigenclass).
This is a class existing specifically for the object m to store the singleton methods that are defined just for m.

Related

How can I access a superclass class variable from a subclass in Ruby

I am trying to access a class variable from a subclass. I understand that class variables are not inherited which answers the question of why the code does not work, however I don't fully understand how I can work around it.
This is my code:
Class A
...
class << self
def format(a, b)
#format = a
end
def item(a, b)
#item[a] = b
end
end
end
Class B < A
format 4, 7
item 7, 12
...
end
Class C < B
item 7, 18
end
Running the following in a irb session
B.format => 4
C.format => nil
So understanding that class variables aren't inherited, is it possible to make C.format => 4 or will I need to refactor as such:
Class B < A
format 4, 7
item 7, 12
end
Class C < A
format 4, 7
item 7, 18
end
My reason for wanting to avoid the latter is that I have a lot of variables defined in that same way (calling a function to set a class variable) and I don't want to have to duplicate all the code across class B and C because one instance variable differs.
I understand that class variables are not inherited
This is completely false and you have not actually understood what class variables are.
A class variable in Ruby is declared with the ## sigil. And they are definitely inherited:
class A
##x= "Hello World"
end
class B < A
puts ##x # Outputs "Hello World"
end
Class variables are actually shared between a class and its subclasses:
class Animal
##greating = "Hello"
def greet
"#{##greating} I'm a #{self.class.name.downcase}"
end
end
class Cat < Animal
##greating = "Meow"
end
class Dog < Animal
##greating = "Woof"
end
puts Dog.new.greet # Woof I'm a dog
puts Cat.new.greet # Woof I'm a cat
puts Animal.new.greet # Woof I'm a animal
As you can see from the example this can often lead to unexpected and undesireable effects. And class variables are not considered thread safe.
What you are actually setting is referred to as a class instance variable - this is just a instance variable except that its scope is not a instance of A. Instead its scope is the singleton class A which is an instance of the Class class.
Unlike true class variables which have their own sigil class instance variables are not shared between a class and its subclasses as thier scope is the singleton class. Every subclass its own singleton class.
class Animal
#greating = "Hello"
def self.greeting
#greating
end
def greet
"#{self.class.greeting} I'm a #{self.class.name.downcase}"
end
end
class Cat < Animal
#greating = "Meow"
end
class Dog < Animal
#greating = "Woof"
end
puts Dog.new.greet # Woof I'm a dog
puts Cat.new.greet # Meow I'm a cat
puts Animal.new.greet # Hello I'm a animal
Class instance variables are actually far more useful then true class variables. If you want to simulate the inheritance of class variables with class instance variables you can do it with the Class#inherited callback provided by Ruby:
class A
#x = [self.name]
class << self
attr_accessor :x
def inherited(subclass)
puts "#{subclass.name} is inheriting from #{self.name}"
subclass.x = x
subclass.x.push(subclass.name)
end
end
end
class B < A; end # outputs "B is inheriting from A"
class C < B; end # outputs "C is inheriting from B"
puts B.x.inspect # => ['A', 'B']
puts C.x.inspect # => ['A', 'B', 'C']
Firstly, to define a class you need to use the keyword class not Class.
Secondly, there is no reason to pass the method A::format an argument it does not use. I therefore changed it to have just one argument.
Thirdly, after defining A, when A.item is first executed an exception will be raised because #item has not been defined. Specifically, #item (like any other undefined instance variable) will return nil when called and nil has no no method []. I therefore changed the method item to initialize the class instance variable (not class variable) #item to an empty array.
class A
class << self
def format(a)
#format = a
end
def item(a, b)
#item = []
#item[a] = b
end
end
end
class B < A
format 4
item 7, 12
end
class C < B
item 7, 18
def self.format
superclass.instance_variable_get(:#format)
end
end
It is my understanding that you want a method C::format to return the value of B's instance variable #format.
C.format
#=> 4
If one attempts to execute B.format an exception will be raised because B::format requires two arguments. If B.format is meant to return the value of B's class instance variable #format you need to write:
B.instance_variable_get(:#format)
#=> 4
You might add read and write accessors for the the instance variable #format, in which case your code would be simplified somewhat:
class A
class << self
attr_accessor :format
def item(a, b)
#item = []
#item[a] = b
end
end
end
class B < A
#format = 4
item 7, 12
end
class C < B
item 7, 18
def self.format
superclass.format
end
end
C.format
#=> 4

difference between declaring object with single '#' and double '#' in Ruby [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What does ##variable mean in Ruby?
What is the difference when I declare an object with double '#'
##lexicon = Lexicon.new()
and declaring object with single '#' in Ruby?
#lexicon = Lexicon.new()
The difference is that the first one is a class variable and the second one is an instance variable.
An instance variable is only available to that instance of an object. i.e.
class Yasin
def foo=(value)
#foo = value
end
def foo
#foo
end
end
yasin = Yasin.new
yasin.foo=1
yasin.foo #=> 1
yasin_2 = Yasin.new
yasin_2.foo #> nil
A class variable is available to all instances of the class (and subclasses, iirc) where the class variable was defined.
class Yasin
def foo=(value)
##foo = value
end
def foo
##foo
end
end
yasin = Yasin.new
yasin.foo=1
yasin.foo #=> 1
yasin_2 = Yasin.new
yasin_2.foo #=> 1

Ruby metaprogramming: adding #variables to existing 'initialize' method (using class_eval) [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Ruby.Metaprogramming. class_eval
I have this little project, the goal is to create a 'attr_accessor_with_history' method, that will keep a record of every single value assigned to variables created by it. Here's the code :
class Class
def attr_accessor_with_history(attr_name)
attr_name = attr_name.to_s # make sure it's a string
attr_reader attr_name # create the attribute's getter
attr_reader attr_name+"_history" # create bar_history getter
a = %Q{
def initialize
##{attr_name}_history = [nil]
end
def #{attr_name}
##{attr_name}
end
def #{attr_name}=(new_value)
##{attr_name}=new_value
##{attr_name}_history.push(new_value)
end }
puts a
class_eval(a)
end
end
Now, when I test the script for one variable. It works fine. But when I try to create two or more variables (like this) ....
class Foo
attr_accessor_with_history :bar
attr_accessor_with_history :lab
end
a = Foo.new
a.bar = 45
a.bar = 5
a.bar = 'taat'
puts a.bar_history
b = Foo.new
b.lab = 4
b.lab = 145
b.lab = 'tatu'
puts b.lab_history
....Ruby gives out a "no-existing 'push' method for (class_eval) bar_history.push(new_value)". I think that 'initialize' method gets overriden on the second call of attr_accessor_with_history, so the record for the first variable gets destroyed.
I have no idea how to get around this. I already tried calling 'super' . Any clue ?
In your setter method just check if the the history instance variable is already initialized:
def #{attr_name}=(new_value)
##{attr_name}=new_value
##{attr_name}_history ||= [nil]
##{attr_name}_history.push(new_value)
end
You'll need another getter for your history variable that sets your default value if it was not set before:
def #{attr_name}_history
##{attr_name}_history ||= [nil]
end
Then you could remove your initialize method, that was btw vulnerable to be overwritten.

Ruby: Unexpected Results from class_exec When Defining Class Variable

In Ruby, when defining the contents of a class with class_exec, I am getting unexpected results. When I define a class variable in the block sent to class_exec, the class variable is being defined on Object instead of the class on which class_exec is being called:
class X; end
X.class_exec do
##inner_value = "123"
def inner_value
##inner_value
end
def inner_value=(arg)
##inner_value = arg
end
end
obj1 = X.new
puts obj1.inner_value
puts ##inner_value
puts Object.class_variables
Produces:
123
123
##inner_value
This does not happen when using class_eval:
X.class_eval(<<-RUBY)
##inner_value = "123"
def inner_value
##inner_value
end
def inner_value=(arg)
##inner_value = arg
end
RUBY
obj1 = X.new
puts obj1.inner_value
puts ##inner_value
puts Object.class_variables
Produces:
123
and an error:
uninitialized class variable ##inner_value in Object (NameError)
The results with class_eval are what I would expect to happen in both cases. I have tried this with both MRI 1.8.7 and MRI 1.9.3 and got the same results running on Windows XP.
Is this expected behavior? If so, why? If not, bug?
class variables are bound to the class in which they are declared at compile time. The block passed to class_exec is compiled before it is passed to class_exec, so the class variables are bound to Object.
I guess your class_exec is at the top level, which is in Object, so that's where they go. To demonstrate:
public
class Object
##x = "ribbit"
end
def foo
puts "test: #{##x}"
end
x = Object.new
x.foo
This is why when you use class vars in a module, all classes that include that module (through the included methods) will see the same class variables. The class variables are bound to the module. If you run this:
class WithClassVars
def self.classvars
#classvars ||= {}
end
def classvars
self.class.classvars
end
end
class A < WithClassVars;end
class B < WithClassVars;end
a = A.new
b = B.new
a.classvars[:a] = 1
b.classvars[:a] = 2
puts a.classvars
puts b.classvars
a and b will end up with the same data.
If you pass your code as a string to class_eval, the string is compiled in class_eval, so you can make sure they are in the right class then.
So if you want to store per-class data, you have to either go with class_eval, or use some mechanism to use a class's instance variables. Say:
class WithClassVars
def self.classvars
#classvars ||= {}
end
def classvars
self.class.classvars
end
end
class A < WithClassVars;end
class B < WithClassVars;end
a = A.new
b = B.new
a.classvars[:a] = 1
b.classvars[:a] = 2
puts a.classvars
puts b.classvars

In Ruby, when should you use self. in your classes? [duplicate]

This question already has answers here:
When to use `self.foo` instead of `foo` in Ruby methods
(3 answers)
Closed 9 years ago.
When do you use self.property_name in Ruby?
Use self when calling a class's mutator. For example, this won't work:
class Foo
attr_writer :bar
def do_something
bar = 2
end
end
The problem is that 'bar = 2' creates a local variable named 'bar', rather than calling the method 'bar=' which was created by attr_writer. However, a little self will fix it:
class Foo
attr_writer :bar
def do_something
self.bar = 2
end
end
self.bar = 2 calls the method bar=, as desired.
You may also use self to call a reader with the same name as a local variable:
class Foo
attr_reader :bar
def do_something
bar = 123
puts self.bar
end
end
But it's usually better to avoid giving a local variable the same name as an accessor.
self references the current object. This lends itself to many uses:
calling a method on the current object
class A
def initialize val
#val = val
end
def method1
1 + self.method2()
end
def method2
#val*2
end
end
Here running A.new(1).method1() will return 3. The use of self is optional here - the following code is equivalent:
class A
def initialize val
#val = val
end
def method1
1 + method2()
end
def method2
#val*2
end
end
self is not redundant for this purpose though - operator overloading makes it neccessary:
class A
def initialize val
#val = val
end
def [] x
#val + x
end
def method1 y
[y] #returns an array!
end
def method2 y
self.[y] #executes the [] method
end
end
This shows how self must be used if you want to call the current object's [] method.
referencing attributes
You can generate the methods to read and write to instance variables using attr_accessor and co.
class A
attr_accessor :val
def initialize val
#val = val
end
def increment!
self.val += 1
end
end
Using self is redundant here because you can just reference the variable directly, eg. #val.
Using the previous class, A.new(1).increment! would return 2.
method chaining
You can return self to provide a form of syntactical sugar known as chaining:
class A
attr_reader :val
def initialize val
#val = val
end
def increment!
#val += 1
self
end
end
Here, because we are returning the current object, methods can be chained:
A.new(1).increment!.increment!.increment!.val #returns 4
creating class methods
You can define class methods using self:
class A
def self.double x
x*2
end
def self.quadruple x
self.double(self.double(x))
end
end
This will enable you to call A.double(2) #= 4 and A.quadruple(2) #=8. Note that in a class method, self references that class because the class is the current object.
how the value of self is determined
The current value of self in a particular method is set to the object that that method was called upon. Normally this uses the '.' notation. When you run some_object.some_method(), self is bound to some_object for the duration of some_method, meaning that some_method can use self in one of the ways mentioned above.
Using self is used will reference the current object accessible within a program. Therefore, self.property is used when accessing a variable through a attr_accessor of some sort. In must cases, it can be used in place of #property from within an object.

Resources