How to create a user defined exception in Ruby from user input - ruby

I am trying to create a user defined error where, if the user enters their first name only, it raises an error and tells the user to try again or, if they enter a numeral, it will raise an error.
This is my code but when I run it it outputs "Enter your first and last name" then it rescues it no matter if I enter a full name. It still says "Sorry I didn't quite catch that." then concatenates it:
class MyNewException < Exception
attr_reader :first, :last
def initialize(first)
#first = first
end
end
print "Enter your first and last name: "
begin
first = gets.chomp
last = gets.chomp
#prompt the user to enter first and last name
rescue MyNewException
puts "Sorry I didn't quite catch that. Try again."
gets
else
puts "Hello, " + first + last + "!"
end

gets will grab everything you type before pressing Enter. So first actually includes the first and last name that you are entering, and then last is empty. It looks like they are being concatenated, but all the text is in first. If you enter your first name, then press Enter, then enter your second name, then it should output both names.
The error is not being raised. You need to add something like this if you want to raise the error when last is blank:
last = gets.chomp
if last == ""
raise MyNewException, first
end

Related

How to convert array into strings?

I have an output as such:
require_relative "array_sort"
arraystudents = []
puts "Type a student name:"
name = gets.chomp
arraystudents.push(name)
while name != ""
puts "Type another student name or press enter to finish:"
name = gets.chomp
arraystudents.push(name)
end
puts "Congratulations! Your Array has #{arraystudents.count} students: #{array_sort(arraystudents)}"
Every time I run it, I end up with an array output rather than just having the names under string form.
Is there a way where the output can be along the lines of: x, y and z? Rather than array, or just commas separating each name?
On another note, I seem to also have a blank [0] at the beginning of my array.
Here's on the array_sort.rb file.
def array_sort(students)
return students.sort.join(",")
end
Any help would be appreciated.
You're getting an extra blank entry at the end of arraystudents. arraystudents.sort makes it look at it's at the front.
The problem is because you check if name is empty, then you ask for a name and push it into arraystudents. Ask, push, then check. Instead you need to ask, check, then push.
arraystudents = []
loop do
puts "Type another student name or press enter to finish:"
name = gets.chomp
break if name == ''
arraystudents.push(name)
end
loop is an infinite loop. break lets you exit a loop.
Is there a way where the output can be along the lines of: x, y and z?
Yes. First handle the case when you have 0 or 1 students. This avoids results like and Zara.
When there's multiple students: Sort them. Remove the last student. Join the rest. Then concatenate them.
def display_students(students)
case students.size
when 0, 1
return students.first
else
sorted_students = students.sort
last = sorted_students.pop
return "#{sorted_students.join(', ')} and #{last}"
end
end
There may be more elegant ways, but it gets the job done.
You are inserting an element of the array also on the last iteration
i suggest doing something like this to avoid the last element of the array
arraystudents = []
puts "Type a student name:"
name = gets.chomp
arraystudents.push(name)
loop do
puts "Type another student name or press enter to finish:"
name = gets.chomp
break if name == ""
arraystudents.push(name)
end
puts "Congratulations! Your Array has #{arraystudents.count} students: #{arraystudents.sort.join(',')}"
The codes outputs
Type a student name:
1
Type another student name or press enter to finish:
2
Type another student name or press enter to finish:
3
Type another student name or press enter to finish:
Congratulations! Your Array has 3 students: 1,2,3
If you want to have the "and" you can do
arraystudents = []
puts "Type a student name:"
name = gets.chomp
arraystudents.push(name)
# puts arraystudents.join(',')
loop do
puts "Type another student name or press enter to finish:"
name = gets.chomp
break if name == ""
arraystudents.push(name)
# puts arraystudents.join(',')
end
arraystudents = arraystudents.sort
if (arraystudents.count == 1)
puts "Congratulations! Your Array has 1 students: #{arraystudents[0]}"
else
puts "Congratulations! Your Array has #{arraystudents.count} students: #{arraystudents[0..-2].join(',')} and #{arraystudents[-1]}"
end
and the output will be
Type a student name:
3
Type another student name or press enter to finish:
2
Type another student name or press enter to finish:
1
Type another student name or press enter to finish:
Congratulations! Your Array has 3 students: 1,2 and 3

Loop through multiple conditions until all of them are met

