Ruby class printing name in another class - ruby

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

Related

Accessing an instance variable within the `initialize` method

Given this basic class in Ruby:
class TestClass
def initialize(name)
#name = name
end
end
How do I then access the instance variable name directly from within the initialize method without creating a getter function? is this even possible? (i.e. using dot notation) or does the initialize method cease to exist once a class is instantiated, hence the need to define a getter method?
I think what I'm trying to ask is initialize a class or instance method?
The "getter method" is defined so that you can use the variable from outside the class:
class TestClass
attr_reader :name
def initialize(name)
#name = name
end
end
# TestClass.new('Ben').name # => 'Ben'
If you don't need to access it from outside the class, you can just use #name:
class TestClass
def initialize(name)
#name = name
end
def greet
puts "Hello, %s" % #name
end
end
# TestClass.new('Ben').greet # outputs: Hello, Ben
You can use the #name inside initialize:
class TestClass
def initialize(name)
#name = name
puts "Name backwards: %s" % #name.reverse
end
end
# TestClass.new('Ben') # outputs neB
Initialize is a special method, when you define initialize instance method, it is automatically marked private. The class method new calls it after creating an instance of your class.
Nothing stops you from calling private methods from inside the class:
class TestClass
def initialize(name)
#name = name
puts "Name is now %s" % #name
end
def flip_name
initialize(#name.reverse)
end
end
# t = TestClass.new('Ben') # outputs "Name is now Ben"
# t.flip_name # outputs "Name is now neB"
# t.instance_variable_get(:#name) # => 'neB'
The flip_name method that calls initialize works just fine, but of course this is quite unconventional and almost never used, because it does not make much sense.
It's possible to call private methods from outside the class using send:
# t.send(:initialize, 'Bill') # outputs "Name is now Bill"
# t.instance_variable_get(:#name) # => 'Bill'
Without send, you get NoMethodError:
> t.initialize('Jack')
NoMethodError: private method `initialize' called for #<TestClass:0x00007fa2df8e4570>
Ruby 1.9 beta releases changed send to act like public_send does today, allowing access to only public methods and there was going to be funccall for calling private methods if you really want to, for unit testing purposes for example. I think it caused too much compatibility issues and the change was reverted.
So in conclusion, yes, you can call initialize again and it does not cease to exist, but it is almost never done because it makes very little sense. To access instance variables from inside the class, you use # notation like #name, to access them from outside of the class, you define a getter.
Yes, there is a way. But it's not a traditional one. It's more like querying the object to know
TestClass.new("foo").instance_variable_get(:#name)
=> "foo"
The initialize method does not "cease to exsist". It's executed, that's it. What your method do, in your case, is that the variable is set.

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

ArgumentError: wrong number of arguments in 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

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

Are all methods public by default?

If I have this in my main program:
def hi
puts 'hi'
end
self.hi
it won't work because hi is private.
I've learned that all methods in Ruby are public by default, but this doesn't seem to be the case, why?
It is tricky to define a method in the main environment. A method is private by default when you define it in main. So you either have to use it as private:
def hi
puts 'hi'
end
hi
or explicitly make it public:
def hi
puts 'hi'
end
public :hi
self.hi
Methods are public by default, except for "top-level procedures". If you define something which looks like a top-level procedure, it will actually be defined as a private instance method of Object.
def main_method
p "This is from main_method"
end
public :main_method
class Klass
def initialize
Object.main_method # Or we can simply say main_method as we do in the second exaple
end
end
puts Klass.new
It is the way to create Object 'class methods' as shown. If it were private, then we could not specify the receiver.
When it is private, as it is by default, then we would call it like this:
def main_method
p "This is from main_method"
end
class Klass
def initialize
main_method # With it being private, implicit self only
end
end
puts Klass.new
So is there any advantage to either way? I guess if you make it private, you send some kind of communication to the developer that you have a preference and this should be used as a private method, rather than a public method.
In practice, I don't see an advantage.

Resources