Ruby: Calling instance method from class method - ruby

I want to call some_instance_method from some_class_method. In the following code, the class method is being called from the instance method.
class Foo
def self.some_class_method
puts self
end
def some_instance_method
self.class.some_class_method
end
end

Does what you want to do really make sense? Which instance should the instance method be called on? If it doesn't matter, you can do this:
class Foo
def self.some_class_method
new.some_instance_method
puts self
end
def some_instance_method
self.class.some_class_method
end
end
This will cause an infinite loop, of course.

To call an instance method you need to have that object already instantiated somewhere, you can't call an instance method without an object.
def self.some_class_method
puts self.first.some_instance_method
end
Something like this should work

You need to pass the instance you want to be the recipient of the call to the class method. So the new code is:
class Foo
def self.some_class_method(instance)
instance.some_other_instance_method
puts self
end
def some_instance_method
self.class.some_class_method(self)
end
end
Notice that the class method is calling 'some_other_instance_method'. If the class method were to call the same method that called it, it would enter an endless loop.

Related

Accessing methods in class body

Can somebody explain the following behavior?
def a_method
"a_method() was called by #{self}"
end
class MyClass
puts a_method
def another_method
puts a_method
end
end
MyClass.new.another_method
It produces following output:
a_method() was called by MyClass
a_method() was called by #<MyClass:0x000000034962b0>
How does puts a_method within the MyClass body work? How could I access a_method while defining Myclass?
(Outermost) Ruby statements are called and executed in the order they are written.
Within MyClass, there is a method call puts a_method, where self is the class MyClass. This prints the first line of your output. Then, later, there is a method call another_method, which calls puts a_method, where self is an instance of MyClass. This prints the second line of your output.
How could I access a_method while defining Myclass?
This seems to imply that you consider class definition to be completed when the (first occurrence of a) class body is closed. That is however not the case. A class is created right after you open the class body for the first time. Hence, in the class body, you can call methods on the class. Defining methods within a class body is not part of the creation of the class; the class is already there, and it is modifying the class.
The first call is executed in Class scope. Therefore self is the class. The second is executed in instance scope of the class. You could add an class method to have the same effect like first method call.
def a_method
"a_method() was called by #{self}"
end
class MyClass
puts a_method # how this works?
def another_method
puts a_method
end
def self.a_class_method
puts a_method
end
end
MyClass.a_class_method
MyClass.new.another_method
# result
a_method() was called by MyClass
a_method() was called by MyClass
a_method() was called by #<MyClass:0x00000002dc49f0>
A class body executes when it initially loads(global variable, class variables and Constants are defined within it, and finally methods are defined).
So that a_method is called by MyClass when it is defined.

Calling instance method in Class in Ruby

I am very confused about this. In Programming Ruby book, it says,
"receiver checks for the method definition in its own class"
So class object stores all instance methods. Then why can't I call
instance method from within a class?
For example
Class ExampleClass
def example_method
end
example_method
end
I cannot call example_method inside ExampleClass.
However if I define a method in top level like this:
class ExampleClass
def example_method
end
end
def example_method1
end
example_method1
Then I can call top level method example_method1.
Isn't top level also a class? How come it is different than
a calling instance method from within ExampleClass?
The biggest reason that you cannot call that function in the way that you have written it is that it is, as you say, an instance method.
Try defining it in this way:
class ExampleClass
def self.class_method
puts "I'm a class method"
end
class_method
end
I believe you will find that you have a different result. It's not that it's "Top Level", it's whether or not it's in scope for what you're dealing with. Since you're dealing with a class, a class method would be necessary. If you're dealing with an object (an instantiated class) it's a different "scope".
Those "global" methods are an exception. They are defined as private instance methods of Object. Everything inherits from Object, so these methods are "globally" visible.
p self.class # => Object
p self.private_methods.sort # => [:Array, :Complex, ... :using, :warn] # all (?) from Kernel module
def aaaa
end
p self.private_methods.sort # => [:aaaa, :Array, ... :using, :warn]
The receiver checks for the method definition in its own class. The receiver is ExampleClass. The class of ExampleClass is Class. There is no example_method method in the Class class, ergo, you get a NoMethodError.
I'll try to explain it as follows.
class MyClass
def self.my_method
puts "Me, I'm a class method. Note that self = #{self}"
end
def my_method
puts "Me, I'm an instance method. Note that self = #{self}"
end
# I'm about to invoke :my_method on self. Which one will it be?"
# "That depends on what self is now, of course.
puts "self = #{self}"
# OK. It's MyClass. But wait. I'm just defining the set now.
# Do the methods I defined above even exist yet?
# Does the class exist yet? Let's find out.
print "class methods: "
puts self.methods(false)
print "instance methods: "
puts self.instance_methods(false)
# Cool! Let's try invoking my_method
my_method
# It worked. It was the class method because self = MyClass
# Now let's see if we can create an instance of the class before
# we finish defining the class. Surely we can't.
my_instance = new
puts "my_instance = #{my_instance}"
# We can! Now that's very interesting. Can we invoke the
# instance method on that instance?
my_instance.my_method
# Yes!
end
The following is printed while the class is being defined:
self = MyClass
class methods: my_method
instance methods: my_method
Me, I'm a class method. Note that self = MyClass
my_instance = #<MyClass:0x007fd6119125a0>
Me, I'm an instance method. Note that self = #<MyClass:0x007fd6119125a0>
Now let's confirm the methods can be invoked from outside the class. There should be no surprises here:
MyClass.my_method
#-> Me, I'm a class method. Note that self = MyClass
my_instance = MyClass.new
my_instance.my_method
#-> Me, I'm an instance method. Note that self = #<MyClass:0x007fd61181d668>

