Class method is accessing instance variable - ruby

I'd like to understand this code. Why is it returning Hello instead of Howdy! ?
class Speaker
#message = "Hello!"
class << self
#message = "Howdy!"
def speak
#message
end
end
end
puts Speaker.speak

First off, your message #message is not an instance variable, or rather not the type of instance variable you may be thinking about: it's a class-level instance var, so an instance variable of Speaker itself, which as an object is an instance of class Class.
Here's a version of the code that does what you're trying to do with a local variable and a closure:
class Speaker
#message = "Hello!"
class << self
message = "Howdy!"
define_method(:speak) { message }
end
end
Speaker.speak
#=> "Howdy!"
And here's some code that illustrates the difference between the class-level instance variable and a "normal" instance variable:
class Speaker
#message = 'Howdy!' # class-level instance variable
def initialize
#message = 'Hello!' # instance variable of Speaker's instances
end
def speak
#message
end
class << self
def speak
#message
end
end
end
Speaker.speak
#=> "Howdy!"
Speaker.new.speak
#=> "Hello!"

Here is your code, except I've defined the class method in the usual way (def self.speak...). As a class method is nothing more than an instance method defined on the class' singleton class, this change is merely a different way of creating the same class method. (If you doubt that, run the code below both ways.) I made that change because I thought it would make my explanation of what is happening clearer. I also added a puts statement.
class Speaker
#message = "Hello!"
def self.speak
puts "self=#{self}"
#message
end
class << self
#message = "Howdy!"
end
end
The first line of the class definition creates a class instance variable #message:
Speaker.instance_variables
#=> [:#message]
Speaker.instance_variable_get(:#message)
#=> "Hello!"
By constrast,
#message = "Howdy!"
creates an instance variable on Speaker's singleton class:
Speaker.singleton_class.instance_variables
#=> [:#message]
Speaker.singleton_class.instance_variable_get(:#message)
#=> "Howdy!"
Now invoke speak on Speaker:
Speaker.speak
# self=Speaker
#=> "Hello!"
As self #=> Speaker, speak is obviously returning the value of the class instance variable.
For speak to return the value of the instance variable defined on Speaker's singleton class we can write the following:
class Speaker
#message = "Hello!"
def self.speak
puts "self=#{self}"
puts "singleton_class = #{singleton_class}"
singleton_class.instance_variable_get :#message
end
class << self
#message = "Howdy!"
end
end
puts Speaker.speak
# self=Speaker
# singleton_class = #<Class:Speaker>
# Howdy!
In the last expression, because self equals Speaker and self is the implied receiver when there is no explicit receiver, "singleton_class is equivalent to Speaker.singleton_class.

The reason this code returns 'Hello' is that it is attempting to change an instance variable in a class << self block.
Class methods are for anything that does not deal with an individual instance of that class - instance variables are tied to individual instances of a class, and we can't change instance variables at a class level.
Instead of using an instance variable in the speak method, we should use a class variable (denoted by ##).
As an example, the following code will return 'Howdy!' -
class Speaker
##message = "Hello!"
class << self
##message = "Howdy!"
def speak
##message
end
end
end
puts Speaker.speak

Related

Variables in class methods

could you please explain me why the class variable cannot be accessed by attribute_accessors?
As i am trying here to have the list of all methods of all subclasses in one array it works a little different. It created array #method_names in every subclass with specific methods for every class ... so i do need to do a loop through subclasses.
What kind of variable/attribute is #method_names?
Thanks!
module First
class First_class
class << self
def info
puts "First_class method info."
puts #subclasses
puts #method_names
end
def inherited(subclass)
puts "#{subclass} located ..."
subclasses << subclass
end
def subclasses
#subclasses ||= []
end
def method_added(method_name)
puts "Method located #{method_name} ..."
method_names << method_name
end
def method_names
#method_names ||= []
end
end
def initialize
puts "Instance of First_class is created."
end
def first_method
end
end
class Second_class < First_class
def self.info
puts "Second_class method info."
puts #subclasses
puts #method_names
end
def second_method
end
def initialize
puts "Instance of Second_class is created."
end
end
class Third_class < First_class
def third_method
end
def initialize
puts "Instance of Third_class is created."
end
end
end
First::First_class.subclasses.each {
|subclass| puts subclass
subclass.method_names.each {
|methodn| puts methodn
}
}
#################UPDATE#########
Ok, maybe I put the question incorrectly.
Basically what is the difference for ##method_names(class variable) and #method_names (instance variable) if i do not create the instance of object? After inserting more inputs into #method_names it still inserts into the same object_id. So what is benefit of ##method_names?
updated to answer updated question.
Classes in ruby can have class variables. However if you modify the class level variable, ALL instances will be modified. This is not recommended but will illustrate the point. But also see this answer
class Foo
##bar = 'bar'
attr_accessor :bar
def initialize
#bar = 'bar'
end
def class_bar
##bar
end
def change_class_bar string
raise ArgumentError unless string.is_a?(String)
##bar = string
end
end
a = Foo.new
b = Foo.new
# change the class variable ##bar
b.change_class_bar 'wtf?'
# see both instances are changed because objects are passed by referrence
print 'a.class_bar is: '
puts a.class_bar
print 'b.class_bar is: '
puts b.class_bar
# change one instance only
a.bar = 'only a has changed'
print 'a.bar is: '
puts a.bar
print 'b.bar is still: '
puts b.bar
run this and you should get output:
a.class_bar is: wtf?
b.class_bar is: wtf?
a.bar is: only a has changed
b.bar is still: bar
original answer left here
#method_names is an instance variable of an instance of the class from which it was instantiated. However it cannot be accessed for read/write unless those attributes are defined with getter or setter methods defined.
ff = First::First_class.new
Instance of First_class is created.
=> #<First::First_class:0x00007fde5a6867b8>
ff.method_names
NoMethodError: undefined method `method_names' for #<First::First_class:0x00007fde5a6867b8>
Did you mean? methods
Now if you call ff.methods you will see all methods defined through standard Ruby inheritance.
As a side note, class names in Ruby conventionally use PascalCase see PascalCase. Mixed_case is discouraged.

Having trouble understanding the scope of instance variables in Ruby

I have two sets of code
#movies = ["Zoolander", "Sandlot"]
def good_movies
#movies.each do |movie|
puts "I love #{movie}!"
end
end
good_movies
#I love Zoolander!
#I love Sandlot!
I understand of the scope of local variables, so there has to be instance variables for the code to run otherwise I would get a NameError
But when it comes to this bit
class Example
#scope = "This is the scope of the class itself"
def hello
p #scope
end
end
ex = Example.new
ex.hello
# nil
Looks like the predefined instance variable cannot be accessed but I didn't get an error. It should be to do with the instantiation, but I don't know the exact reason why.
Can anyone give me some pointers on this?
Looks like the predefined instance variable cannot be accessed but I didn't get an error.
Accessing an undefined instance variable doesn't raise an exception, it just returns nil.
Since you define the instance variable on the class object itself, you need a method in the same scope to access it, i.e. a class method:
class Example
#scope = "This is the scope of the class itself"
def self.hello
p #scope
end
end
Example.hello
#=> "This is the scope of the class itself"
To define a variable for your instances, use initialize: (so each instance has its own #scope)
class Example
def initialize
#scope = "This is the scope of an instance"
end
def hello
p #scope
end
end
ex = Example.new
ex.hello
#=> "This is the scope of an instance"
You could also access the instance's class via Object#class and its instance variable via instance_variable_get:
class Example
#scope = "This is the scope of the class itself"
def hello
p self.class.instance_variable_get(:#scope)
end
end
ex = Example.new
ex.hello
#=> "This is the scope of the class itself"
Apparently this violates any encapsulation efforts. If you really wanted to do this, you should define a getter:
class Example
#scope = "This is the scope of the class itself"
class << self
attr_reader(:scope)
end
def hello
p self.class.scope
end
end
ex = Example.new
ex.hello
#=> "This is the scope of the class itself"
Example.scope
#=> "This is the scope of the class itself"

Circumventing Ruby's scope gates when defining class methods

I have a class A, which I would like to anonymously extend and add a class method to the child class. E.g.:
class A
end
Class.new A do
def self.new_class_method
puts 'I am a class method'
end
end.new_class_method
=> I am a class method
The above example works well, unless you want to access some variables outside of the def self.new_class_method block. E,g,
greeting = 'hello'
Class.new A do
def self.new_class_method
puts greeting + ' I am a class method'
end
end.new_class_method
=> NameError: undefined local variable or method `greeting'
I am using Ruby 1.8.7, which is sad because I believe Ruby 1.9+ contains an analog to define_method which adds a class method. Does anyone have a work around for 1.8.7?
I have tested the below in Ruby 1.8.7 :-
greeting = 'hello'
class A
end
Class.new A do
meta_klass = class << self; self ;end
meta_klass.send(:define_method, :new_class_method) do
puts greeting + ' I am a class method'
end
end.new_class_method
# >> hello I am a class method
As Ruby 1.8.7 doesn't support Object#singleton_class, I used meta_klass = class << self; self ;end. This method is available since 1.9.2, I think.
You can also use extend() to pry open an object's singleton class. Calling extend(module) adds the methods in the module to the calling object's(i.e. the receiver's) singleton class. So if you call extend(module) when self=A, i.e. inside class A, then the module's methods will be inserted into A's singleton class, and the methods in A's singleton class are also known as class methods of A:
class A
end
greeting = "hello"
Class.new(A) do
extend(
Module.new do
define_method(:greet) do
puts greeting
end
end
)
end.greet
--output:--
hello
And you can rewrite that like this (although then it's not as tricky):
class A
end
greeting = "hello"
Class.new(A) do
m = Module.new do
define_method(:greet) do
puts greeting
end
end
extend(m)
end.greet
...which isn't much different than:
class A
end
greeting = "hello"
m = Module.new do
define_method(:greet) do
puts greeting
end
end
Class.new(A) do
extend(m)
end.greet
...which moves the closure out of the class, and doesn't seem very tricky at all because it only opens up two scope gates instead of three.
Also note, extend() is a public method, so it doesn't require the trickery of a private method, i.e. where you can't specify an explicit receiver, so you have to create a context in which self is the object you want to call the private method on. In other words, you can specify an explicit receiver for extend(). How about the class that is returned by Class.new(A)?
class A
end
greeting = "hello"
Class.new(A).extend(
Module.new do
define_method(:greet) do
puts greeting
end
end
).greet
--output:--
hello
Hey, tacking on ".greet" there works! Uh oh, that has the makings of a one liner:
class A
end
greeting = "hello"
Class.new(A).extend(Module.new {define_method(:greet) {puts greeting} }).greet
--output:--
hello
Yeech!
More generally,
class A
end
class Object
def meta_def name, &blk
(class << self; self; end).instance_eval { define_method name, &blk }
end
end
greeting = 'hello'
Class.new A do
meta_def :new_class_method do
puts greeting + ' I am a class method'
end
end.new_class_method
#=> hello I am a class method
If you find this useful, don't thank me, thank some lucky stiff (which I saw mentioned by Jay Fields).
Not sure if this will solve your problem, but changing Greeting to uppercase (making it a constant) would work...
class A
end
Greeting = 'hello'
Class.new A do
def self.new_class_method
puts Greeting + ' I am a class method'
end
end.new_class_method

instance variable declaration

The following code prints nothing, I was expecting welcomeBack. Please explain.
class Hello
#instanceHello = "welcomeBack"
def printHello
print(#instanceHello)
end
Hello.new().printHello();
end
I just started to lean ruby, so please forgive if the question looks dumb.
If you could memorize only one thing about defining methods and setting variables, it's this: always ask yourself, what is self at this point?
class Hello
# This ivar belongs to Hello class object, not instance of Hello.
# `self` is Hello class object.
#instanceHello = "welcomeBack"
def printHello
# Hello##instanceHello hasn't been assigned a value. It's nil at this point.
# `self` is an instance of Hello class
print #instanceHello
end
def self.printSelfHello
# Now this is Hello.#instanceHello. This is the var that you assigned value to.
# `self` is Hello class object
print #instanceHello
end
end
Hello.new.printHello # >>
Hello.printSelfHello # >> welcomeBack
If you want to set a default value for an ivar, do it in the constructor:
class Hello
def initialize
#instanceHello = "welcomeBack"
end
def printHello
print #instanceHello
end
end
Hello.new.printHello # >> welcomeBack
In Ruby, instance variables are defined and used in instance methods. So you need to put your assignment in the initialize method:
class Hello
def initialize
#instanceHello = "welcomeBack"
end
def printHello
print(#instanceHello)
end
end
Hello.new.printHello();
Also, note that I moved the printHello call outside of the class definition. This is because the class isn't actually defined until after it is closed the first time; that is, after the last end.
class Hello
#instanceHello = "welcomeBack"
def printHello
puts self.class.instance_variable_get(:#instanceHello)
end
Hello.new.printHello; #=> welcomeBack
end
This is not good programming, but just to illustrate that a class (which is an instance of the class Class) can also have instance variables, like any other instance. They are called "class instance variables" and are preferred to class variables. The following example illustrates how a counter could be defined at the class level :
class Hello
#counter = 0
class << self # access methods for class instance variables must be
# defined in the singleton class
attr_accessor :counter
end
def printHello
self.class.counter += 1
puts "Hello #{self.class.counter}"
end
end
Hello.new.printHello; #=> Hello 1
Hello.new.printHello; #=> Hello 2
p Hello.singleton_methods #=> ["counter=", "counter"]
change #instanceHello to self.class.instance_variable_get(:#instanceHello)
#instanceHello is a class instance variant
the code is this:
class Hello
#instanceHello = "welcomeBack"
##classHello = "greeting!"
def printHello
print(self.class.instance_variable_get(:#instanceHello))
print(self.class.class_variable_get(:##classHello))
end
end
Hello.new().printHello();

Dynamically creating class method

I am writing a class method to create another class method. There seems to be some strangeness around how class_eval and instance_eval operate within the context of a class method. To illustrate:
class Test1
def self.add_foo
self.class_eval do # does what it says on the tin
define_method :foo do
puts "bar"
end
end
end
end
Test1.add_foo # creates new instance method, like I'd expect
Test1.new.foo # => "bar"
class Test2
def self.add_foo
self.instance_eval do # seems to do the same as "class_eval"
define_method :foo do
puts "bar"
end
end
end
end
Test2.add_foo # what is happening here?!
Test2.foo # => NoMethodError
Test2.new.foo # => "bar"
class Test3
def self.add_foo
(class << self; self; end).instance_eval do # call explicitly on metaclass
define_method :foo do
puts "bar"
end
end
end
end
Test3.add_foo # => creates new class method, as I'd expect
Test3.foo # => "bar"
My understanding is that class methods are instance methods defined on the metaclass of the class in question (Test2 in this case). Based on that logic, I would expect the receiver for the class method call add_foo to be the metaclass.
What is self referring to inside the Test2.add_foo method?
Why does calling instance_eval on this receiver object create an instance method?
The main difference between instance_eval and class_eval is that instance_eval works within the context of an instance, while class_eval works within the context of a class. I am not sure how familiar you are with Rails, but let's look at this for an example:
class Test3 < ActiveRecord::Base
end
t = Test3.first
t.class_eval { belongs_to :test_25 } #=> Defines a relationship to test_25 for this instance
t.test_25 #=> Method is defined (but fails because of how belongs_to works)
t2 = Test3.find(2)
t2.test_25 #=> NoMethodError
t.class.class_eval { belongs_to :another_test }
t.another_test #=> returns an instance of another_test (assuming relationship exists)
t2.another_test #=> same as t.another_test
t.class_eval { id } #=> NameError
t.instance_eval { id } #=> returns the id of the instance
t.instance_eval { belongs_to :your_mom } #=> NoMethodError
This happens because belongs_to is actually a method call that happens within the context of the class body, which you cannot call from an instance. When you try to call id with class_eval, it fails because id is a method defined on an instance, not in a class.
Defining methods with both class_eval and instance_eval work essentially the same when called against an instance. They will define a method only on the instance of the object it is called against.
t.class_eval do
def some_method
puts "Hi!"
end
end
t.instance_eval do
def another_method
puts "Hello!"
end
end
t.some_method #=> "Hi!"
t.another_method #=> "Hello!"
t2.some_method #=> NoMethodError
t2.another_method #=> NoMethodError
They differ, however, when dealing with the class.
t.class.class_eval do
def meow
puts "meow!"
end
end
t.class.instance_eval do
def bark
puts "woof!"
end
end
t.meow #=> meow!
t2.meow #=> meow!
t.bark #=> NoMethodError
t2.bark #=> NoMethodError
So where did bark go? It got defined on the instance of the class' singleton class. I'll explain more below. But for now:
t.class.bark #=> woof!
Test3.bark #=> woof!
So to answer your question about what self is referring to within the class body, you can construct a little test:
a = class Test4
def bar
puts "Now, I'm a #{self.inspect}"
end
def self.baz
puts "I'm a #{self.inspect}"
end
class << self
def foo
puts "I'm a #{self.inspect}"
end
def self.huh?
puts "Hmmm? indeed"
end
instance_eval do
define_method :razors do
puts "Sounds painful"
end
end
"But check this out, I'm a #{self.inspect}"
end
end
puts Test4.foo #=> "I'm a Test4"
puts Test4.baz #=> "I'm a Test4"
puts Test4.new.bar #=> Now I'm a #<Test4:0x007fa473358cd8>
puts a #=> But check this out, I'm a #<Class:Test4>
So what is happening here is that in the first puts statement above, we see that inspect is telling us that self within the context of a class method body is referring to the class Test4. In the second puts, we see the same thing, it's just defined differently (using the self.method_name notation for defining class methods). In the third, we see that self refers to an instance of Test4. The last one is a bit interesting because what we see is that self is referring to an instance of Class called Test4. That is because when you define a class, you're creating an object. Everything in Ruby is an object. This instance of object is called the metaclass or the eigenclass or singleton class.
You can access the eigenclass with the class << self idiom. While you're in there, you actually have access to the internals of the eigenclass. You can define instance methods inside of the eigenclass, which is congruent with calling self.method_name. But since you are within the context of the eigenclass, you can attach methods to the eigenclass' eigenclass.
Test4.huh? #=> NoMethodError
Test4.singleton_class.huh? #=> Hmmm? indeed
When you call instance_eval in the context of a method, you're really calling instance_eval on the class itself, which means that you are creating instance methods on Test4. What about where I called instance_eval inside of the eigenclass? It creates a method on the instance of Test4's eigenclass:
Test4.razors #=> Sounds painful
Hopefully, this clears up a few of your questions. I know that I have learned a few things typing out this answer!

Resources