Ruby Inheritance - Super Initialize getting wrong number of arguments - ruby

I'm playing with Ruby and learning about OO techniques and inheritance and I've finally hit an error that has eluded me for awhile.
Person Class
class Person
attr_accessor :fname, :lname, :age
def has_hat?
#hat
end
def has_hat=(x)
#hat = x
end
def initialize(fname, lname, age, hat)
#fname = fname
#lname = lname
#age = age
#hat = hat
end
def to_s
hat_indicator = #hat ? "does" : "doesn't"
#fname + " " + #lname + " is " + #age.to_s + " year(s) old and " + hat_indicator + " have a hat\n"
end
def self.find_hatted()
found = []
ObjectSpace.each_object(Person) { |p|
person = p if p.hat?
if person != nil
found.push(person)
end
}
found
end
end
Programmer Class (inherits from Person)
require 'person.rb'
class Programmer < Person
attr_accessor :known_langs, :wpm
def initialize(fname, lname, age, has_hat, wpm)
super.initialize(fname, lname, age, has_hat)
#wpm = wpm
#known_langs = []
end
def is_good?
#is_good
end
def is_good=(x)
#is_good = x
end
def addLang(x)
#known_langs.push(x)
end
def to_s
string = super.to_s
string += "and is a " + #is_good ? "" : "not" + " a good programmer\n"
string += " Known Languages: " + #known_languages.to_s + "\n"
string += " WPM: " + #wpm.to_s + "\n\n"
string
end
end
Then in my main script It's failing on this line
...
programmer = Programmer.new('Frank', 'Montero', 46, false, 20)
...
With this error
./programmer.rb:7:in `initialize': wrong number of arguments (5 for 4) (ArgumentError)
from ./programmer.rb:7:in `initialize'
from ruby.rb:6:in `new'
from ruby.rb:6:in `main'
from ruby.rb:20

call super with required params instead calling super.initialize.
super(fname, lname, age, has_hat)

Programmer initialize should be -
def initialize(fname, lname, age, has_hat, wpm)
super(fname, lname, age, has_hat)
#wpm = wpm
#known_langs = []
end

Related

unexpected ',' syntax error ruby

I'm learning ruby and have looked the internet to see whats wrong with this. I have tried adding spacing and removing it between the variables I'm passing but i keep getting this error:
25: syntax error, unexpected ',', expecting ')'
student1.grades =(60,70,80)
Here is the code:
class Student
attr_accessor :name, :age
def initialize(name,age)
#name = name
#age = age
end
def grades(math,english,science)
#math = math
#english = english
#science = science
average_grade = (math.to_i + english.to_i + science.to_i) / 3
return average_grade
end
def to_s
puts "Name = #{name}"
puts "Age = #{age}"
puts self.grades
end
end
student1 = Student.new("Tom","23")
student1.grades = (60,70,80)
puts student1
grades receives three parameters. You don't do that with assignment. So change
student1.grades = (60,70,80)
to
student1.grades(60,70,80)
You can assign the grades for student object as
student1.grades(60,70,80)
Also minor edits.You can add the method for computing average
def grades_details
average_grade = (#math.to_i + #english.to_i + #science.to_i) / 3
return average_grade
end
So when you override to string u call it instead of self.grades
def to_s
puts "Name = #{name}"
puts "Age = #{age}"
puts grades_details
end

How to count votes, and deal with undefined method 'push' for nil:NilClass

My goal is to make a voting machine that counts candidates' votes into totals. Eventually I want to add the option to write-in a candidate, but I'm stuck with this error:
undefined method 'push' for nil:NilClass) at line 30.
I defined castVote, why isn't it being recognized?
class Candidate
attr_accessor :name
attr_accessor :count, :total
def initialize(cname)
#name = cname
#vote = Array.new
end
def totalVotes
sum = 0
if #count > 0
#count.each { |c| sum += c }
#total = sum
else
#total = 0
end
end
def castVote(vote)
if vote.is_a?(Integer) || vote.is_a?(Float)
#count.push(vote)
totalVotes
end
end
#Candidate
candidate1 = Candidate.new("Donald Duck")
#Next line is where error occurs
candidate1.castVote(1)
candidate1.castVote(1)
candidate2 = Candidate.new("Minnie Mouse")
candidate2.castVote(1)
candidate3 = Candidate.new("Goofy")
finalResults = Array.new
finalResults[0] = candidate1
finalResults[1] = candidate2
finalResults[2] = candidate3
finalResults.each { |candidate| puts "Name: " + candidate.name + "Score " + candidate.totalVotes }
end
You left out the #count instance variable in your initialize method so it's nil everywhere in that class and never gets initialized:
class Candidate
attr_accessor :name
attr_accessor :count, :total
def initialize(cname)
#name = cname
#vote = Array.new
#count = []
end
def totalVotes
sum = 0
if #count.length > 0
#count.each { |c| sum += c }
#total = sum
else
#total = 0
end
end
def castVote(vote)
if vote.is_a?(Integer) || vote.is_a?(Float)
#count.push(vote)
totalVotes
end
end
#Candidate
candidate1 = Candidate.new("Donald Duck")
#Next line is where error occurs
candidate1.castVote(1)
candidate1.castVote(1)
candidate2 = Candidate.new("Minnie Mouse")
candidate2.castVote(1)
candidate3 = Candidate.new("Goofy")
finalResults = Array.new
finalResults[0] = candidate1
finalResults[1] = candidate2
finalResults[2] = candidate3
finalResults.each { |candidate| puts "Name: " + candidate.name + "Score " + candidate.totalVotes }
end