I'm using Ruby to get input from a user to provide new names for a list of files. I'm storing the names in an array, but before I store it, I have a series of conditions that I'm looping through to make sure that the user's input is valid. It essentially boils down to this (I've removed parts of the code that aren't relevant to the question):
puts "Rename file to:"
new_name = gets.chomp
new_name = check_input(new_name,#all_names)
#all_names << new_name
def check_input(new_name,all_names)
while new_name.match(/\s/)
puts "Names must not contain spaces:"
new_name = gets.chomp
end
while new_name.empty?
puts "Please enter a name:"
new_name = gets.chomp
end
while all_names.include? new_name
puts "That name already exists. Please enter a different name:"
new_name = gets.chomp
end
return new_name
end
Overall this works pretty well, but I want to make sure to loop through each "while" condition again and again, until all of the conditions are met. If, for instance, a name "abc" already exists, the user follows this order:
enters "abc" => "That name already exists. Please enter a different
name"
enters "a b c" => "Names must not contain spaces"
enters "abc" again =>
The last entry works successfully, but I don't want it to, since it's skipping over the condition that checks for duplicates. Is there a better way to loop through these conditions simultaneously, with each new entry?
Thank you for any help!
Right idea with the loop, just the wrong place for it. You need to check each gets from the user against all possible invalid cases. What you were doing was checking until a single invalid case was passed and then going on to a different one, which didn't check if the previous case(s) still passed:
# outputs an error message and returns nil if the input is not valid.
# Otherwise returns the input
def check_input(input, all_names)
if input.match(/\s/)
puts "Name must not contain spaces:"
elsif input.empty?
puts "Please enter a name:"
elsif all_names.include?(input)
puts "That name already exists. Please enter a different name:"
else
input
end
end
#all_names = ['abc']
puts "Rename file to:"
# keep gets-ing input from the user until the input is valid
name = check_input(gets.chomp, #all_names) until name
#all_names << name
puts #all_names.inspect
Since puts returns nil, check_input will return nil if the input is not valid. Otherwise, in the final else, we'll return the valid input and assign it to the variable name and stop executing the until loop.
Example run:
Rename file to:
abc
That name already exists. Please enter a different name:
a b c
Name must not contain spaces:
abc
That name already exists. Please enter a different name:
abc23
["abc", "abc23"]
Code
def rename_files(fnames)
fnames.each_with_object({}) do |fn,h|
loop do
puts "Rename file '#{fn}' to:"
new_name = gets.chomp
bad_name = bad_name?(new_name, h)
if bad_name
print bad_name
else
h.update(new_name=>fn)
break
end
end
end.invert
end
def bad_name?(new_name, h)
if new_name.include?(' ')
"Names must not contain spaces. "
elsif new_name.empty?
"Names cannot be empty. "
elsif h.key?(new_name)
"That name already exists. Duplicates are not permitted. "
else
nil
end
end
Example
rename_files(["cat", "dog", "pig"])
Rename file 'cat' to:
# <enter "three blind mice">
Names must not contain spaces. Rename file 'cat' to:
# <enter ENTER only>
Names cannot be empty. Rename file 'cat' to:
# <enter "three_blind_mice">
Rename file 'dog' to:
# <enter "four_blind_mice">
Rename file 'pig' to:
# <enter "three_blind_mice?>
That name already exists. Duplicates are not permitted. Rename file 'pig' to:
# <enter "five_blind_mice"
#=> {"cat"=>"three_blind_mice", "dog"=>"four_blind_mice", "pig"=>"five_blind_mice"}
Notes
bad_name? returns a (truthy) message string if the proposed file name is invalid for one of the three specified tests; else nil is returned.
if bad_name? returns a truthy value, it is printed using print, rather than puts, as it will be followed by puts "Rename file '#{fn}' to:" on the same line. The latter message is in part to remind the user which file is being renamed.
Hash.key? is used to determine if a proposed filename is a duplicate of one already entered, in part because hash key lookups are much faster than linear searches used to find array elements.
the new names may include the original names. Care therefore must be taken in renaming the files. (Consider, for example, "f1" changed to "f2" and "f2" changed to "f1".) If none of the original names are to be used as new filenames an additional test must be added to bad_name? (and fnames must be passed as a third argument to that method).
the hash being constructed is inverted (using Hash#invert) as a final step, so that the keys are the original filenames.
This could be a good use for recursion (just showing one of the conditions here, the others are the same structure):
def check_input(new_name,all_names)
# using if instead of while ... recursion provides the 'loop' here
if new_name.match(/\s/)
puts "Names must not contain spaces:"
new_name = check_input(gets.chomp, all_names)
end
# etc, other conditionals
new_name
end
Essentially, none of these new_name assignments will resolve until the input passes all the checks. The program moves deeper into the stack, but everything will resolve as soon as some input passes all the checks.
Yep, recursion is the right way to do something like this I think. Just throw this in a test.rb file and run ruby test.rb:
#all_names = []
def check_name(name = nil)
# Find out if it's invalid and why
invalid_reason = if name.empty?
"Please enter a name:"
elsif name.match(/\s/)
"Names must not contain spaces:"
elsif #all_names.include?(name)
"That name already exists. Please enter a different name:"
end
# Either return the name or ask for it again
if invalid_reason
puts invalid_reason
name = check_name(gets.chomp)
end
# Once we have it return the name!
name
end
puts "Rename file to:"
new_name = check_name(gets.chomp)
puts "Successfully storing name '#{new_name}'..."
#all_names << new_name
Let me know if that's doing what you were looking for!

Ruby validation of name, email, and phone number

After creating the loop to check that the phone number is 10 characters I believe the phone issue is now resolved. Now I'm working with checking the email address, name, and making sure it outputs correctly, and making sure 2 names are entered by the user. Having issues getting the email address to validate and output in the correct format.
NAME_PATTERN = /([\w\-\']{2,})([\s]+)([\w\-\']{2,})/
EMAIL_PATTERN = /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
PHONE_PATTERN = /^(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})$/
def valid_name?(name)
!!name.match(NAME_PATTERN)
end
puts "Enter your first and last name (John Doe): "
while (name=gets)
names = name.split(" ", 2)
if valid_name?(name)
puts "Great, that looks like it could work."
break
else
puts "Please enter first and last name."
end
end
def valid_email?(email)
!!email.match(EMAIL_PATTERN)
end
puts "Enter your email address (joe#info.com): "
while (email = gets)
if valid_email?(email)
puts "Great, that looks like it could work."
break
else
puts "Invalid email address entered. Please try again. "
end
end
def valid_phone?(number)
!!number.match(PHONE_PATTERN)
end
puts "Enter your phone number including area code (numbers only): "
while (number=gets)
if valid_phone?(number)
puts "Great, that looks like it could work."
break
else
puts "Invalid phone number entered. Please try again."
end
end
puts names
puts email
puts number
I suspect you didn't intend to use the assignment operator here:
if (email = /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i)
Try this:
if email =~ /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
You have a similar error in the phone_number method:
if number = 10
I'm not sure what you intended here. Perhaps this?
if number.size == 10
You have more problems, however. Take a look at this loop:
loop do
if number.size == 10
break
else
puts "Invalid phone number entered. Please try again."
end
end
How will the user ever exit this loop if the number is invalid? The value of number never changes.
Here's a Ruby flavoured approach to what you're trying to do:
# Define constants for things that are special and get re-used.
EMAIL_PATTERN = /\A\S+#\S+\z/
# Methods that test something and return true or false often end with
# a question mark (?) to indicate this.
def valid_email?(email)
!!email.match(EMAIL_PATTERN)
end
# Try and keep your support methods separate from the main body of
# your program.
puts "Enter your email address (joe#info.com): "
# This sets up a loop that waits until you get a valid response.
while (email = gets)
email = gets
if valid_email?(email)
puts "Great, that looks like it could work"
break
else
# Note that the message is rendered here, not in the validation
# method, so there's no assumptions about how this method is used.
puts "Invalid email address entered. Please try again. "
end
end
If you try and structure your code this way you'll find it's a lot easier to keep things organized. This is one of the big challenges when learning programming so as not to get overwhelmed.
Regular expressions are great for validating things that conform to a very specific pattern, but try not to get overly confident in the pattern of everyday things. Even the humble IPv4 address comes in a multitude of forms.

how to re-ask a user for input if none was given the first time?

My current code is this:
print "Feed me input."
def get_input
input_value=gets.chomp
if !input_value
print "you didn't type anything"
else
input_value.downcase!
if input_value.include? "s"
input_value.gsub!(/s/,"th")
else
print "You entered a string but it had no 's' letters."
end
end
return input_value
end
get_input()
if !get_input
get_input
else
puts "#{get_input}"
end
I'm not sure why it isn't working. When I run it I get prompted for input then when I press enter after entering none I get the "You entered a string but it had no 's' letters", not the "you didn't type anything" that I wanted.
Every object except false and nil is treated as false if they are used as predicates. Even empty string is treated as true:
s = ""
puts true if s # => true
Use String#empty? to check if it is empty string.
As you said When I run it I get prompted for input then when I press enter after entering none - It means what happened acctually is
input_value="\n".chomp #( you gets methods take only `\n` as input)
"\n".chomp # => ""
so your input_value variable holds and empty string object. Now in Ruby every object has true value, except nil and false. Said that "" is also true,but you did !input_value,which means you are making it false explicitly. That's the reason in the below if-else block, else part has been executed and you didn't see the expected output "you didn't type anything".
if !input_value
print "you didn't type anything"
else
input_value.downcase!
if input_value.include? "s"
#.. rest code.
So I would suggest you in such a context replace the line if !input_value to if input_value.empty?, Which will make your code to behave as you are expecting. I didn't take your logic as a whole,but tries to show you how to code to meet your needs:
print "Feed me input."
def get_input
input_value=gets.chomp
if input_value.empty?
puts "you didn't type anything"
false
else
puts "found value"
input_value.downcase!
end
end
until input = get_input
# code
end
puts input
output
kirti#kirti-Aspire-5733Z:~/Ruby$ ruby test.rb
Feed me input.
you didn't type anything
you didn't type anything
you didn't type anything
HH
found value
hh
kirti#kirti-Aspire-5733Z:~/Ruby$

I need help in ruby

I'm making a software but I don't want to publish it's source code right now because I don't want people to steal my hard work. I'm not rude or anything like that. Below is an example of what the program I'm making looks like.
print "Username : "
name = gets.chomp
print "Password : "
pass = gets.chomp
if name =="user" and pass=="pa$$word"
print "Hello"
else print "Error, incorrect details"
Now this is a simplest login form in ruby but what bad happens here is whenever the user inserts wrong information the program will simply shutdown and what I want to happen is that I want to keep the program asking the user for right information until right information is inserted.
Are you a windows user? Do know how to program in batch files?
examples
echo Hello world
cls
pause
So here is the code for ruby
a. print "Command : "
b. command = gets.chomp
c. if command == "Hello"
d. print "Hi, how are you?"
e. elsif command == "help"
f. print "Say hi to me, chat with me"
now what I want here too is just like in the first question
Details : After the user types in "Hi" the program just shuts down but what I want here it to make the program ask go to line a again
Use a while loop which continually asks for entry and checks the user's submission until a valid result is entered.
username = nil
while username.nil?
puts "What is your username?"
entered_username = gets.chomp
if entered_username == "Bob"
username = entered_username
end
end
puts "Thanks!"
When run on the terminal this produces:
What is your username?
sdf
What is your username?
dsfsd
What is your username?
sdfsd
What is your username?
sdfds
What is your username?
sdf
What is your username?
Bob
Thanks!
1.
until (print "Username : "; gets.chomp == "user") and
(print "Password : "; gets.chomp == "pa$$word")
puts "Error, incorrect details"
end
puts "Hello"
2.
loop do
print "Command : "
case gets.chomp
when "Hello" then print "Hi, how are you?"; break
when "help" then print "Say hi to me, chat with me"; break
end
end
Here is the easy method :) If anyone is stuck with the problem I encountered all you do is this. The "while" loop
number = 1 # Value we will give for a string named "number"
while number < 10 # The program will create a loop until the value
# of the string named "number" will be greater than 10
puts "hello"
# So what we need to do now is to end the loop otherwise
# it will continue on forever
# So how do we do it?
# We will make the ruby run a script that will increase
# the string named "number"'s value every time we run the loop
number = number + 1
end # Now what happens is every time we run the code all the program
# will do is print "hello" and add number 1 to the string "number".
# It will continue to print out "hello" until the string is
# greater than 10

Resources