Ruby program doesn't save hash changes - ruby

My source code:
books = {
Harry_Potter: 5,
Steve_Jobs: 10
}
def finder(bookName)
books.each {
|n| if n == bookName
puts "Are you sure you want to #{choice} #{n}?"
confirmAction = gets.chomp
if confirmAction == "yes"
case choice
when "update"
puts "Enter the new name:"
newName = gets.chomp.to_sym
books[newName.to_sym] = books.delete(n)
puts "Update the rating for #{newName}:"
newRating = gets.chomp.to_i
books[newName.to_sym] = newRating.to_i
puts "Successfully updated #{newName} with rating of #{newRating}"
when "delete"
books.delete(n)
else puts "Invalid option!"
end
else puts "Invalid book name."
end
end
}
end
puts "What would you like to do?\n[Add] [Update] [Delete] [View]"
action = gets.chomp.capitalize
case action
when "Add"
puts "Enter the new book name:"
title = gets.chomp.to_sym
puts "Please rate the book [1-10]:"
rating = gets.chomp.to_i
books[title.to_sym] = rating.to_i
puts "Successfully added #{title} with rating of #{rating}"
puts books
when "Update"
choice = "update"
puts "Enter the name of the book:"
bookName = gets.chomp.to_sym
finder(bookName)
when "Delete"
choice = "delete"
puts "Enter the name of the book:"
bookName = gets.chomp.to_sym
finder(bookName)
when "View"
choice = "view"
puts books.each {
|k, v| puts "#{k}: #{v}"
}
end
Whenever I use add option and add something it works. But once I exit and re-open the program, it doesn't show books that I've added using the add option, it returns to the default list.
I need Ruby to save all the changes permanently.

You have to save your objects yourself, e.g. using YAML:
require 'yaml'
File.write('data.yml', YAML.dump(books))
The contents of "data.yml" will be:
---
:Harry_Potter: 5
:Steve_Jobs: 10
To read the file use:
books = YAML.load(File.read('data.yml'))
#=> {:Harry_Potter=>5, :Steve_Jobs=>10}

Well, you could use Maglev which is a ruby interpreter based on the GemStone/S Object Server which will be able to store your books persistently (by setting a reference to your books Hash and Maglev.commit_transaction). However this might be a bit of an overkill for your purposes :-)

Related

Codeacademy - a night at the movies (ruby). Enabling the user to find movies without being case sensitive

