How do I pull a name from a class in an Array? - ruby

Okay, this is a little hard to explain but I will try (For starters I am only just learning to code so it may be something super simple I'm missing..)
I created a few classes, I put a name in those classes, I put them in an array, I then chose one at random and try to puts the name, and it outputs blank.
Am I doing this all completely wrong? I've been learning ruby for about 3 months now so I'm sure there is a lot I don't know.
class A
attr :name
def set_name
#name = "Aaa"
end
def get_name
return #name
end
end
class B
attr :name
def set_name
#name = "Bbb"
end
def get_name
return #name
end
end
class C
attr :name
def set_name
#name = "Ccc"
end
def get_name
return #name
end
end
name_a = A.new
name_b = B.new
name_c = C.new
which_name = Array.new
which_name[0] = name_a
which_name[1] = name_b
which_name[2] = name_c
roll = rand(max 3)
puts which_name[roll].get_name

I then chose one at random and try to puts the name, and it outputs
blank.
You never called the #set_name method in your code. You can add this:
name_a.set_name
name_b.set_name
name_c.set_name
Also, you probably want to look into #attr_accessor.

Related

How do I randomly select a name from an array and check if it's uppercase

I need to randomly pick a name from an array in Ruby and then check if it uppercase. So far I have:
def namegenerator
return #name.sample
end
def namechecker
if name.upcase then
check = TRUE
else
check = FALSE
end
end
It needs to be as two separate methods like this.
Something like this:
def sample_word(words)
words.sample
end
def upcase?(word)
word == word.upcase
end
And then something like:
words = %w[APPLE banana CherRy GRAPE]
word = sample_word(words)
puts word # e.g. BANANA
puts upcase?(word) # will print true
If you just want to check just the first letter:
names = %w(Kirk mccoy scott Spock)
names.sample.then { |name| [name, name[0] == name[0].upcase] }
#=> ["mccoy", false]
Maybe something like this:
class NameGenerator
def initialize(size, items)
#name = ""
#size = size
#items = items
end
def namegenerator
#name = #items.sample(#size).to_s
end
def namechecker?
#name == #name.upcase
end
def name
#name
end
end
ng = NameGenerator.new 1, ["name", "Name", "NAME"]
ng.namegenerator
puts ng.name, ng.namechecker?
Update
I've posted code without much thinking about abstraction and i think it would be much better to encapsulate name and upper case check to separate class and make it immutable, then make generator class that selects one entity from collection.
class NameGenerator
def initialize(items)
#items = items
end
def next
#items.sample
end
end
class Name
attr_reader :name
def initialize(name)
#name = name
end
def is_uppercase?
#name.match(/\p{Lower}/) == nil
end
end
ng = NameGenerator.new [
Name.new("name"),
Name.new("Name"),
Name.new("NAME"),
Name.new("na-me")
]
name = ng.next
puts name.name, name.is_uppercase?

Avoiding initialize method in ruby

I am writing the Ruby program found below
class Animal
attr_reader :name, :age
def name=(value)
if value == ""
raise "Name can't be blank!"
end
#name = value
end
def age=(value)
if value < 0
raise "An age of #{value} isn't valid!"
end
#age = value
end
def talk
puts "#{#name} says Bark!"
end
def move(destination)
puts "#{#name} runs to the #{destination}."
end
def report_age
puts "#{#name} is #{#age} years old."
end
end
class Dog < Animal
end
class Bird < Animal
end
class Cat < Animal
end
whiskers = Cat.new("Whiskers")
fido = Dog.new("Fido")
polly = Bird.new("Polly")
polly.age = 2
polly.report_age
fido.move("yard")
whiskers.talk
But when I run it, it gives this error:
C:/Users/akathaku/mars2/LearningRuby/Animal.rb:32:in `initialize': wrong number of arguments (1 for 0) (ArgumentError)
from C:/Users/akathaku/mars2/LearningRuby/Animal.rb:32:in `new'
from C:/Users/akathaku/mars2/LearningRuby/Animal.rb:32:in `<main>'
My investigations shows that I should create objects like this
whiskers = Cat.new("Whiskers")
Then there should be an initialize method in my code which will initialize the instance variable with the value "Whiskers".
But if I do so then what is the purpose of attribute accessors that I am using? Or is it like that we can use only one and if I have to use attribute accessors then I should avoid initializing the instance variables during object creation.
initialize is the constructor of your class and it runs when objects are created.
Attribute accessors are used to read or modify attributes of existing objects.
Parameterizing the constructor(s) gives you the advantage of having a short and neat way to give values to your object's properties.
whiskers = Cat.new("Whiskers")
looks better and it's easier to write than
whiskers = Cat.new
whiskers.name = "Whiskers"
The code for initialize in this case should look like
class Animal
...
def initialize(a_name)
name = a_name
end
...
end
All attr_reader :foo does is define the method def foo; #foo; end. Likewise, attr_writer :foo does so for def foo=(val); #foo = val; end. They do not do assume anything about how you want to structure your initialize method, and you would have to add something like
def initialize(foo)
#foo = foo
end
Though, if you want to reduce boilerplate code for attributes, you can use something like Struct or Virtus.
You should define a method right below your class name, something like
def initialize name, age
#name = name
#age = age
end

How to access attribute

I want to put the name of the enemies (toto and titi). How can I do that ?
My simplified code :
class Gamer
attr_accessor :name, ...
def initialize(name)
#name = name
...
end
...
end
class Enemy < Gamer
...
end
class Map
attr_accessor :enemies
...
end
##############
map = Map.new
map.enemies = [
Enemy.new("toto"),
Enemy.new("titi")
]
puts "#{map.enemies}"
I'm a beginner in Ruby
return :
[#<Gamer:0x000002e29da0 #name="toto">, #<Gamer:0x000002e29d50 #name="titi">]
If I understand your question correctly then:
map.enemies.each do |enemy|
puts enemy.name
end
Return an array of names
puts map.enemies.map(&:name)
You can define the method to_s in Enemy. This method is used when you puts an object:
class Enemy
...
def to_s
#name
end
end
enemy = Enemy.new("foo")
puts enemy
#=> foo
To print the names of an Array of objects, you can then use join on the Array:
map = Map.new
map.enemies = [Enemy.new("foo"), Enemy.new("bar")]
puts map.enemies.join(", ")
#=> foo, bar
This has the benefit that the Enemy object now is responsible for knowing how it should be printed, rather than this behaviour being spread across the code that uses Enemy.

Undefined local variable error with simple program that adds items to array

class Books
attr_accessor :name, :book_id
def initialize(name, book_id)
#name = name,
#book_id = book_id
end
end
class BookCollection
def intialize
#book_names = []
end
def add_to_books(book_name)
book_name.push(book_names)
end
end
book1 = Books.new("catch22", "12345")
book_collection1 = BookCollection.new
book_collection1.add_to_books(book1.name)
puts book_collection1
end
That is my code and the error I'm getting is "undefined local variable or method `book_names'". I tried adding " attr_accessor :book_names" and when I do that the printed output doesn't make sense.
There are a few mistakes in your code:
line 4 should not end with a comma.
initialize in class BookCollection is misspelled, resulting in #book_names not being initialized. #book_names therefore equals nil when you attempt to add an element to it with push. nil does not have a method push; hence the exception, and the message printed with the exception.
book_name.push(book_names) should be #book_name.push(book_name). (#book_name must be an instance_variable, as opposed to a local variable, to be visible outside a method, within the class definition.
puts book_collection1 prints the class instance; you want to print #book_names.
Here I've fixed your code. I've used << instead of push. Either is OK, but the former seems to be favored my most.
class Books
attr_accessor :name, :book_id
def initialize(name, book_id)
puts "name = #{name}, book_id = #{book_id}"
#name = name
#book_id = book_id
end
end
class BookCollection
attr :book_names
def initialize
#book_names = []
end
def add_to_books(book_name)
#book_names << book_name
end
end
book_collection1 = BookCollection.new
book1 = Books.new("Catch22", "12345")
book2 = Books.new("Hawaii", "67890")
book_collection1.add_to_books(book1.name)
book_collection1.add_to_books(book2.name)
book_collection1.book_names # => ["Catch22", "Hawaii"]
Probably just a typo at
book_name.push(book_names)
Should have been
book_names.push(book_name)
With attr_accessor :book_names

How do I write a writer method for a class variable in Ruby?

I'm studying Ruby and my brain just froze.
In the following code, how would I write the class writer method for 'self.total_people'? I'm trying to 'count' the number of instances of the class 'Person'.
class Person
attr_accessor :name, :age
##nationalities = ['French', 'American', 'Colombian', 'Japanese', 'Russian', 'Peruvian']
##current_people = []
##total_people = 0
def self.nationalities #reader
##nationalities
end
def self.nationalities=(array=[]) #writer
##nationalities = array
end
def self.current_people #reader
##current_people
end
def self.total_people #reader
##total_people
end
def self.total_people #writer
#-----?????
end
def self.create_with_attributes(name, age)
person = self.new(name)
person.age = age
person.name = name
return person
end
def initialize(name="Bob", age=0)
#name = name
#age = age
puts "A new person has been instantiated."
##total_people =+ 1
##current_people << self
end
You can define one by appending the equals sign to the end of the method name:
def self.total_people=(v)
##total_people = v
end
You're putting all instances in ##current_people you could define total_people more accurately:
def self.total_people
##current_people.length
end
And get rid of all the ##total_people related code.
I think this solves your problem:
class Person
class << self
attr_accessor :foobar
end
self.foobar = 'hello'
end
p Person.foobar # hello
Person.foobar = 1
p Person.foobar # 1
Be aware of the gotchas with Ruby's class variables with inheritance - Child classes cannot override the parent's value of the class var. A class instance variable may really be what you want here, and this solution goes in that direction.
One approach that didn't work was the following:
module PersonClassAttributes
attr_writer :nationalities
end
class Person
extend PersonClassAttributes
end
I suspect it's because attr_writer doesn't work with modules for some reason.
I'd like to know if there's some metaprogramming way to approach this. However, have you considered creating an object that contains a list of people?

Resources