Allowing a user to edit a CSV file - ruby

In Ruby 3.1.2, using this CSV file:
make,model,color,doors,email
dodge,charger,black,4,practice1#whatever.com
ford,focus,blue,5,practice2#whatever.com
nissan,350z,black,2,practice3#whatever.com
mazda,miata,white,2,practice4#whatever.com
honda,civid,brown,4,practice5#whatever.com
corvette,stingray,red,2,practice6#whatever.com
ford,fiesta,blue,5,practice7#whatever.com
bmw,m4,black,2,practice8#whatever.com
audi,a5,blue,2,practice9#whatever.com
subaru,brz,black,2,practice10#whatever.com
lexus,rc,black,2,practice11#whatever.com
If the user does not enter an email address that is part of the CSV file above or anything that does not equal an email, the program asks the user to enter a valid email until they actually do, and then update the CSV file with the deleted row corresponding to the email. My current code does this but there is no checking if the user enters a valid email address or not and that is the part I am stuck on. This is my code:
def delete_from_data
print "\nTo delete a car, enter the email (this is case sensitive): \n> "
delete_car = gets.chomp
#this allows me to delete a certain row based on email
table = CSV.table("cars.csv")
table.delete_if do |row|
row[:email] == delete_car
end
File.open("cars.csv", "w") do |f|
f.write(table.to_csv)
end
#this shows the updated student roster after deleting user
puts "\nThis is the updated roster after deleting: #{delete_car}"
end
for example, if the user enters "practice11#whatever.com", then that whole row from the CSV file gets deleted. But if a user enters anything else other than a valid email address from the CSV file, it still runs.

You should read the documentation of CSV::Table and notice that it has a #each method that allows you to iterate over the rows. In fact, lots of Ruby container classes have a #each method so you will need to get familiar with it if you want to do anything meaningful in Ruby.
Something like this should work:
email_found = false
while !email_found
email = gets.chomp
table.each do |row|
if row[:email] == email
email_found = true
end
end
end
Note that this is not meant to be beautiful or optimized code, but simple, beginner-level code.
For extra credit, learn about Ruby's Enumerable module and use .any? instead of .each.

Related

Ruby: How to make program return nil if there is nothing in location of table

