class Employee
EMP = []
attr_reader :name, :hobbies, :friends
def initialize(name)
#name = name
#hobbies = []
#friends = []
EMP << self
end
end
Can we discuss what happens at this line please: EMP << self ?
Obviously an element is added to the existing array (the array called EMP) - that is what is implied by the << symbol.
But, which is the element that is added? Is it only #name and do we know it is only the #name variable because it is the only argument from the initialize method?
What if the initialize method had 2 arg:
def initialize(name, hob)
#name = name
#hobbies = hob
#friends = []
EMP << self
end
What would then be the effect of EMP << self? Thank you in advance.
The keyword self inside an instance method is a reference to the current object. So you are adding the object that is being intialized to the EMP array of the object itself - a thing that doesn't really make sense!. the class Employee.
You may want to add the object to a class variable, which can be defined this way:
class Employee
##EMP = []
def initialize(name)
#name = name
#hobbies = []
#friends = []
##EMP << self
end
end
Thus, every time a new object is initialized, it's added to the ##EMP array of the class itself.
Related
I want to make a small application where users can create a list of stores and inventories for the stores. Here's what I have for the store class:
class Store
attr_accessor :name, :inventory
def initialize(name)
#name = name
#inventory = []
##all << self
end
end
It initializes with an empty array, and I want to fill it up with items (as strings). I'm aware you can use, say newStore = Store.new to create a store and then further call on newStore, but I want the user to be able to create as many stores as they want and when they create a second newStore it will override that variable. How do I re-access that previous class instance to edit the inventory array?
You can change it to the following and just create a new array when it wasn't initialized before:
def initialize(name)
#name = name
#inventory = []
##all ||= []
##all << self
end
I have a ruby class like this:
class Table
def initialize(name)
#name = name
#columns = {}
end
end
I'm creating different objects:
table_1 = Table.new("First")
table_2 = Table.new("Second")
table_3 = Table.new("Third")
How can I find among the objects of the Table class the object having "Second" as name attribute ?
Without creating any additional structures of data:
ObjectSpace.each_object(Table).find { |object| object.instance_variable_get(:#name) == "Second" }
=> #<Table:0x007f9f912b0ce0 #columns={}, #name="Second">
You can keep a reference to an array of instances in the class.
class Table
#instances = []
class << self
attr_accessor :instances
end
def initialize(name)
#name = name
#columns = {}
self.class.instances << self
end
end
Then you can get all the instances by
Table.instances
This, however, will prevent all the Table objects being garbage collected, so it is only feasible if you have only a small amount of Tables and that amount never grows, otherwise you'll have a memory leak.
Let's add a getter method for the name attribute
class Table
attr_reader :name
def initialize(name)
#name = name
#columns = {}
end
end
Now, if you have an array of Table objects
arr = [Table.new("First"), Table.new("Second"), Table.new("Third")]
You can find by name
arr.find { |table| table.name == "Second" }
=> #<Table:0x007f862107eb18 #name="Second", #columns={}>
You can use enumerable find or any similar method from the enumerable module:
class Table
attr_reader :name
def initialize(name)
#name = name
#columns = {}
end
end
table_1 = Table.new("First")
table_2 = Table.new("Second")
table_3 = Table.new("Third")
x = [table_1, table_2, table_3].find { |t| t.name == "Second" }
puts x.name => "Second"
Let's say we have reader on name in Table:
class Table
attr_reader :name
def initialize(name)
#name = name
#columns = {}
end
end
I'd encourage you to store these Table classes' objects in a list/array:
table_1 = Table.new("First")
table_2 = Table.new("Second")
table_3 = Table.new("Third")
tables = [table_1, table_2, table_3]
Which can then be used for finding it using find(as mentioned in one of the answers) or detect:
tables.detect { |t| t.name == "Second" } #=> table_2 object
If you'd like to go one more step ahead then we can have another class maintaining this array:
class TableList
attr_reader :tables
def initialize
#tables = tables
end
def add(table)
#tables << table
end
def find_by_name(name)
tables.detect{ |table| table.name == name }
end
end
Which can then be used as:
table_1 = Table.new("First")
table_2 = Table.new("Second")
table_3 = Table.new("Third")
table_list = TableList.new
table_list.add(table_1)
table_list.add(table_2)
table_list.add(table_3)
table_list.find_by_name('Second') #=> table_2 object
I need to have add_student add multiple students to the array grade. The method should add multiple students to the array and assign them to grade or the key in the hash.
class School
def initialize(name)
#name = name
end
def roster
#roster ||= {}
end
def add_student(student, grade)
roster[grade] = []
roster[grade] << student
end
def student_grade(grade)
return students
end
end
I do not understand why add_student does not add multiple arguments. I get an error that it returns only one argument or nil.
Vutran's answer correctly identifies the problem, but a better solution would be to use a default proc to automatically initialize any missing value in the hash.
class School
attr_reader :roster
def initialize(name)
#name = name
#roster = Hash.new {|h,k| h[k] = [] }
end
def add_student(student, grade)
roster[grade] << student
end
# ...
end
Every time you add a student, you reinitialize your roster[grade] to [] which discards all of previous added students. To fix this, you might change:
roster[grade] = []
to
roster[grade] ||= []
This line of code does the following work: it initializes roster[grade] to [] if roster[grade] is nil.
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
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?