Ruby class methods. Is it getting called on main object? - ruby

I have a class that looks like this:
class Test
def self.hello
puts "I say hello"
end
def goodbye
puts 'goodbye'
end
hello
goodbye
end
This is my output:
ruby class_methods.rb
I say hello
class_methods.rb:11:in `<class:Test>': undefined local variable or method `goodbye' for Test:Class (NameError)
from class_methods.rb:1:in `<main>'
What is hello being called on? It looks like it does indeed get called but what is it being called on? I think it's being called on the main object... but why does that make sense? I thought class methods can only be called on the class object itself not a main object?

What is hello being called on?
Whenever there isn't an explicit receiver, the receiver is self. So the call is implicitly:
self.hello
... where self is the class Test, on which you have just defined the self.hello method.
goodbye is not found because it is defined on an instance of class Test, not the class itself, where it's currently called.

def self.hello is a class method
def self.hello
puts "I say hello"
end
hello or Test.hello
#I say hello
def goodbye is a instance method of class of Test
def goodbye
puts 'goodbye'
end
Test.new.goodbye #instance of Test
#goodbye

self defines class methods whereas defining just a method without self will mean that the method is an instance method, therefore, calling goodbye will raise an error because there is no receiver, an instance of the Test class is needed to send that message or call that method.
When ruby parses the class and evaluates the code it can call hello because there is no need to instantiate an object.

You are defining hello as class method whereas goodbye is an instance method.
class Test
def self.hello
puts "I say hello"
end
...
end
This class method since you are calling hello on self, which is Test class, so you can call hello by Test.hello.
...
def goodbye
puts 'goodbye'
end
To call goodbye method, since this is an instance method, you need to create an instance of Test by using Test.new method. Therefore, you need to use Test.new.goodbye to call goodbye method.

Related

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

Call a function and method with the same name in Ruby

Forgive me for my english.
I am a php programmer and now i want to learn Ruby.
In php if you want to call function "foo" within a class, you simply call foo(), and if you want to call method "foo" you call this->foo().
The question is, is it possible to call function and method with the same name in Ruby?
For example:
def foo
puts "In foo function"
end
class A
def call_foo
foo
#How can i call foo function, not a method?
end
def foo
puts "In foo method"
end
end
a = A.new
a.call_foo #Prints "In foo method"
There is no such thing as a function in Ruby, only methods.
If you define a method at the top-level it is an instance method of Object.
If you define a class without a superclass, it's superclass is Object.
So, your A#foo simply overrides Object#foo. And if it overrides Object#foo, it should respect its contract. You should never need to call Object#foo on an A, if A#foo implements Object#foo's contract correctly (and it should, otherwise it would be a violation of the Liskov Substitution Principle). If you want to reuse Object#foo's implementation within A#foo, you can defer to the superclass implementation using super.
Note: what you want is possible using reflection, but the correct solution would be to fix your design:
def foo
puts "In foo function"
end
class A
def call_foo
self.class.superclass.public_instance_method(:foo).bind(self).()
end
def foo
puts "In foo method"
end
end
a = A.new
a.call_foo #Prints "In foo function"
The foo method outside of your class definition is bound to Object, which is an instance of Class. So to call your method you can do:
> Object.foo­
=> "In foo function"
But as it was pointed out before, you should rather declare this method in an appropiate class.
You can also declare this method as class method with:
class A
def self.foo
puts "In foo class method"
end
end
Now you can call it without creating an A instance:
> A.foo
=> puts "In foo class method"

execute a method in a ruby file

I have this dummy ruby class file (Bar.rb):
class Bar
foo() # execute foo()
def foo()
puts "Hello world, "
end
end
And I ran the file with:
$ ruby Bar.rb
I was expecting to see "Hello, world" in the command, but got this error:
undefined local variable or method `foo' for Bar:Class (NameError)
from bar.rb:3:in `<main>'
So how do I execute a method? Does Ruby have any main method (as in Java or C/C++)?
A couple of reasons this doesn't work as you have written it.
The method foo hasn't been declared before you attempt to call it.
The method foo, as you have declared it, is an instance method. You're not invoking it on an instance of the class.
This would work:
class Bar
def self.foo
end
foo
end
As others have said, though, you probably don't need to wrap this in a class.
You don't need any class, you can call any methods you like right from the file itself:
bar.rb:
puts "Hello, world!"
If you want to stick with your code, you are calling foo before its declaration, which obviously doesn't work.
First you define the method, then you call it.
No need for a main procedure, the first code outside a class or method will be executed first. The script itself is the main. I'm sure there are better definitions for this, but i'm sure you will underdstand.
def foo()
puts "Hello world, "
end
foo() # execute
Also no need to put this in a class, then you would have to initiate her first.
class Bar
def foo()
puts "Hello world, "
end
end
bar = Bar.new
bar.foo

Generic methods/Default methods in Ruby, when some method is not defined

I want to do something but I'm not sure if it is possible. I want to use "generic methods" or "default methods" in case when some method is called but is not defined. This is a simple example so you can understand my point:
This is the class:
class XYZ
def a
#...
end
def b
#...
end
end
The instance of the class XYZ:
n = XYZ.new
n.a
n.b
n.c
As you can see, I'm calling the method "c" which is not defined and it will throw an error. Can I do something in the class XYZ so when someone calls a method not defined get the name of the method and do something, in base of the name of the method? And, is this possible in another languages (not making a compiler)? If this is possible, how is it called (theory speaking)?
Use method_missing:
class XYZ
def a; end
def b; end
def method_missing(name, *args)
"Called #{name} with args: #{args}"
end
end
XYZ.new.c #=> "Called c"
You should also define respond_to_missing? to get respond_to? to work nicer in 1.9.2+. You should read more about respond_to?/respond_to_missing? when using method_missing.
This, by the way, would be considered to be metaprogramming. This isn't typically possible in compiled languages because of the way they call functions.
its called method_missing.
When you call a method that is not defined on object, ruby redirects the call to method_missing method which raises the error for you.
you can do this:
class XYZ
def method_missing(method, *args, &blck)
puts "called #{method} with arguments #{args.join(',')}"
end
end
now instead of error you will get output to your console.

Why does this Ruby method need to be a class level method?

Here's a classic fizzbuzz in Ruby:
class PrimeChecker
def print_em
1.upto 100 do |fizzbuzz|
if (fizzbuzz % 2) == 0 && (fizzbuzz % 5) == 0
puts "fizzbuzz: " + fizzbuzz.to_s
elsif (fizzbuzz % 5) == 0
puts "fizz: "+fizzbuzz.to_s
elsif (fizzbuzz % 2) == 0
puts 'buzz: ' + fizzbuzz.to_s
else
puts "-" + fizzbuzz.to_s
end
end
end
end
PrimeChecker.print_em
When I execute this, I get this error:
undefined method 'print_em'.
I change the method to self.print_em and it works. Does this mean it's a class method (I think so)? Was the method "not found" before because I can only call such methods in a class on actual instances of the object? If I wanted it to be a instance method what is the syntax for that? I'm trying to understand Ruby, classes and methods better.
Class methods are just that: called on the class. Whereas instance methods are called on an instance of that class. An example is more useful:
class Foo
def self.bar
"This is a class method!"
end
def bar
"This is an instance method!"
end
end
Foo.bar # => "This is a class method!"
foo = Foo.new # This creates "foo" to be a new instance of Foo
foo.bar # => "This is an instance method!"
Note that "class methods" in Ruby are actually methods on the class object's singleton. This is a rather difficult concept to explain, and you can read more about it if you'd like.
It's not a class method as written; you need to run it with an instance of PrimeChecker:
pc = PrimeChecker.new
pc.print_em
Using self. turns it into a class method, runnable with the syntax you show.
It doesn't need to be a class method, it's just that that's how you're trying to execute it.
Q: When I run ruby.rb I get undefined method 'print_em'. I change the method to self.print_em and it works. Does this mean it's a class method (I think so).
A: Yes. class Bar; ... def self.foo defines a class method foo for class Bar.
Q: Was the method "not found" before because I can only call such methods in a class on actual instances of the object?
A: You were first defining it as an instance method. In that case, it is only available to instances of the class.
Q: If I wanted it to be a instance method what is the syntax for that?
A: The way you had it originally: class Bar; def foo defines instance method foo for class Bar
Yes, you are completely correct. Currently, the way you define it, you can evaluate the method with:
PrimeChecker.new.print_em
The reason def self.my_awesome_method defines it on the class side is because the stuff inside
class MyAwesomeClass
end
is being executed in the context of MyAwesomeClass. It's all Ruby code, as you can see! This enables you to do things like this:
class MyAwesomeClass
puts "Hello from innards of #{self}!" #=> Hello from the innards of MyAwesomeClass!
end
Method definitions will also only work if you call them after the definition location, for example:
class MyAwesomeClass
my_awesome_method # produces a nasty error
def self.my_awesome_method
puts "Hello world"
end
my_awesome_method # executes just fine
end
Hope this clears some things up.

Resources