In Ruby I have this csv file:
make,model,color,doors,email
dodge,charger,black,4,practice1#whatever.com
ford,focus,blue,5,practice2#whatever.com
nissan,350z,black,2,practice3#whatever.com
mazda,miata,white,2,practice4#whatever.com
honda,civid,brown,4,practice5#whatever.com
corvette,stingray,red,2,practice6#whatever.com
ford,fiesta,blue,5,practice7#whatever.com
bmw,m4,black,2,practice8#whatever.com
audi,a5,blue,2,practice9#whatever.com
subaru,brz,black,2,practice10#whatever.com
lexus,rc,black,2,practice11#whatever.com
and this is my code:
def delete_from_data
print "\nTo delete a car, enter the email (this is case sensitive): \n> "
delete_car = gets.chomp
#this allows me to delete a certain row based on email
table = CSV.table("cars.csv")
table.delete_if do |row|
row[:email] == delete_car
end
File.open("cars.csv", "w") do |f|
f.write(table.to_csv)
end
#this shows the updated student roster after deleting user
puts "\nThis is the updated roster after deleting: #{delete_car}"
end
Having this code, how can I make it if the user enters an invalid email (misspelled email or anything that doesn't match an email from the CSV file), it asks to enter a valid one since the one they typed isn't an email from the CSV file
Load table first.
Now you can use #any? to see if any rows contain that email address.
table.any? { |row| row[:email] == delete_car }
Now, just do this in a loop, breaking the loop and returning the input email when the input email address is in the table.
delete_car = loop do
email = gets.chomp
break email if table.any? { |row| row[:email] == email }
puts "Invalid input. Try again."
end
You may also wish to generate a set of valid email addresses. If you think you'll need to loop several times, this may lead to better performance.
valid_emails = table.map { |row| row[:email] }.to_set
delete_car = loop do
email = gets.chomp
break email if valid_emails.include?(email)
puts "Invalid input. Try again."
end

How do I access submitted params in a rails mailer?

I had a previous question that helped me loop through all users where a certain question is met.
However, I'm realizing I can't hard code that condition. I need to somehow get that data from the submitted form, which doesn't seem to be possible in the mailer.
In other words, I'm trying to loop through all users where the user's state is equal to the home_state of the candidate being entered. Basically when the candidate is created, I want to get the home_state of that candidate, and then loop through all users, and for each user that has same state as that candidate, I want to send them the email via this mailer.
Here's my candidate_mailer.rb file
class CandidateMailer < ApplicationMailer
default from: 'wesleycreations#gmail.com'
def self.send_request(row)
#candidate = Candidate.new(candidate_params) # if I can access this here, how to I create the
# following array?
emails = []
User.where(state: #candidate.home_state).each do |u|
emails << u.email # To insert the user email into the array
end
emails.each do |email|
new_request(email,row).deliver_now
end
end
def new_request(email, row)
#candidate = row
mail(to: email, subject: 'New request')
end
end
But the
#candidate = Candidate.new(candidate_params)
obviously doesn't work because the params aren't available in the mailer.
Here in the candidates_controller.rb I have this
def create
#candidate = Candidate.new(candidate_params) #of course here I can access params
if #candidate.save
row = #candidate
CandidateMailer.send_request(row)
else
render('new')
end
end
SO the question is, how do I access params in rails mailer? And if I can't, then how do I refactor my code so that the lines that check if the user meets certain condition is done in the controller?
I was able to figure this out by doing this. after I saved the candidate, I saved the candidate to a global variable. and THEN I send the mailer.
def create
#candidate = Candidate.new(candidate_params)
if #candidate.save
row = #candidate
$candidate = #candidate
end
CandidateMailer.send_request(row)
else
end
end
This way the mailer had access to the new candidate that been created, and I was able to check my condition in there.
So in my mailer, when I use $candidate.home_state, it returned the correct state, mail went out, and made me very happy :)
emails = []
User.where(state: $candidate.home_state).each do |u|
emails << u.email # To insert the user email into the array
end

Checking if a record exists in Sinatra/DataMapper

I currently generate a user's profile page using their serial ID, like so:
get '/users/:id' do
#user = User.get(params[:id])
end
This works great, until a number is entered that doesn't exist in the database.
I'm aware I can change User.get to User.get! to return an ObjectNotFoundError error if the record isn't found, but I'm not sure how I can use this to my aid.
I used to use .exists? when using RoR.
Any suggestions?
Thanks in advance!
Edit: I'm going to leave the question unanswered, as I haven't actually found a solution to what I asked in the title; however, I did manage to solve my own problem by checking to see if the :id entered is higher than the amount of users that exist in the database, like so:
if params[:id].to_i > User.count
"This user does not exist."
else
#users_id = User.get(params[:id])
erb(:'users/id')
end
You already have the correct code:
#user = User.get(params[:id])
If no user exists with the given id, then #get will return nil. Then you can just do a conditional:
#user = User.get params[:id]
if #user
# user exists
else
# no user exists
end
This is a very common pattern in Ruby which takes advantage of the "truthiness" of anything other than false or nil. i.e. you can say if 0 or if [] and the condition will evaluate to true
You can recreate a .exists? method:
class User
def self.exists?(id_or_conditions)
if id_or_conditions.is_a? Integer
!! User.get id_or_conditions
else
!! User.first id_or_conditions
end
end
end
#get is similar to #find in rails, except it doesn't raise an error if the record is not found. #first is similar to #find_by in rails.

How can I use arrays to loop through a collective set of variables I read from a file?

I'm trying to make a loop so that during each loop it will take the name and password variables from the file and enter where called.
array = []
File.open("file_users.txt") do |login|
login.each do |item|
name, password = item.chomp.split(',')
array << "#{name}" "#{password}"
browser.goto "https://website.com"
browser.text_field(:id => "user_name").set "#{name}"
browser.text_field(:id => "user_password").set "#{password}"
browser.button(:id => "login").click
sleep(5)
browser.close
end
end
I think the main issue is trying to make the loop call the next set of email and password after using the previous ones.
*edited:
The result I'm trying to get is to pull text from a file, then give it a "name" and "password" value, then have it be entered into the text field on the browser when called...
for example, the text file looks like:
jerryname
jerrypassword
careyname
careypassword
britneyname
britneypassword
The result I want is:
#=> loop 1
puts jerrynamme
puts jerrypassword
#=> logs in
#=> waits, then closes browser
#=> loop 2
puts careyname
puts careypassword
#=> logs in
#=> waits, then closes browser... and so on.
The result I get is the browser opening and the name first being entered then the code just stops....the browser doesn't close, it just remains still.
You say:
the text file looks like:
jerryname jerrypassword careyname careypassword britneyname britneypassword
If the file_users.txt file is as you describe, one line with a list of names and password pairs separated by spaces, the line
File.open("file_users.txt") do |login|
will return the entire contents of the file in first login value. The code
name, password = item.chomp.split(',')
will assign the entire contents of the file to name and set password to nil
You need to build a new "file_users.txt" file in the following form (note the commas and line breaks):
jerryname,jerrypassword
careyname,careypassword
britneyname,britneypassword
Then your code will be closer to working.
PS, this line doesn't seem to be used for anything and can be removed.
array << "#{name}" "#{password}"

Read from a file and Update

i have a text file called text.txt with stored text like so
713,socks,3.99
888,hat,12.99
634,shirt,7.99
I want to open my file and choose a item number then i want to update the description and price. and save that back to the file.
heres my code so far
puts "Whats's the item number of the product you wish to update?"
item_num = gets.chomp
puts 'Enter new products description. '
new_description = gets.chomp
puts 'Enter new products price. '
new_price = gets.chomp.to_s
open('text.txt', 'r+') { |update|
update.puts "#{item_num},#{new_description},#{new_price}"
}
end
all this is doing is adding a new product with the same item number.
The easiest way to achieve a goal is to use CSV class to operate your file content. After all, the content is csv, right?
So, instead of:
open('text.txt', 'r+') { |update|
update.puts "#{item_num},#{new_description},#{new_price}"
}
you might want to search for an element inside loaded array, update it and then write the content back to file:
require 'csv'
items = CSV.read("text.txt")
items.map! { |i|
next i unless i.first == item_num # skip unless it’s our item
i[1] = new_description
i[2] = new_price
i
}
# here should go the code whether you need to insert line if `num` is not found
CSV.open("text.txt", "wb") do |csv|
items.each { |i| csv << i }
end
This is definitely not a best production-quality code, the intent was to demonstrate how it’s to be done in general. Hope it helps.

Resources