I am finishing my coursework, a simple library system.
I am facing a problem like below:
book.rb
class Book
attr_accessor :title, :author, :language, :classification, :isbn, :book_id, :borrow_status
def initialize(title, author, language, classification, isbn, book_id, borrow_status)
#title = title
#author = author
#language = language
#classification = classification
#isbn = isbn
#book_id = book_id
#borrow_status = borrow_status
end
def bookid
#book_id
end
def booklist
#title = #title.split(/ |\_|\-/).map(&:capitalize).join(" ")
#author = #author.split(/ |\_|\-/).map(&:capitalize).join(" ")
#language = #language.capitalize!
#isbn.to_s
#book_id.to_s
{
"Title" => #title,
"Author" => #author,
"Language" => #language,
"Classification" => #classification,
"ISBN" => #isbn,
"Book ID" => #book_id,
"Status" => #borrow_status,
}
end
end
user.rb
require_relative 'book.rb'
class User
attr_accessor :name, :address, :gender, :age, :id, :borrow
def initialize(name, address, gender, age, id, borrow)
#name = name
#address = address
#gender = gender
#age = age
#id = id
#borrow = borrow
end
def userlist
#name = #name.split(/ |\_|\-/).map(&:capitalize).join(" ")
#address = #address.split(/ |\_|\-/).map(&:capitalize).join(" ")
#age.to_s
#id.to_s
if #borrow.nil?
puts "nothing"
elsif
puts #I wish I can put book's name here, if I entered correct #borrow. eg.,for user3's #borrow=4(in top.rb), user3.#borrow=book4.#book_id, then print #name of book4
else
puts "error"
end
the problem is inside user.rb, in if-elsif-else loop, which is
I wish I can put book's name here, if I entered correct #borrow.
eg.,for user3's #borrow=4(in top.rb), user3.#borrow=book4.#book_id,
then print #name of book4
any solution?
#user.rb
require_relative 'book.rb'
class User
attr_accessor :name, :address, :gender, :age, :id, :borrow
def initialize(name, address, gender, age, id, borrow)
#name = name
#address = address
#gender = gender
#age = age
#id = id
#borrow = borrow
end
def userlist
#name = #name.split(/ |\_|\-/).map(&:capitalize).join(" ")
#address = #address.split(/ |\_|\-/).map(&:capitalize).join(" ")
#age.to_s
#id.to_s
if borrow.nil?
puts "nothing"
elsif borrow
puts borrow.title
else
puts "error"
end
end
end
#book = Book.new('Book1', 'Auth1', 'EN', 'fiction', 'isb123', 1, 'borrowed')
#user = User.new('Mr Foo', '123 st', 'male', 23, 123, nil)
#user.borrow=#book
#user.userlist
#=>Book1
But you might instead want to send an array of books, then you'll need to modify your method to handle array
def userlist
#name = #name.split(/ |\_|\-/).map(&:capitalize).join(" ")
#address = #address.split(/ |\_|\-/).map(&:capitalize).join(" ")
#age.to_s
#id.to_s
if borrow.nil?
puts "nothing"
elsif borrow.length > 0 # checks if anythint in the array
borrow.each{ |b| puts b.title }
else
puts "error"
end
end
Related
I started learning Ruby today and immediately dove into a question of how to pass an object to another class as its initializer. How do I pass a Name and Address to the initializer of a Person?
Here is my code:
class Name
attr_accessor :first, :last
def initialize (first, last)
#first = first
#last = last
puts "created name"
end
def show ()
puts first, last
end
end
class Address
attr_accessor :street, :city, :state
def initialize (street, city, state)
#street = street
#city = city
#state = state
puts "Created addresss"
end
def show()
puts street, city, state
end
end
class Person
attr_accessor :name, :address
def initialize (name, address)
#name = name
#address = address
end
def show()
puts name
puts addr
puts "created person"
end
end
name = Name.new("Sam", "Spade")
name.show()
addr = Address.new("111 State St", "Albany", "NY")
addr.show()
person = Person.new (name, addr)
person.show()
The diagnostic I get is person.rb:48: syntax error, unexpected ')', expecting '='
...erson = Person.new (name, addr)
It might appear that the creation of the person object is missing a "p", but the code shows the "p" as being present.
I fixed a few bugs in your code.
class Name
attr_accessor :first, :last
def initialize (first, last)
#first = first
#last = last
puts "created name"
end
def show()
puts first, last
end
end
class Address
attr_accessor :street, :city, :state
def initialize (street, city, state)
#street = street
#city = city
#state = state
puts "Created addresss"
end
def show()
puts street, city, state
end
end
class Person
attr_accessor :name, :address
def initialize (name, address)
#name = name
#address = address
end
def show()
name.show
address.show
puts "created person"
end
end
name = Name.new("Sam", "Spade")
name.show()
addr = Address.new("111 State St", "Albany", "NY")
addr.show()
person = Person.new(name, addr)
person.show()
You had an extra space between new and the params Person.new (name, addr)
In the person class the variable is called address not addr
I think instead of putsing the objects in Person#show I'm guessing you wanted to call your show method which puts their attributes.
I think you got confused by the ellipses (...) in the syntax error. It was trying to simplify your code to the erroring part and it just so happened to cut off that p. The real issue is the space after new told the program to call the new method with no params passed in.
I don't get it, how "dungeon.rb" runs ok, while "tarifs.rb" crushes with:
undefined method 'full_description' for nil:NilClass(NoMethodError).
Both piece of code are similar. what is different: Names of classes, some methods names and their arguments.
### OK piece -> dungeon.rb code
class Dungeon
attr_accessor :player
def initialize(player)
#player = player
#rooms = {}
end
def add_room(reference, name, description, connections)
#rooms[reference] = Room.new(reference, name, description, connections)
end
def start(location)
#player.location = location
show_current_description
end
def show_current_description
puts "Location: " + find_room_in_dungeon(#player.location).full_description
end
def find_room_in_dungeon(reference)
#rooms[reference]
end
def find_room_in_direction(direction)
find_room_in_dungeon(#player.location).connections[direction]
end
def go(direction)
puts "\nYou go: " + direction.to_s
#player.location = find_room_in_direction(direction)
show_current_description
puts "Where you want to go now? "
end
end
class Player
attr_accessor :name, :location
def initialize(name)
#name = name
end
end
class Room
attr_accessor :reference, :name, :description, :connections
def initialize(reference, name, description, connections)
#reference = reference
#name = name
#description = description
#connections = connections
end
def full_description
#name + "\n\n ** You are in " + #description
end
end
#######---------------- Creating Player and Dungeon objects ------------------
puts "Enter your name"
username = gets.chomp
player = Player.new(username)
my_dungeon = Dungeon.new(player)
#######---------------- Add rooms to the dungeon ----------------------
my_dungeon.add_room(:largecave,
"Large Cave",
"a large cavernous cave **",
{:west => :smallcave,
:south => :grotto,
:east => :largecave })
my_dungeon.add_room(:smallcave,
"Small Cave",
"a small, claustrophobic cave **",
{:west => :smallcave,
:south => :grotto,
:east => :largecave })
my_dungeon.add_room(:grotto,
"Wet Grotto",
"a medium range grotto with water pool in center **",
{:west => :smallcave,
:south => :grotto,
:east => :largecave })
puts "Your name is: #{player.name}"
#######------------ Start the dungeon by placing the player in the large cave ------------------------
my_dungeon.start(:largecave)
#######------------- Player navigation ---------------------
ok_input = [:west, :east, :south, :exit]
loop do
puts "Choose direction please: "
input = gets.chomp.to_sym
if ok_input.include?(input)
my_dungeon.go(input)
elsif ok_input.include?(input) == false
puts "Wrong input!!! Allowed input: 'west', 'east', 'south' or 'exit' "
end
break if input == :exit
end
#### NOT OK piece -> tarifs.rb code
class Invest
attr_accessor :user
def initialize(user)
#user = user
#tarifs = {}
end
def add_tarif(reference, tname, description, connections, procent, days)
#tarifs[reference] = Tarif.new(reference, tname, description, connections, procent, days)
end
def start(choosed_tarif)
#user.choosed_tarif = choosed_tarif
show_current_description
end
def show_current_description
puts " Current tarif is: " + find_tarif_in_tarifs(#user.choosed_tarif).full_description
end
def find_tarif_in_tarifs(reference)
#tarifs[reference]
end
def find_tarif_in_direction(direction)
find_tarif_in_tarifs(#user.choosed_tarif).connections[direction]
end
def go(direction)
puts "\nYou choosed: " + direction.to_s
#user.choosed_tarif = find_tarif_in_direction(direction)
show_current_description
end
end
class User
attr_accessor :uname, :choosed_tarif
def initialize(uname)
#uname = uname
end
end
class Tarif
attr_accessor :reference, :tname, :description, :connections, :procent, :days
def initialize(reference, tname, description, connections, procent, days)
#reference = reference
#tname = tname
#description = description
#connections = connections
#procent = procent
#days = days
end
def full_description
#tname + "\n " + #description
end
end
#######-------- Creating User and Invest objects ----------
puts "Enter your name"
uname = gets.chomp
user = User.new(uname)
my_tarif = Invest.new(user)
#######-------- Adding tariffs to the Invest --------------
my_tarif.add_tarif(:start,
"Start",
"Start tarif 2% daily for 3 days in total",
{:first => :start,
:second => :business,
:third => :profi },
2,
3)
my_tarif.add_tarif(:business,
"Business",
"Business tarif 3% daily for 5 days in total",
{:first => :start,
:second => :business,
:third => :profi },
3,
5)
my_tarif.add_tarif(:profi,
"Profi",
"Profi tarif 4% daily for 10 days in total",
{:first => :start,
:second => :business,
:third => :profi },
4,
10)
#######-------- Autostarts with first tarif --------------
my_tarif.start(:business)
#######-------- Tarif navigation --------------
ok_input = [:start, :business, :profi, :exit]
puts "Choose your tarif: "
input = gets.chomp.to_sym
if input == :start
my_tarif.go(input)
elsif input == :business
my_tarif.go(input)
elsif input == :profi
my_tarif.go(input)
else puts "Please input 'start', 'business' or 'profi' !!!"
end
tarifs.rb code must allow to navigate in tarifs.rb - just like dungeon.rb code allows to navigate in rooms. Meanwhile, tarifs.rb output shows folowing:
tarifs.rb:22:in show_current_description': undefined methodfull_description' for nil:NilClass (NoMethodError) from tarifs.rb:36:in go' from tarifs.rb:111:in'
You are calling a mehtod full_description on a nil. The problem lays in those methods:
def show_current_description
puts " Current tarif is: " + find_tarif_in_tarifs(#user.choosed_tarif).full_description
end
def find_tarif_in_tarifs(reference)
#tarifs[reference]
end
Youl call full_description on a result of find_tarif_in_tarifs(#user.choosed_tarif). i.e. on #tarifs[#user.choosed_tarif]. Apparently there is not tariff registered for this user in #tarifs hash.
You have to handle this case - either by checking if returned value is not nil,
def show_current_description
if find_tarif_in_tarifs(#user.choosed_tarif)
puts " Current tarif is: " + find_tarif_in_tarifs(#user.choosed_tarif).full_description
else
puts "No tariff found"
end
or by returning a default value from find_tarif_in_tarifs
def show_current_description
#tarifs.fetch(references, default_tariff) #(you need to define default_tariff method)
end
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
kindly give an example, i am new to return number of ingredients. new to ruby and having prob with syntax.
class Recipe
include Enumerable
attr_reader :name
def initialize(name, cuisine, ingredients, steps)
#name = name
#cuisine = cuisine
#ingredients = ingredients
#steps = steps
end
def name
#name
end
def cuisine
#cuisine
end
def ingredients
#ingredients
end
def steps
#steps
end
def display
puts "Recepie Manager"
puts "\n Name:", #name, "\n Cuisine:", #cuisine ,"\n Ingredients and Quantity:", #ingredients, "Steps:", #steps
end
def how_many_ingredients
puts "Number of Ingridents:" , #ingredients
end
end
obj1 = Recipe.new('Briyani', 'Indian','chicken,rice,spices,blah,blah','cook and eat')
obj1.display
From the code it looks like you have misplaced your end statement. You have to create objects of class after it ends.
class Receipe
include Enumerable
attr_reader :name
def initialize(name, cuisine, ingredients, steps)
#name = name
#cuisine = cuisine
#ingredients = ingredients
#steps = steps
end
def name
#name
end
def cuisine
#cuisine
end
def ingredients
#ingredients
end
def steps
#steps
end
def display
puts "Recepie Manager"
puts "\n Name:", #name, "\n Cuisine:", #cuisine ,"\n Ingredients and Quantity:", #ingredients, "Steps:", #steps
end
def how_many_ingredients
puts "Number of Ingridents:" , #ingredients
end
end
obj1 = Receipe.new('Briyani', 'Indian','chicken,rice,spices,blah,blah','cook and eat')
obj1.display
Thanks,
I used my guru "Google" for finding solution. For returning the ingredients count i used split and count methods.
def how_many_ingredients
ingredients = #ingredients.split(",")
puts "Number of Ingridents:" , ingredients.count
end
you can use gets.chomp to get input from users if thats what you mean
class Receipe
include Enumerable
attr_reader :name
def initialize(name, cuisine, ingredients, steps)
#name = name
#cuisine = cuisine
#ingredients = ingredients
#steps = steps
end
def name
#name
end
def cuisine
#cuisine
end
def ingredients
#ingredients
end
def steps
#steps
end
def display
puts "Recepie Manager"
puts "\n Name:", #name, "\n Cuisine:", #cuisine ,"\n Ingredients and Quantity:", #ingredients, "Steps:", #steps
end
def how_many_ingredients
puts "Number of Ingridents:" , #ingredients
end
end
yourname = gets.chomp
yourcuisine = gets.chomp
youringridents = gets.chomp
yoursteps = gets.chomp
obj1 = Receipe.new(yourname, yourcuisine,youringridents,yoursteps)
obj1.display
#!/usr/bin/env ruby
# this is the data I have
#data = {
:student => {
:id => '123477',
:first_name => 'Lazlo',
:last_name =>'Fortunatus',
:email=>'Lazlo#fortunatus.org'
},
:contact_info => {
:telephone=>'1 415 222-2222',
:address => '123 Main St',
:city =>'Beverly Hills',
:state=>'California',
:zip_code=>90210,
:social_security_number =>'111-11-1111'
}
}
class Student
# not fully implemented - this is what I need help on.
def get_id_original
# I need this to return the value #data[:student][:id]
end
def get_city_original
# I need this to return the value #data[:contact_info][:city]
end
end
s = Student.new
# this is the original method
# how can I access the #data variable here I tried #data[:student][:id] doesnt work
# I realize that data is outside of the scope of this method. However, is there any way!
s.get_id_original
# My goal is to have a singleton method that acts exactly like get_id_original,
# but get_id_original doesn't work.
def s.id
get_id_original
end
It can be done!
It didn't at first work because #data is an instance attribute of the top level object, so even though Student is derived from Object the attribute isn't in the new instance.
But you can pass self into s.id, and so then the only thing you need to add is an accessor for the data attribute.
However, that's slightly tricky because attr_reader et al are private class methods so you can't use them directly, and you can't (because it's private) just say self.class.attr_reader, you have to open up Object and do it...with these changes your program works...
#data = { :student => { :id => '123477'} }
class Student
end
s = Student.new
def s.id o
o.data[:student][:id]
#how can I access the #data variable here I tried #data[:student][:id] doesnt work
#I realize that data is outside of the scope of this method. However, is there any way!
end
class Object
attr_reader :data
end
puts s.id self
First off, your id method actually has to go into the class.
You could try something like this:
#data = { :student => { :id => '123477'} }
class Student
attr_accessor :id
def initialize(student)
self.id = student[:id]
end
end
s = Student.new(#data[:student])
puts s.id
#!/usr/bin/ruby
#data = { :student => { :id => '123477', :first_name => 'Lazlo', :last_name =>'Fortunatus', :email=>'Lazlo#fortunatus.org' }, :contact_info => { :telephone=>'1 415 222-2222', :address => '123 Main St', :city =>'Beverly Hills', :state=>'California', :zip_code=>90210, :social_security_number =>'111-11-1111' } }
class Student
def initialize( data )
#data = data
end
def get_id_override
#data[:student][:id]
end
def get_first_name_override
#data[:student][:first_name]
end
def get_last_name_override
#data[:student][:last_name]
end
def get_email_override
#data[:student][:email]
end
def get_telephone_override
#data[:contact_info][:telephone]
end
def get_city_override
#data[:contact_info][:city]
end
def get_state_override
#data[:contact_info][:state]
end
def get_zip_code_override
#data[:contact_info][:zip_code]
end
def get_social_security_number_override
#data[:contact_info][:social_security_number]
end
end
s = Student.new(#data)
def s.id
get_id_override
end
def s.first_name
get_first_name_override
end
def s.last_name
get_last_name_override
end
def s.email
get_email_override
end
def s.contact_info
get_telephone_override
end
def s.city
get_city_override
end
def s.state
get_state_override
end
def s.zipcode
get_zip_code_override
end
def s.ssn
get_social_security_number_override
end
puts s.id
puts s.first_name
puts s.last_name
puts s.email
puts s.contact_info
puts s.city
puts s.state
puts s.zipcode
puts s.ssn
Here is the answer after some work. Anyone has a better answer than mine let me know.
You really should be passing in the data object so it s has its own reference to it.
#data = { :student => { :id => '123477'} }
class Student
attr_accessor :data
def initialize(data)
#data = data
end
end
s = Student.new(#data)
# or you can use s.data = #data
def s.id
#data[:student][:id]
end
puts s.id
A word of caution. Any modifications to #data in the outermost scope will be reflected in s, because both #data variables point to the same object in memory.
But what if you don't want to modify the Student class? You can just add the accessor to s:
#data = { :student => { :id => '123477'} }
class Student
end
s = Student.new
class << s
attr_accessor :data
end
def s.id
#data[:student][:id]
end
s.data = #data
puts s.id
This code does the equivalent of your own answer, with some improvements. (Only by reading that did I realize what you were trying to accomplish.) To avoid being overly complex, I tried to avoid dynamically generating method names.
#!/usr/bin/env ruby
require 'forwardable'
#data = {
:student => {
:id => '123477',
:first_name => 'Lazlo',
:last_name =>'Fortunatus',
:email=>'Lazlo#fortunatus.org'
},
:contact_info => {
:telephone=>'1 415 222-2222',
:address => '123 Main St',
:city =>'Beverly Hills',
:state=>'California',
:zip_code=>90210,
:social_security_number =>'111-11-1111'
}
}
class ContactInfo
def initialize( data )
#data = data
end
def get_telephone_override
#data[:telephone]
end
def get_city_override
#data[:city]
end
def get_state_override
#data[:state]
end
def get_zip_code_override
#data[:zip_code]
end
def get_social_security_number_override
#data[:social_security_number]
end
end
class Student
extend Forwardable # enables delegation (see ruby-doc.org's standard library)
# delegates multiple methods to #contact_info, so they can be called on Student.
# Remember to have the leading colon.
def_delegators :#contact_info,
:get_telephone_override,
:get_city_override,
:get_state_override,
:get_zip_code_override,
:get_social_security_number_override
def initialize( data )
#data = data[:student]
# this is an example of composing objects to achieve separation of concerns.
# we use delegators so ContactInfo methods are available on the Student instance.
#contact_info = ContactInfo.new(data[:contact_info])
end
def get_id_override
#data[:id]
end
def get_first_name_override
#data[:first_name]
end
def get_last_name_override
#data[:last_name]
end
def get_email_override
#data[:email]
end
end
s = Student.new(#data)
class << s
alias_method :id, :get_id_override
alias_method :first_name, :get_first_name_override
alias_method :last_name, :get_last_name_override
alias_method :email, :get_email_override
alias_method :contact_info, :get_telephone_override
alias_method :city, :get_city_override
alias_method :state, :get_state_override
alias_method :zipcode, :get_zip_code_override
alias_method :ssn, :get_social_security_number_override
end
puts s.id
puts s.first_name
puts s.last_name
puts s.email
puts s.contact_info
puts s.city
puts s.state
puts s.zipcode
puts s.ssn
I think your question would've been clearer if you posted the code as you wanted it to work. I'm going to suggest an edit.
Should you be defining an instance variable (prefixed by "#") outside of a class definition?
Also, you can't define a method with a period in the name