I've just completed the Ruby CodeAcademy task, 'night at the movies' (code below).
When searching for existing movies in the database, I want the user to be able to input the title (in this case only StarWars or Divergent) without it having to be case sensitive? At the moment a search of 'starwars' returns an error (as opposed to StarWars which doesn't)!
Thanks
movies = {
StarWars: 4.8,
Divergent: 4.7
}
puts "What would you like to do? "
choice = gets.chomp
case choice
when "add"
puts "What movie would you like to add? "
title = gets.chomp.downcase
if movies[title.to_sym].nil?
puts "What rating does the movie have? "
rating = gets.chomp
movies[title.to_sym] = rating.to_i
else
puts "That movie already exists! Its rating is #{movies[title.to_sym]}."
end
when "update"
puts "Please enter movie title."
title = gets.chomp.to_sym
if movies[title].nil?
puts "This title doesn't exist!"
else
puts "what's the new rating for this movie (0 to 4)"
rating = gets.chomp
movies[title.to_sym] = rating.to_i
puts "#{title} has been updated with a rating of
#{rating}."
end
when "display"
movies.each do |movies, rating|
puts "#{movies}: #{rating}"
end
when "delete"
puts "Please enter movie title:"
title = gets.chomp.to_sym
if movies[title.to_sym].nil?
puts "Title doesn't exist"
else
movies.delete(title)
puts "#{title} has been deleted!"
end
else
puts "Error!"
end
You could use lowercase symbols
movies = { starwars: 4.8, divergent: 4.7}
and then turn the users input into all lowercase letters
title = gets.chomp.downcase.to_sym
this would turn any variance in capitalization of the titles (STARwars, StARwarS, ect...) into starwars

Ruby - Create a class with a file as a variable - possible?

I need to create a program in which the user can take different tests. As i dont want to copy paste my code all over for every test, i have tried to setup a class for that purpose - but i have problems with this class.
Error message = undefined variables or method in 'display_test'
I have predefined som test as a .txt file
I want to choose the file in the class depending on what the user answer - is that possible?
Class code:
class Test
#correct_answers = 0
def display_question( question, options, answer )
puts question
options.each_with_index { |option, idx| puts "#{ idx + 1 }: #{ option
}" }
print 'Answer: '
reply = gets.to_i
if answer == reply
puts 'Correct!'
#correct_answers += 1
puts "#{#correct_answers}"
else
puts 'Wrong. The correct answer was: ' + answer.to_s
end
end
def display_test()
f = File.new(userinput, 'r')
while ! (f.eof?) #logikken til at splitte
line = f.gets()
question = line.split("|")
question[1] = question[1].split(";")
display_question question[0], question[1], question[2].to_i
end
end
display_test
puts "________________________________________________________"
puts "Total score:"
puts "You've got" + " #{#correct_answers}" + " correct answers!"
Before hand i have used ("geografitest.txt") instead of username in the File.new so it looked like this:
f = File.new('geografitest.txt','r')
But now i am trying to let the user decide what test to take.
I am very new to ruby, so please bear with me.
I have tried to do it this way, which obviously is not working.
puts "Which test do you want to take?"
select = 0
while (select != 3)
puts "Press 1 to take Geografi test."
puts "Press 2 to take Math test."
puts "Press 3 to take Religion test."
puts "Press 3 to exit"
select = gets.chomp.to_i
if (select == 1)
gets.chomp = userinput
userinput =`geografitest.txt`
echo $userinput
end
if (select == 2)
gets.chomp = userinput
userinput =`matematiktest.txt`
echo $userinput
end
if (select == 3)
gets.chomp = userinput
userinput =`religionstest.txt`
echo $userinput
end
if (select > 4)
puts "Not a correct selection"
elsif (select == 4)
puts "Goodbye"
end
end
abort
So my questions is now;
How can i make the user choose what test to take? Can i make a variable instead of the textfile as i have tried, but in a different way? Or is there a smarter way?
And in what way is my class wrong and how do i fix it? I know its not the way to make it, but i simple cant get my head around how to make it right.
Please help a rookie out.
Cheers!
You can pass file as dependency to you Test class based on user input with object constructor. Something like this
class Test
attr_reader :correct_answers_count
def initialize(file)
#file = file
#correct_answers_count = 0
end
#other code goes here
end
loop do
case user_input = gets.chomp
when '1'
file_name = 'some_file1'
when '2'
file_name = 'some_file1'
when '3'
break
else
puts 'wrong variant'
end
test = Test.new(File.new(file_name, 'r'))
test.display
end

How to search in new files and print the added information?

I'm having a problem in my ruby code. My project is to:
Make a new file and put the data inside and than search:
1. Enter a new employee (number, name, date of birth).
2. Search for an employee by number. (in the database)
3. To exit press 3
This is the code I have, but the terminal won't run it.
class Person
def setNumber(number)
#number=number
end
def setInfo(name)
#name=name
end
def getInfo()
return #name
end
def setInf(dateofBirth)
#dateofBirth=dateofBirth
end
def getInf()
return dateofBirth
end
puts "Companies employees"
select = 0
while(select!=3)
puts "Press 1 to Enter information about new employee"
puts "Press 2 to Search for employee by number"
puts "Press 3 to exit"
select=gets.chomp.to_i
if(select == 1)
f=File.new("Repository.txt","a+")
puts "Please enter employee number:"
number = gets.chomp.to_s
f.puts (number)
person = Person.new
person.setInfo(number)
puts "Please enter the employee name:"
name = gets.chomp.to_s
f.puts (name)
set_name = Person.new
set_name.setInfo(name)
puts "Please enter the employees date of birth:"
dateofBirth = gets.chomp.to_s
f.puts (dateofBirth)
dateofBirth = Person.new
dateofBirth.setInfo(dateofBirth)
f.close()
elsif(select==2)
puts "Which employee number would you like to search for?"
search = gets.chomp
f = File.open("Repository.txt","r")
while !(f.eof?)
line.include?(search)
end
f.close()
elsif(select > 3)
puts "Incorrect selection."
elsif(select == 3)
puts "Goodbye!"
end
end
This looks incredibly similar to someone else who asked a similar question yesterday. Maybe a school project going around.
You might check the code here: I trying to make a code that gives the user a personal number after they have made an user

Deleting a row from a CSV file with Ruby

I've seen similar answers to this question but I think I need something more specific to my code. Basically I've called the row from the CSV file but now I need to delete the called row. Sounds simple as I write this yet here I am asking you all for help. I know there is a lot of code here but I figured the more there is the more easier you will be able to understand the context. Apologies if there is too much noise in the code.
def delete_user_menu
puts "============================================"
delete_users_active_list
puts " "
puts "Please type in the name of the user you wish to eradicate: "
print "> "
eradicate(gets.chomp)
end
def eradicate(delete_input)
delete_row = delete_authentication(delete_input)
if delete_row
puts "Are you sure you want to delete #{delete_input} from the database?"
puts "[y]es or [n]o"
print "> "
delete_answer = gets.chomp
if delete_answer == "y"
delete_user
after_deletion_menu
elsif delete_answer == "n"
puts "Close call! Taking you back to main menu."
sleep 2
admin_main_menu
else
puts "Input not recognised. Please try again."
eradicate(delete_input)
end
else
puts "User not recognized. Please try again."
sleep 1
delete_user_menu
end
end
def delete_user
# this is where the delete user function needs to go
after_deletion_menu
end
def after_deletion_menu
puts " "
puts "User deleted! What would you like to do now?"
puts "1. Delete another user"
puts "2. Back to main menu"
print "> "
after_deletion_choice = gets.chomp
if after_deletion_choice == "1"
delete_user_menu
elsif after_deletion_choice == "2"
admin_main_menu
else
puts "Input not recognized. Please try again."
after_deletion_menu
end
end
def delete_users_active_list
CSV.foreach("./users.csv", headers: true) do |row|
username = row['username']
puts "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
puts "Username: #{username}"
end
end
def delete_authentication(username)
CSV.open('users.csv', headers: true).find { |row| row['username'] == username }
end
I've had a look at this question How to remove a row from a CSV with Ruby
but I don't fully understand the answers, hence why I'm here. Any help is much appreciated.
I looked at the link. First, they are reading the entire csv file into table:
table = CSV.table(#csvfile)
then deleting the row from table:
table.delete_if do |row|
row[:foo] == 'true'
end
Finally, they are completely replacing the original file with the new table minus the row:
File.open(#csvfile, 'w') do |f|
f.write(table.to_csv)
end
This is generally how you have to do this kind of operation when you are dealing with a file. It's not like a database.
EDIT - in your case:
delete_user(delete_input)
...
def delete_user(user)
...
table.delete_if { |row| row[:username] == user }
...

How would I access a key from a hash that is within an array that is within a text file in ruby?

I'm trying to create an if/else statement that compares a user's input against all the name keys within a hash that's stored with a text file. How would I write this?
user_accts is the array.
Update of full if/else statement:
elsif choice == "2"
puts "====== NEW CUSTOMER ======"
puts "Choose a username:"
prompt; login_name = gets.chomp
#file.open("cust_accts.txt", "r")
#if #user_accts.map { |acct| acct["name"]}.include?(login_name)
if #user_accts.any? {|acct| acct["name"] == login_name }
puts "Sorry, that username has already been taken"
elsif
puts "Choose a password:"
prompt; login_password = gets.chomp
user_accts << create_account(login_name, login_password)
File.open("cust_accts.txt", "a") { |file| file.puts(user_accts)}
end
original if/else statement:
if login_name == #??? #user_accts.has_key?(login_name) ???
puts "Sorry, that username has already been taken"
elsif
puts "Choose a password:"
prompt; login_password = gets.chomp
user_accts << create_account(login_name, login_password)
File.open("cust_accts.txt", "a") { |file| file.puts(user_accts)}
end
This is exactly what is inputted to the cust_accts.txt file using this command:
user_accts << create_account(login_name, login_password)
File.open("cust_accts.txt", "a") { |file| file.puts(user_accts)}
cust_accts.txt
{"name"=>"Tom", "password"=>"popcorn", "balance"=>0}
{"name"=>"Avril", "password"=>"chain", "balance"=>0}
It's not quite clear what your starting point is.
Assuming you have parsed your text file into #user_accts, so you have:
#user_accts = [{"name"=>"Tom", "password"=>"popcorn", "balance"=>0},
{"name"=>"Avril", "password"=>"chain", "balance"=>0}]
Then you would want to do:
if #user_accts.map {|acct| acct["name"]}.include?(login_name)
puts "Sorry, that username has already been taken"
else
# ...
end
#user_accts = [{"name"=>"Tom", "password"=>"popcorn", "balance"=>0},
{"name"=>"Avril", "password"=>"chain", "balance"=>0}]
if #user_accts.any? {|acct| acct["name"] == "Tom" }
puts "Sorry, that username has already been taken"
else
# ...
end
#=> Sorry, that username has already been taken
It seems like your problem is "How do I store objects in ruby for use at a later date?". If you want to use a text file you need to put it into a format that can be parsed back into a ruby object like JSON or XML. I would recommend using a database though. A database like SQLite is very light weight and easy to learn. An advantage to using a DB is that when you use a DB you can set an option to have your results returned as a hash.
I had a similar problem, eventually decided that a DB was the way to go.

Resources