ArgumentError: wrong number of arguments in Ruby - ruby

Trying to solve this problem,
class Person
def initialize(name)
#name=name
end
def greet(other_name)
puts "Hi #{other_name}, my name is #{name}"
end
end
initialize("ak")
greet("aks")
but I am getting the error like:
ArgumentError: wrong number of arguments calling `initialize` (1 for 0)
I don't understand what is asking here, if its just the argument then why the error is like (1 for 0). can someone help me understand this problem.

Look at this code:
class Person
attr_reader :name
def initialize( name )
puts "Initializing Person instance #{object_id}"
#name = name
end
def greet( name )
puts "Hi #{name}, I'm #{name()}"
end
end
When you wrote initialize without explicit receiver:
initialize( "ak" )
It was only a matter of luck that your message was recognized. Look who has responded:
method( :initialize ).owner
#=> BasicObject
BasicObject, the foremother of all Object instances, herself responded to your call, by scolding you about wrong number of arguments, because:
method( :initialize ).arity
#=> 0
Not only this method does not expect any arguments, but also you are not expected to call it at all. In fact, you are not expected to call #initialize on any object by yourself, save for exceptional situations. Class#new method handles calling of Person#initialize method for you:
A = Person.new( 'Abhinay' )
Initializing Person instance -605867998
#=> #<Person:0xb7c66044 #name="Abhinay">
Person.new handled creation of a new instance and automatically called its #initialize method. Also, #initialize method is created private, even if you did not specify it explitcitly. The technical term for such irregular behavior is magic. Person#initialize is magically private:
A.initialize( 'Fred' )
NoMethodError: private method `initialize' called for #<Person:0xb7c66044 #name="Abhinay">
You cannot just reinitialize yourself to 'Fred', you know. All other methods are public unless prescribed otherwise:
A.greet "Arup"
Hi Arup, I'm Abhinay
#=> nil

You need to call the methods on the object (not just call the methods) and initialize is automatically called when creating a new object:
p = Person.new("ak")
p.greet("aks") #=> "Hi aks, my name is ak"

The problem is that to create new object you need to call method new on class, and not initialize on the object.
So code looks like this:
p = Person.new("John")

Please, take a look at code below:
class Person
attr_reader :name
def initialize(name)
#name = name
end
def greet(other_name)
puts "Hi #{other_name}, my name is #{name}"
end
end
person = Person.new("ak")
person.greet("aks")
#=> Hi aks, my name is ak

Related

What does `&method(:method_ name)` mean in ruby?

I was trying to create a class that has a private class method. I want this private class method available to be used inside an instance method.
The following was my first attempt:
class Animal
class << self
def public_class_greeter(name)
private_class_greeter(name)
end
private
def private_class_greeter(name)
puts "#{name} greets private class method"
end
end
def public_instance_greeter(name)
self.class.private_class_greeter(name)
end
end
Animal.public_class_greeter('John') works fine, printing John greets private class method.
However, Animal.new.public_instance_greeter("John") throws an error: NoMethodError: private method 'private_class_greeter' called for Animal:Class.
That is expected, as the invocation self.class.private_class_greeter is same as Animal.private_class_greeter, which obviously throws an error.
After searching on how this can be fixed, I came up with the following code, that does the job:
class Animal
class << self
def public_class_greeter(name)
private_class_greeter(name)
end
private
def private_class_greeter(name)
puts "#{name} greets private class method"
end
end
define_method :public_instance_greeter, &method(:private_class_greeter)
end
I don't exactly understand what is happening here: &method(:private_class_greeter).
Could you please explain what does this mean?
If I were to replace:
define_method :public_instance_greeter, &method(:private_class_greeter)
with:
def public_instance_greeter
XYZ
end
then, what should be the content in place of XYZ?
How does Ruby parse &method(:private_class_greeter)?
The expression &method(:private_class_greeter) is
the value of the method call method(:private_class_greeter)
prefixed with the & operator.
What does the method method do?
The method method looks up the specified method name in the current context and returns a Method object that represents it. Example in irb:
def foo
"bar"
end
my_method = method(:foo)
#=> #<Method: Object#foo>
Once you have this method, you can do various things with it:
my_method.call
#=> "bar"
my_method.source_location # gives you the file and line the method was defined on
#=> ["(irb)", 5]
# etc.
What is the & operator for?
The & operator is used to pass a Proc as a block to a method that expects a block to be passed to it. It also implicitly calls the to_proc method on the value you pass in, in order to convert values that are not Proc into a Proc.
The Method class implements to_proc — it returns the contents of the method as a Proc. Therefore, you can prefix a Method instance with & and pass it as a block to another method:
def call_block
yield
end
call_block &my_method # same as `call_block &my_method.to_proc`
#=> "bar"
The define_method method just happens to take a block with the contents of the new method that is being defined. In your example, &method(:private_class_greeter) passes in the existing private_class_greeter method as a block.
Is this how &:symbol works?
Yes. Symbol implements to_proc so that you can simplify your code like this:
["foo", "bar"].map(&:upcase)
#=> ["FOO", "BAR"]
# this is equivalent to:
["foo", "bar"].map { |item| item.upcase }
# because
:upcase.to_proc
# returns this proc:
Proc { |val| val.send(:upcase) }
How can I replicate &method(:private_class_greeter)?
You can pass in a block that calls the target method:
define_method :public_instance_greeter do |name|
self.class.send(:private_class_greeter, name)
end
Of course, then you don't need to use define_method anymore, which results in the same solution Eric mentioned in his answer:
def public_instance_greeter(name)
self.class.send(:private_class_greeter, name)
end
First, take good care with your indentation. private should be 2 spaces to the right: it gives the impression that public_instance_greeter is private otherwise.
If you don't care about encapsulation, you could simply use Kernel#send:
class Animal
class << self
def public_class_greeter(name)
private_class_greeter(name)
end
private
def private_class_greeter(name)
puts "#{name} greets private class method"
end
end
def public_instance_greeter(name)
self.class.send(:private_class_greeter, name)
end
end
Animal.public_class_greeter('John')
# John greets private class method
Animal.new.public_instance_greeter("John")
# John greets private class method

Ruby class printing name in another class

class NameData
def initialize
#name="Cleetus"
end
class Greetings
def hello()
puts "Hello #{#name}! How wonderful to see you today."
end
end
end
greet=Greetings.new
p greet.hello
Im a little bit of a beginner, but i'm trying to get my name to be used in the Greetings class so the string will print with my name inside. Any ideas?
It's common to put multiple classes inside the same module (sometimes called "namespacing") in Ruby, but it's unusual to have a class inside another class. Even if you do it's still a separate class and does not have access to instance variables in the "outer" class.
What you need to do is to make your #name value accessible outside your NameData object. The usual way to do this in Ruby is with an attribute reader:
class NameData
attr_reader :name
def initialize
#name = "Cleetus"
end
end
name_data = NameData.new
puts name_data.name
# => Cleetus
Once you've done that you need to tell your Greetings object about the NameData object, and how to use it. One way to do that is to pass the NameData object as an argument to hello:
class Greetings
def hello(name_data)
puts "Hello #{name_data.name}! How wonderful to see you today."
end
end
greet = Greetings.new
greet.hello(name_data)
# => Hello Cleetus! How wonderful to see you today.
Another way is to pass it to the Greetings constructor and save it in an instance variable:
class Greetings
def initialize(name_data)
#name_data = name_data
end
def hello
puts "Hello #{#name_data.name}! How wonderful to see you today."
end
end
greet = Greetings.new(name_data)
greet.hello
# => Hello Cleetus! How wonderful to see you today.
The #name variable is an instance variable and it can not be seen by methods within a different class. It can only be seen by methods within the same class. Try this instead:
class Greetings
def initialize
#name="Cleetus"
end
def hello()
puts "Hello #{#name}! How wonderful to see you today."
end
end
greet = Greetings.new.hello

creating method on fly as a parameter while calling singleton method

How's it possible in ruby ?
class Test
# Creating singleton method
def self.some_singleton_method(param1)
puts param1
end
end
# calling singleton method by creating method on fly as a parameter to it
Test.some_singleton_method def method_name(some_param)
# do something
end
## method_name
I've tried many places looking around, can't come up with an idea how's it's working.
Thanks!
It is possible, since def is keyword, that creates new method in current scope, which is Object since you're calling it on the "top" level, i.e. not inside any class. Starting from Ruby 2.1, def returns method name as a symbol, so your code is actually equivalent to
name = def method_name(some_param)
// do something
end
Test.some_singleton_method(name) # outputs "method_name"
EDIT: Thanks to Cary Swoveland for clarification that def is actually a keyword and not a method.
Here are two ways to do that.
#1
class Test
def self.doit(m)
send(m) yield
end
end
Test.doit(:hello) do
puts 'hi'
end
#=> :hello
Test.new.hello
#=> "hi"`.
#2
class Test
def self.doit(str)
eval(str)
end
end
Test.doit "def hello; puts 'hi'; end"
#=> :hello
Test.new.hello
#=> "hi"`.

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

