Ruby access instance variables from another method - ruby

I have a class and I try to access an instance variable and I noticed I can do it in both ways by addressing it with # and without it. Why si there no error when I call it without a # puts "name : #{name}"?
class Blabla
attr_accessor :name
def initialize(blabla)
#name = blabla
end
def populate()
puts "name : #{name}"
puts "name : #{#name}"
end
end

As hinted in the comments:
attr_accessor :name
is shorthand for:
def name
#name
end
def name=(name)
#name = name
end
So expanding your code we have:
class Blabla
def name
#name
end
def name=(name)
#name = name
end
def initialize(blabla)
#name = blabla
end
def populate()
puts "name : #{name}"
puts "name : #{#name}"
end
end
#{name} references the method name which returns #name.
#{#name} references #name directly.
Also be sure to understand attr_reader, attr_writer methods.

Related

Ruby: self keyword in class definition

Is replacing self.name.split with #name.split the same thing?
class Person
attr_accessor :name
def initialize(name)
#name = name
end
def normalize_name
self.name.split(" ").collect{|w| w.capitalize}.join(" ")
end
end
Yes it has the same outcome, because you defined a attr_accessor which gives you getter and setter methods
class Person
def name
#name
end
def name=(str)
#name = str
end
end
so you can call the function name using self receiver but if you get rid of the attr_accessor from your class you can't use self.name to get the value of name
class Person
def initialize(name)
#name = name
end
def normalize_name
self.name.split(" ").collect{|w| w.capitalize}.join(" ")
end
end
p = Person.new('John Doe')
p.normalize_name
NoMethodError: undefined method `name' for #<Person:0x00557e98300f68 #name="John Doe">

How do I get the string value in this string interpolation

I've been trying to get this code to output:
'Mary has a pet called Satan.'
But what I get is:
'Mary has a pet called #<Cat:0x00000002784c20>'
Code Below:
class Person
def initialize(name)
#name = name
#pet = nil
#hobbies = []
end
def describe()
puts "This persons name is #{#name}."
puts "#{#name}'s hobbies are:"
#hobbies.map { |hobby| puts hobby }
if #pet == nil
puts "#{#name} has not got any pets."
else
puts "#{#name} has a pet called #{#pet}"
end
end
attr_accessor :pet, :hobbies
end
class Cat < Animal
def initialize(name)
#name = name
end
end
satan = Cat.new("Satan")
mary = Person.new("Mary")
mary.pet = satan
mary.describe
Thanks for all your help.
In your describe() function, you are calling the object Cat without specifing the name.
But if you call #{pet.name} it will throw:
<undefined method `name' for #<Cat:0x0055d750a1a450 #name="Satan">
You have to allow the access to the variable name in the Cat class first with attr_accessor
class Cat < Animal
attr_accessor :name # First allow access
end
class Person
def describe()
puts "#{#name} has a pet called #{#pet.name}" # Then call the pet's name!
end
end

Adapter Pattern in ruby: Accessing Your Instance Variables