Issue with ruby class method definitions

The following code returns an error:
class ABC
def self.method1()
method2
end
def method2
end
end
ABC.method1
NameError: undefined local variable or method `method2' for ABC:Class
However, the code below works fine:
class ABC
def initialize
method2
end
def method2
end
end
ABC.new
Does initialize need to be used in order to properly define all methods within the class? What is wrong with the first code block?
You cannot call an instance method without having an object of that class.
method1 is a class method of class ABC, so you can call it on the class itself. But if you want to call your instance method method2, you need an object of class ABC rather than calling it on the class itself, ie.
o = ABC.new
o.method2
The other code works, because in initialize, you already have you instance of ABC, your method call can be understood as self.method2.
method1 is static, method2 not.
ABC.method2 is undefined, ABC.new.method2 is ok
class ABC
def self.method1()
method2 # ABC.method2
end
def initialize
method2 # self.method2, self is instance of ABC
end
def method2
end
end
In first code block you try to call instance method in class method, it is like you call ABC.method2, but ABC does not have such method.

Why can't I call include from a class method in ruby?

You can call include to mixin a module with a class in ruby, but it must be done at the beginning of the class definition. Why can't it be done inside a class function? Is there an alternate syntax?
EX:
module UsefulThings
def a() puts "a" end
end
class IncludeTester
include UsefulThings
def initialize
end
end
n = IncludeTester.new
n.a()
^^ This works, but if I change IncludeTester to the following, I get the error "undefined method `include'"
class IncludeTester
def initialize
include UsefulThings
end
end
It can be done in a class method.
This works:
module UsefulThings
def a
puts "a"
end
end
class IncludeTester
def self.mix_in_useful_things
include UsefulThings
end
end
x = IncludeTester.new
IncludeTester.mix_in_useful_things
x.a # => a
But "initialize" is not a class method, it's an instance method.
"new" is a class method. You can think of new as allocating a new object and then calling initialize on it, passing initialize whatever arguments were passed to new.
You can't call include directly in initialize because include is a private method of Class (inherited from Module), not of the newly created IncludeTester instance.
If you want to include a module into a class from an instance method, you have to do something like this:
class IncludeTester
def initialize
self.class.send(:include, UsefulThings)
end
end
It's necessary to use "send" here because include is private method, which means it can only be directly invoked with an implicit receiver (of self).
When you call initialize normally in a class definition, you're actually calling it with an implicit receiver of "self", referring to the class being defined.
This is what is actually happening when you do this:
class IncludeTester
include UsefulThings
end
include is a method from Module, Module is the superclass of Class and so include is a method on Class and that makes it a class method in your IncludeTester. When you do this:
class IncludeTester
def initialize
include UsefulThings
end
end
you're trying to call a class method inside an instance method and Ruby says
`initialize': undefined method `include'
because there is no instance method called include. If you want to call a class method inside an instance method (such as initialize), you'd do this:
def initialize
self.class.include UsefulThings
end
But that won't work because include is a private method; you can get around that with class_eval though:
def initialize
self.class.class_eval {
include UsefulThings
}
end
You would be doing include UsefulThings every single time you instantiated an IncludeTester, aside from not making much sense, it could cause problems if UsefulThings had an included method.
It's actually fully possible to include a module from a class method, like so:
module Stuff
def say_hello
puts "hello"
end
end
class Foo
def self.i_am_a_class_method
include Stuff
end
def i_am_an_instance_method
end
end
You cannot however do that from an instance method, because the include method is only available as a private class method, and therefore not accessible from a Foo.new instance.
You want the extend method:
class IncludeTester
def initialize
extend UsefulThings
end
end
This need not be done within the a method either:
IncludeTester.new.tap { |newTester| newTester.extend(UsefulThings) }

When an instance method calls a class method, I have to use self.class_method?

I'm getting an error so I guess I have to reference a class method from inside of an instance method with self.class_method_name, but why is that?
Shouldn't it resolve this by itself? Confused.
def self.blah(string)
..
end
def some_method()
some_thing = blah("hello")
end
If you have
# This won't work
class Foo
def self.blah(string)
puts "self.blah called with a string of #{string}"
end
def some_method
# This won't work
self.blah("hello")
end
end
foo = Foo.new
foo.some_method
It won't work, because it'll look for the instance method Foo#blah. Instead, you're looking for Foo.bar.
To make some_method call Foo.bar, you have to make some_method refer to the Foo class, and then call blah on it.
class Foo
def self.blah(string)
puts "self.blah called with a string of #{string}"
end
def some_method
# This will work
self.class.blah("hello")
end
end
foo = Foo.new
foo.some_method
The reason you have def self.blah to define the method, but self.class.blah to call the method, is that in the former, self refers to the Foo class, while in the latter, self refers to the foo object, so you need self.class to refer to the Foo class.
It may be easier to think of self as part of the method name, that way it's clear that you never defined a blah method, you defined only a self.blah method. (To clarify: the previous sentence shouldn't be thought of too much, so please don't read into it, as it's not how things are actually working, just a sort of "layman's terms" attempt at describing why it doesn't work.)
Also, what if you had defined a blah instance method in addition to the class method? If calling blah was enough to access the class method, how would you call the instance method?
Finally, there really isn't any such thing as a class method in Ruby, "class methods" are really methods of the singleton class.

Resources