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
Related
How can i add all created objects in self.persons ?
class Person
attr_accessor :name, :last_name, :age
def initialize(name, last_name, age = "no_age")
##persons = []
#name = name
#last_name = last_name
#age = age
add(#name, #last_name, #age)
end
def self.persons
##persons
end
private
def add(name, last_name, age)
##persons << [name, last_name, age]
end
end
person1 = Person.new("name1", "lastname1", 12)
person2 = Person.new("name2", "lastname2", 16)
p Person.persons # => [["name2", "lastname2", 16]]
You can use "conditional assignment operator" to check if array is nil.
def initialize(name, last_name, age = "no_age")
##persons ||= []
#name = name
#last_name = last_name
#age = age
add(#name, #last_name, #age)
end
modify this code to create search by first name using user input output must show age and title
# A simple Employee class
class Employee
attr_reader :first_name, :last_name, :title, :age
def initialize(fname, lname, title, age)
#first_name = fname
#last_name = lname
#title = title
#age = age
end
# A string representation of the Employee object
def to_s
"#{first_name} #{last_name}, #{title}, #{age}"
end
end
# The collection class for Employee objects
class Employees
include Enumerable
def initialize
#employees = []
end
# Add Employee objects to the collection
def <<(employee)
#employees << employee
end
# Method mandated by the Enumerable module
def each
#employees.each { |e| yield(e) }
end
end
employees = Employees.new
employees << Employee.new('Anita', 'Baker', 'President', 48)
employees << Employee.new('Frank', 'Gifford', 'Director', 58)
employees << Employee.new('Barbara', 'Eden', 'Secretary', 34)
employees << Employee.new('George', 'Clooney', 'Project Manager', 37)
employees << Employee.new('Emily', 'Davies', 'Programmer', 28)
employees << Employee.new('David', 'Faber', 'Programmer', 55)
employees << Employee.new('Cindy', 'Adams', 'Programmer', 33)
employees << Employee.new('Helen', 'Hamilton', 'Business Analyst', 42)
You have already done all the necessary work by implementing Enumerable interface. Just accept user input and call the Enumerable#find method:
fname = gets.chomp
e = employees.find {|i| i.first_name == fname}
In the balance function, I am trying to calculate the balance using the inject method instead. Tried the documentation but couldn't get it..
I am new to Ruby and programming so any help would be appreciated..
class BankAccount
attr_reader :name
def initialize(name)
#name = name
#transactions = []
add_transaction("Beginning Balance", 0)
end
def credit(description, amount)
add_transaction(description, amount)
end
def debit(description, amount)
add_transaction(description, -amount)
end
def add_transaction(description, amount)
#transactions.push(description: description, amount: amount)
end
def balance
balance = 0.0
#transactions.each do |transaction|
balance += transaction[:amount]
end
return balance
end
def to_s
"Name: #{name}, Balance: #{sprintf("%0.2f", balance)}"
end
def print_register
puts "#{name}'s Bank Account"
puts "-" * 40
puts "Description".ljust(30) + "Amount".rjust(10)
#transactions.each do |transaction|
puts transaction[:description].ljust(30) + sprintf("%0.2f", transaction[:amount]).rjust(10)
end
puts "-" * 40
puts "Balance:".ljust(30) + sprintf("%0.2f", balance).rjust(10)
puts "-" * 40
end
end
Your example is great, as it's very easy to change the loop you have to an inject call:
def balance
#transactions.inject(0.0) { |balance, transaction| balance + transaction[:amount] }
end
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
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