How to have an object(class) be a parameter for another object(class) in Ruby

I am new to Ruby so I am not exactly sure what I am doing wrong or violating.
I have two classes in my example, Deliverable and Pillar.
A pillar object can contain one or more deliverables.
class Deliverable
def initialize (name, state, pillar)
#name = name
#state = state
#pillarNumber = pillar
end
def getName
#name
end
def state
#state
end
def pillarNumber
#pillarNumber
end
end
class Pillar
def initalize (name, mine)
#name = name
#mine = mine
end
def getName
#name
end
def getDeliverable
#mine
end
def getDeliverableName
#mine.getName
end
end
aDel = Deliverable.new("Deliverable", 0, 1)
puts "Deliverable Name: " + aDel.getName
aPil = Pillar.new("Pillar", aDel)
puts "Pillar Name: " + aPil.getName + "\n"
When I run this code I get this error:
pillar.rb:46:in `initialize': wrong number of arguments (2 for 0) (ArgumentError)
from pillar.rb:46:in `new'
from pillar.rb:46:in `<main>'
Any advise on what I am doing wrong?
You mispelled the name of the constructor for Pillar
# ā‡“ NOTE MISSED ā€œiā€ HERE
def initialize (name, mine)

m.one + m.two + m.three doesn't work

This is my code:
class Person
def initialize(first_name, last_name, age)
#first_name = first_name
#last_name = last_name
#age = age
end
def first_name
puts #first_name
end
def last_name
puts #last_name
end
def age
puts #age
end
end
class Musician < Person
def initialize(first_name, last_name, age, instrument)
#first_name = first_name
#last_name = last_name
#age = age
#instrument = instrument
end
def instrument
puts #instrument
end
end
Then when I try to do the following:
m = Musician.new("George", "Harrison", 58, "guitar")
m.first_name + " " + m.last_name + ": " + m.age.to_s
I get an error:
in <main>': undefined method+' for nil:NilClass (NoMethodError)
Why can't I just concatenate the results of objects method?
all your methods return nil rather than the value you wish, that is, "puts" returns nil.
just eliminate the "puts" and try again

In Ruby is there a way to overload the initialize constructor?

In Java you can overload constructors:
public Person(String name) {
this.name = name;
}
public Person(String firstName, String lastName) {
this(firstName + " " + lastName);
}
Is there a way in Ruby to achieve this same result: two constructors that take different arguments?
The answer is both Yes and No.
You can achieve the same result as you can in other languages using a variety of mechanisms including:
Default values for arguments
Variable Argument lists (The splat operator)
Defining your argument as a hash
The actual syntax of the language does not allow you to define a method twice, even if the arguments are different.
Considering the three options above these could be implemented with your example as follows
# As written by #Justice
class Person
def initialize(name, lastName = nil)
name = name + " " + lastName unless lastName.nil?
#name = name
end
end
class Person
def initialize(args)
name = args["name"]
name = name + " " + args["lastName"] unless args["lastName"].nil?
#name = name
end
end
class Person
def initialize(*args)
#Process args (An array)
end
end
You will encounter the second mechanism frequently within Ruby code, particularly within Rails as it offers the best of both worlds and allows for some syntactic sugar to produce pretty code, particularly not having to enclose the passed hash within braces.
This wikibooks link provides some more reading
I tend to do
class Person
def self.new_using_both_names(first_name, last_name)
self.new([first_name, last_name].join(" "))
end
def self.new_using_single_name(single_name)
self.new(single_name)
end
def initialize(name)
#name = name
end
end
But I don't know if this is the best approach.
class Person
def initialize(name, lastName = nil)
name = name + " " + lastName unless lastName.nil?
#name = name
end
end
class StatementItem
attr_reader :category, :id, :time, :amount
def initialize(item)
case item
when Order
initialize_with_order(item)
when Transaction
initialize_with_transaction(item)
end
end
def valid?
!(#category && #id && #time && #amount).nil?
end
private
def initialize_with_order(order)
return nil if order.status != 'completed'
#category = 'order'
#id = order.id
#time = order.updated_at
#amount = order.price
end
def initialize_with_transaction(transaction)
#category = transaction.category
#id = transaction.id
#time = transaction.updated_at
#amount = transaction.amount
end
end
You can use konstructor gem to declare multiple constructors in Ruby and imitate overloading:
class Person
def initialize(name)
#name = name
end
konstructor
def from_two_names(first_name, last_name)
#name = first_name + ' ' + last_name
end
end
Person.new('John Doe')
Person.from_two_names('John', 'Doe')
You could use the double splat operator ** in conjunction with logical or (double pipes) || inside the initialize method to achieve the same effect.
class Person
def initialize(**options)
#name = options[:name] || options[:first_name] << ' ' << options[:last_name]
end
end
james = Person.new(name: 'James')
#=> #<Person #name="James">
jill_masterson = Person.new(first_name: 'Jill', last_name: 'Masterson')
#=> #<Person #name="Jill Masterson">
However, if a new Person is created without a first_name, then the append << operation will fail with NoMethodError: undefined method '<<' for nil:NilClass. Here is a refactored initialize method to handle this case (using strip to remove whitespace if either option is excluded).
class Person
def initialize(**options)
#name = options[:name] || [ options[:first_name] , options[:last_name] ].join(' ').strip
end
end
goldfinger = Person.new(last_name: 'Goldfinger')
#=> #<Person #name="Goldfinger">
oddjob = Person.new(first_name: 'Oddjob')
#=> #<Person #name="Oddjob">
In fact, this approach handles calling Person.new without arguments or with an unexpected key to return the new instance with #name set to an empty string:
nameless = Person.new
#=> <#Person #name="">
middle_malcom = Person.new(middle_name: 'Malcom')
#=> <#Person #name="">
checkout functional-ruby gem which is inspired by Elixir pattern matching features.
class Person
include Functional::PatternMatching
defn(:initialize, String) { |name|
#name = name
}
defn(:initialize, String, String) {|first_name, last_name|
#name = first_name + ' ' + last_name
}
end

Resources