Accessing the name of an anonymous class in superclass' self.inherited

I would like to access a class' name in its superclass MySuperclass' self.inherited method. It works fine for concrete classes as defined by class Foo < MySuperclass; end but it fails when using anonymous classes. I tend to avoid creating (class-)constants in tests; I would like it to work with anonymous classes.
Given the following code:
class MySuperclass
def self.inherited(subclass)
super
# work with subclass' name
end
end
klass = Class.new(MySuperclass) do
def self.name
'FooBar'
end
end
klass#name will still be nil when MySuperclass.inherited is called as that will be before Class.new yields to its block and defines its methods.
I understand a class gets its name when it's assigned to a constant, but is there a way to set Class#name "early" without creating a constant?
I prepared a more verbose code example with failing tests to illustrate what's expected.
Probably #yield has taken place after the ::inherited is called, I saw the similar behaviour with class definition. However, you can avoid it by using ::klass singleton method instead of ::inherited callback.
def self.klass
#klass ||= (self.name || self.to_s).gsub(/Builder\z/, '')
end
I am trying to understand the benefit of being able to refer to an anonymous class by a name you have assigned to it after it has been created. I thought I might be able to move the conversation along by providing some code that you could look at and then tell us what you'd like to do differently:
class MySuperclass
def self.inherited(subclass)
# Create a class method for the subclass
subclass.instance_eval do
def sub_class() puts "sub_class here" end
end
# Create an instance method for the subclass
subclass.class_eval do
def sub_instance() puts "sub_instance here" end
end
end
end
klass = Class.new(MySuperclass) do
def self.name=(name)
#name = Object.const_set(name, self)
end
def self.name
#name
end
end
klass.sub_class #=> "sub_class here"
klass.new.sub_instance #=> "sub_instance here"
klass.name = 'Fido' #=> "Fido"
kn = klass.name #=> Fido
kn.sub_class #=> "sub_class here"
kn.new.sub_instance #=> "sub_instance here"
klass.name = 'Woof' #=> "Woof"
kn = klass.name #=> Fido (cannot change)
There is no way in pure Ruby to set a class name without assigning it to a constant.
If you're using MRI and want to write yourself a very small C extension, it would look something like this:
VALUE
force_class_name (VALUE klass, VALUE symbol_name)
{
rb_name_class(klass, SYM2ID(symbol_name));
return klass;
}
void
Init_my_extension ()
{
rb_define_method(rb_cClass, "force_class_name", force_class_name, 1);
}
This is a very heavy approach to the problem. Even if it works it won't be guaranteed to work across various versions of ruby, since it relies on the non-API C function rb_name_class. I'm also not sure what the behavior will be once Ruby gets around to running its own class-naming hooks afterward.
The code snippet for your use case would look like this:
require 'my_extension'
class MySuperclass
def self.inherited(subclass)
super
subclass.force_class_name(:FooBar)
# work with subclass' name
end
end

Resources