Accessing methods in class body - ruby

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.

Related

How to output the Ruby class name wrapped around Singleton method

My goal is:
class Bermuda
class << self
def grass
puts self.superclass.name
end
end
end
# my goal is that this expression
Bermuda.grass
# will output a string of the class name it resides in
=> "Bermuda"
I cannot display the name of the class that holds a singleton method. I have tried a number of different stabs and standard library searches but haven't found an answer.
This is partially pointless because you would not need a class method to display the information that you would need in the first place to call that method. I'm curious if it's possible or I'm completely missing the scope.
Just call name on self
class Bermuda
class << self
def grass
puts self.name
end
end
end
This sort of an implementation isn't recommended since all you have to do to get the class name is call Bermuda.name
Please see the answer given below by #MatthewCliatt for more info.
It's as simple as:
self.class.name
But, the catch is that this isn't for class methods, it's for instance methods.
That means you don't declare it with self. This was your code:
class Bermuda
class << self
def grass
puts self.superclass.name
end
end
end
And that will make the grass method a class method. You would have to call it like Bermuda.grass.
But, if you can call class methods like the one above, you could just as easily write: Bermuda.name.
I'm assuming you can't call class methods, probably because you're working with an instance. So you want to write this method as such:
class Bermuda
def grass
puts self.class.name
end
end
You say you're creating a singleton method, but I don't think your method is a singleton method in the usual sense (i.e. a method on an object that is an instance of a class, but not itself a class).
I believe the class << self notation you use merely results in a class method being defined, identical to:
def self.grass
puts name
end
In irb:
2.3.0 :003 > class Bermuda; def self.grass; puts name; end; end
=> :grass
2.3.0 :004 > Bermuda.grass
Bermuda

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>

Ruby: Calling instance method from class method

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.

Ruby: What is the difference between writing extend outside the class and inside the class

I have read this question at url In Ruby or Rails, why is "include" sometimes inside the class and sometimes outside the class?. As per the answer of this question i am expecting the method print2 of module Calculation should be accessible to the class Addition and ABc .but i am getting error as undefined methodprint2' for Addition:Class (NoMethodError)`
module Calculation
def print2
puts "print2"
end
end
require '/home/sanjay/Desktop/m'
extend Calculation;
class Addition
end
class Abc
end
Addition.print2
Abc.print2
Please explain why this error is comming??
Thanks
The receiver of extend should be each class, so you want Addition.extend Calculation and Abc.extend Calculation (after the class definitions). The semicolon is not required.
module D
def dog
puts "woof"
end
end
class A; end
A.extend D
A.dog #=> "woof"
class B; end
B.extend D
B.dog #=> "woof"
My hunch is that your extend (outside any class) extends the Eigenclass of main. Main being the instance of Object that is your current runtime environment. An Eigenclass is the class of an object whose only instance is the given object. Each object in Ruby has a Eigenclass. Nothing inherits from an Eigenclass. So your extend is not inherited to any other classes.

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) }

Resources