Issue with ruby class method definitions - ruby

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.

Related

Why private class methods are not allowed in ruby classes?

I came across this error while in development.
class ABC
def self.method_1
method_2
p "method_1"
end
private
def method_2
p "method_2"
end
end
ABC.method_1 # `method_1': undefined local variable or method `method_2' for ABC:Class (NameError)
But if I do it like this, then it works
def self.method_1
method_2
p "method_1"
end
def self.method_2
p "method_2"
end
private_class_method :method_2
end
ABC.method_1
Please help me in understanding this.
In Ruby, the default implicit receiver of a message send, if you do not explicitly specify a receiver, is self.
Also, the default definee of a method definition expression, if you do not explicitly define a definee, is the closest lexically enclosing module definition.
In your first example, method1 is defined with an explicit definee of self, which at that point in the execution is the class ABC itself. This means that method1 is a class method of class ABC, which is actually just a singleton method of ABC, which in turn is just an instance method of ABC's singleton class.
method2 is defined without an explicit definee, which means it will be defined on the default definee. The closest lexically enclosing class or module definition in this case is the class definition of ABC, so method2 is defined as an instance method of ABC.
In other words: method1 and method2 are defined in two completely different classes.
Now, when you call ABC.method1, you are sending the message method1 to the explicit receiver ABC. This works, because method1 is found in ABC's singleton class.
Inside of method1, you are calling method2 without an explicit receiver. That means, the message is sent to the implicit receiver self, which is ABC in this case. So, it is roughly equivalent (module access restrictions) to ABC.method2.
BUT there is no definition of method2 in either the singleton class of ABC er the class of ABC (which is Class) or any of its superclasses (Module, Object, Kernel, BasicObject) because method2 is defined in ABC itself.
Therefore, method2 cannot be found in the method lookup chain.
In the second example, both methods are defined in the same class, namely both methods are defined in ABC's singleton class. Therefore, method2 is in the method lookup chain when called inside of method1.
In fact, your first example is actually not substantially different from this:
class Foo
def method 1
method2
end
end
class Bar
def method2; end
end
foo = Foo.new
foo.method1
#Jorg's answer is correct, but this might be easier to read. As Jorg pointed out, the use of the "self" applies to the class object, not the specific instance of it. basically mixing and matching the "self" and not-"self" entries is hard work, and you shouldn't try to cross the streams. the "self" and non-"self" objects will act as completely different classes.
class
class ABC
def method_1
method_2
p "method_1"
end
def self.method_3
method_4
p "method_3"
end
private
def method_2
p "method_2"
end
def self.method_4
p "method_4"
end
end
output
> ABC.new.method_1
"method_2"
"method_1"
=> "method_1"
> ABC.method_3
"method_4"
"method_3"
=> "method_3"

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.

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.

in ruby, is it possible to redefine the execution context of an added method?

For example
class Foo
def bar
end
end
In that code, bar would only be available within any instance of the class.
Is it possible to change the execution context of the method to the Eigenclass
without changing how the method itself is defined so that the method is now available as a singleton without ever needing to call self.new?
Preferably I would like to do it any of the code that doe this to code that is added in via a class that Foo could inherit from.
At the moment what I'm doing amounts to:
class Test
def method_added method
self.define_singleton_method method do
self.new.send method
end
end
end
and for what I need this doesn't work as I'm changing the execution context by calling new.
You can simply do:
class Test
def self.method_added method
module_function method
end
end
class A < Test
def foo
:hello
end
end
A.foo #=> :hello
I worked out how to do it just now >_<.
Here's the code:
class Test
def method_added method
m = self.new.method(method) #get method object
self.define_singleton_method(method) do #create method with same name within the singleton class
m.call #call the block which will now run the code of the added method within the context of the Eigenclass/Singleton
end
end
end
So what it does is it grabs a method object form an instance and then calls that method as a block within the context of the class.
So the first code example becomes:
class Foo < Test
def bar
end
end
and the method bar can now be accessed as
Foo.bar rather then Foo.new.bar which means no instance creation; besides the time it does within method added, but that's fine as it's the only way to get the method object as far as I'm aware.
Which is why it's probably best to create an instance only the once when the class is inherited (within def self.inherited), store it within the class and then just access that instead of calling self.new.

Ruby: NoMethodError, but why?

I was working on a simple Pi Generator while learning Ruby, but I kept getting NoMethodError on RubyMine 6.3.3, so I decided to make a new project and new class with as simple as possible, and I STILL get NoMethodError. Any reason?
class Methods
def hello (player)
print "Hello, " << player
end
hello ("Annie")
end
And the error I get is:
C:/Users/Annie the Eagle/Documents/Coding/Ruby/Learning Environment/methods.rb:5:in `<class:Methods>': undefined method `hello' for Methods:Class (NoMethodError)
You have defined an instance method and are trying to call it as a method of a class. Thus you need to make the method hello a class method, not an instance method of the class Methods.
class Methods
def self.hello(player)
print "Hello, " << player
end
hello("Annie")
end
Or, if you want to define it as instance method then call it as below :
class Methods
def hello(player)
print "Hello, " << player
end
end
Methods.new.hello("Annie")
You're trying to call an instance method as a class method.
Here's some code that illustrates the difference between the two in ruby:
class Person
# This is a class method - note it's prefixed by self
# (which in this context refers to the Person class)
def self.species
puts 'Human'
# Note: species is OK as a class method because it's the same
# for all instances of the person class - ie, 'Bob', 'Mary',
# 'Peggy-Sue', and whoever else, are ALL Human.
end
# The methods below aren't prefixed with self., and are
# therefore instance methods
# This is the construct, called automatically when
# a new object is created
def initialize(name)
# #name is an instance variable
#name = name
end
def say_hello
puts "Hello from #{#name}"
end
end
And now try it out, calling the methods...
# Call a class method...
# We're not referring to any one 'instance' of Person,
Person.species #=> 'Human'
# Create an instance
bob = Person.new('Bob')
# Call a method on the 'Bob' instance
bob.say_hello #=> 'Hello from Bob'
# Call a method on the Person class, going through the bob instance
bob.class.species #=> 'Human'
# Try to call the class method directly on the instance
bob.species #=> NoMethodError
# Try to call the instance method on the class
# (this is the error you are getting)
Person.say_hello #=> NoMethodError
You've created an instance method, but you're calling a class method. In order to call hello("Annie"), you have to make an instance of Methods. For instance:
class Methods
def self.hello(player)
print "Hello, " << player
end
end
my_method = Methods.new
my_method.hello("Annie")
This would output Hello, Annie
By defining a method with def method_name args you are defining a instance method that will be included in every object of that class, but not in the class itself.
On the other hand, by def self.method_name args you will get a class method that will be directly in the class, without the need of instanciate an object from it.
So If you have this:
Class Test
def self.bar
end
def foo
end
end
You can execute the instance method this way:
a = Test.new
a.foo
And as for the class one should be:
Test.foo

Resources