I am studying the adapter pattern implementation in ruby. I want to access an instance variable within the adapter module definition. Take a look at the following code:
module Adapter
module Dog
def self.speak
# I want to access the #name instance variable from my Animal instance
puts "#{name} says: woof!"
end
end
module Cat
def self.speak
# I want to access the #name instance variable from my Animal instance
puts "#{name} says: meow!"
end
end
end
class Animal
attr_accessor :name
def initialize(name)
#name = name
end
def speak
self.adapter.speak
end
def adapter
return #adapter if #adapter
self.adapter = :dog
#adapter
end
def adapter=(adapter)
#adapter = Adapter.const_get(adapter.to_s.capitalize)
end
end
To test it out I did the following:
animal = Animal.new("catdog")
animal.adapter = :cat
animal.speak
I want it to return the following:
catdog says: meow!
Instead it says:
Adapter::Cat says: meow!
Any tips on how I can get access to the Animal#name instance method from the adapter module? I think the issue is that my adapter methods are class-level methods.
Thanks!
You need to use your Module as a mixin and provide a way to keep track of which module is active, the methods don't seem to be overwritten by reincluding or reextending so I took the extend and remove methods I found here.
module Adapter
module Dog
def speak
puts "#{name} says: woof!"
end
end
module Cat
def speak
puts "#{name} says: meow!"
end
end
def extend mod
#ancestors ||= {}
return if #ancestors[mod]
mod_clone = mod.clone
#ancestors[mod] = mod_clone
super mod_clone
end
def remove mod
mod_clone = #ancestors[mod]
mod_clone.instance_methods.each {|m| mod_clone.module_eval {remove_method m } }
#ancestors[mod] = nil
end
end
class Animal
include Adapter
attr_accessor :name, :adapter
def initialize(name)
#name = name
#adapter = Adapter::Dog
extend Adapter::Dog
end
def adapter=(adapter)
remove #adapter
extend Adapter::const_get(adapter.capitalize)
#adapter = Adapter.const_get(adapter.capitalize)
end
end
animal = Animal.new("catdog")
animal.speak # catdog says: woof!
animal.adapter = :cat
animal.speak # catdog says: meow!
animal.adapter = :dog
animal.speak # catdog says: woof!
This is because name inside of the module context refers to something entirely different than the name you're expecting. The Animal class and the Cat module do not share data, they have no relationship. Coincidentally you're calling Module#name which happens to return Adapter::Cat as that's the name of the module.
In order to get around this you need to do one of two things. Either make your module a mix-in (remove self, then include it as necessary) or share the necessary data by passing it in as an argument to speak.
The first method looks like this:
module Adapter
module Dog
def self.speak(name)
puts "#{name} says: woof!"
end
end
end
class Animal
attr_accessor :name
attr_reader :adapter
def initialize(name)
#name = name
self.adapter = :dog
end
def speak
self.adapter.speak(#name)
end
def adapter=(adapter)
#adapter = Adapter.const_get(adapter.to_s.capitalize)
end
end
That doesn't seem as simple as it could be as they basically live in two different worlds. A more Ruby-esque way is this:
module Adapter
module Dog
def speak
puts "#{name} says: woof!"
end
end
end
class Animal
attr_accessor :name
attr_reader :adapter
def initialize(name)
#name = name
self.adapter = :dog
end
def adapter=(adapter)
#adapter = Adapter.const_get(adapter.to_s.capitalize)
extend(#adapter)
end
end

Trying to figure out what's wrong with this code

I can't come up with a solution.
class Person
def initialize(name)
#name = name
end
def greet(other_name)
#other_name
print "Hi #{#other_name}, my name is #{#name}"
end
end
kit = Person.new("Tom", "Jerry")
kit.greet
I would appreciate a helping hand.
You have to make a decision:
Do you want to provide both names when initializing the Person:
class Person
def initialize(name, other)
#name = name
#other = other
end
def greet
puts "Hi #{#other}, my name is #{#name}"
end
end
kit = Person.new("Tom", "Jerry")
kit.greet
#=> Hi Jerry, my name is Tom
Or do you want to provide the second name when calling the greet method:
class Person
def initialize(name)
#name = name
end
def greet(other)
puts "Hi #{other}, my name is #{#name}"
end
end
kit = Person.new("Tom")
kit.greet("Jerry")
#=> Hi Jerry, my name is Tom
In the initialize method, you should take in two parameters, because you are calling it with two. You were declaring the #other_name variable inside the greet function instead of the initialize one.
This will work.
class Person
def initialize(name, other_name)
#name = name
#other_name = other_name
end
def greet
print "Hi #{#other_name}, my name is #{#name}"
end
end
kit = Person.new("Tom", "Jerry")
kit.greet
https://repl.it/C5wn
Consider writing your code like this:
class Person
attr_reader :name
def initialize(name)
#name = name
end
def greet(person)
puts "Hi #{person.name}, my name is #{name}"
end
end
tom = Person.new("Tom")
jer = Person.new("Jerry")
tom.greet(jer) #=> Hi Jerry, my name is Tom.
This way you actually have another person as an object instead of just a name.

Basic Ruby class - correct code so it returns expected value

Extremely basic, yet I can't figure it out! Noob issues - I have tried several different answers for this, and I am still getting argument errors. Can someone please help enlighten me on the correct answer?
Correct this code, so that the greet function returns the expected value.
class Person
def initialize(name)
#name = name
end
def greet(other_name)
"Hi #{other_name}, my name is #{name}"
end
end
class Person
def initialize(name)
#name = name
end
def greet(other_name)
"Hi #{other_name}, my name is #{#name}"
end
end
You need to access your instance variables by prefixing the variable name with #. Just the same way as when you assigned it.
name is not available in greet. You can either use #name, or add an accessor:
class Person
def initialize(name)
#name = name
end
def greet(other_name)
"Hi #{other_name}, my name is #{#name}"
end
end
or
class Person
attr_accessor :name
def initialize(name)
#name = name
end
def greet(other_name)
"Hi #{other_name}, my name is #{name}"
end
end

Resources