Ruby Throw-Catch tutorial unclear - ruby

def promptAndGet(prompt)
print prompt
res = readline.chomp
throw :quitRequested if res == "!"
return res
end
catch :quitRequested do
name = promptAndGet("Name: ")
age = promptAndGet("Age: ")
sex = promptAndGet("Sex: ")
# ..
# process information
end
promptAndGet("Name:")
From https://www.tutorialspoint.com/ruby/ruby_exceptions.htm
When executed normally, it goes through name, age, sex, and back to name again, despite the prompt only asking for the name.
Why does this happen instead of "Name" just being asked?

That final line promptAndGet("Name") does not get immediately executed, since it's after the catch block.
The normal flow is that everything within the catch :quitRequested block gets executed immediately, in order. That's why you get all 3 prompts inside. If you answer all 3 prompts, you'll also get to the prompt on the last line.
If you answer ! to any of the three prompts, the block will terminate. So you will not get the remaining prompts inside the block.
You'll still get the prompt on the last line since it's outside of the catch.
throw is what terminates the catch block - not what initiates it.
Also, if you answer ! to that final prompt outside of the catch block, you'll get an error, because the throw was uncaught.

Related

Ruby if/else flow

Good morning,
I'm starting on Ruby, wanted to create a small tool that fetches my public IP and sends it over by email. I'm stumbling on a basic problem with a string comparison and an if/else block that won't process.
Code is quite simple (see below). The problem comes at the string comparison at line 21. What I'd want is that whenever the IP address changed from what was recorded in a file, the new IP overwrites the one in the file, and other actions (not in the current code) will ensue.
It looks like that sometimes the comparison is not executed, thus the statements following the if do not execute.
I'd like to understand why is that. Line 21 has been changed at times for if (!oldIP == ip) to if (oldIP != ip), same result. I also suspect the return value to be ignored (dead code path ?) sometimes.
Here's the code
#!/usr/bin/env ruby
require "net/http"
puts "\e[H\e[2J"
def FetchIPAddress()
oldIP = ""
if File::exists?('/tmp/wanipaddress.txt')
iFile = File.open('/tmp/wanipaddress.txt')
oldIP = iFile.read()
iFile.close()
end
oFile = File.open('/tmp/wanipaddress.txt', "w+")
ip = Net::HTTP.get(URI("https://api.ipify.org"))
puts "old = " + oldIP
puts "new = " + ip
if (!oldIP == ip)
puts "changed"
oFile.puts ip
oFile.close()
else
ip = "unchanged"
puts "unchanged"
end
return ip
end
Really, I do see some erratic behaviour here; I suspect it's just me being a newbie with Ruby.
Your file likely contains a line break.
Try this
if old_ip.chomp != ip.chomp
...
end
chomp removes a trailing linebreak.
Best use p to print values for debugging, this will escape whitespace and thus make trailing linebreaks visible. You should never use puts for debugging.
And here is why !a == b will never ever work.
!a == b is the same as a.!().==(b) and executed as follows
first the ! method is called on object a, which returns false for a string
then ==(b) method is called on the resulting boolean
and since a boolean is never equal to a string the comparison will always fail
The problem with this line (if (!oldIP == ip)) is pretty simple - what you want it to do is check whether the oldIP is different from the new IP.
What you do instead is take oldIP, check whether it's true or false, then negate it (that's what the ! does), then compare it to ip. Since oldIP is a String, and thus always true, which gets negated to false, and ip is (I'm guessing) a String, it will always be true, your line essentially reads if (false == true).
To solve this problem, you could use the != comparison operator, like if oldIP != ip, or, if you like the negation, if !(oldIP == ip).

How to create a new struct when an error occurs

I am trying to make a text based game using ruby to learn the language, I want to throw an error if a player enters a string incorrectly, it should ask them to start the character making process over again.
My issue is that when I re-call the function to make the character, it creates a new struct and keeps the old one. So at the end I have something that is printing the new corrected struct as well as the old wrong struct. I obviously only want to keep the correct one so how do I go about getting rid of the old one?
Character = Struct.new(:name,:race,:alignment,:str,:dex,:int,:const,:char,:wis)
def makeChar()
newChar = Character.new()
until i == 6 do
case skillArray[i]
when "str"
newChar.str = rolledStats[i]
when "int"
newChar.int = rolledStats[i]
when "dex"
newChar.dex = rolledStats[i]
when "wis"
newChar.wis = rolledStats[i]
when "const"
newChar.const = rolledStats[i]
when "char"
newChar.char = rolledStats[i]
else
puts("It appears you have entered an invalid value for a skill")
puts("Please try again but enter only the following: str, dex, int, char, wis, const")
makeChar()
end
i += 1
end
puts("Our brave adventurer's name is #{name} the #{race}")
puts("#{name}'s stats are as follows:\n Str: #{newChar.str} \n Dex: #{newChar.dex}
Const: #{newChar.const} \n Int: #{newChar.int} \n Wis: #{newChar.wis} \n Char: #{newChar.char}")
print("#{name} prepare for your quest!")
There are cases when you need to 'get rid of' variables but this doesn't sound like one.
Just see this example:
word = "test"
def guess_word
guess = gets.chomp
exit if word == guess
end
loop do
puts "guess word"
guess_word
end
This program runs on a loop. Each iteration, it prompts the user for a word. If the word is correct, it exits. You can see in guess_word that the guess variable is assigned each time the method is called.
If you have a method which defines a variable, that variable's scope is just in that method unless there is a variable with the same name in a higher scope.

Why do `while` loops never check user input, whereas `if` statements do?

An if statement such as:
if first_name || first_name.length == 0
puts "You can't leave the first name blank, try again: "
first_name = gets
end
functions correctly, because it asks for user input, and when tested by not entering any input (including spaces), it prints an error message, followed by prompting for user input, following which, However, when I attempt to use the same in a while loop:
while first_name || first_name.length == 0
puts "You can't leave the first name blank, try again: "
first_name = gets
end
the loop doesn't function correctly.
The loop does the following instead:
• Print the error message above (OK)
• Asks for user input (OK)
• As soon as user input is received, the loop executes once again (Wrong).
It isn't testing the length of the user's (now correct) input, and then allowing the rest of the application to carry out its tasks.
I also tried unless, nil?, and so on to rectify the loop's error using an if statement inside the loop, with no success. I thought the while loop would evaluate first_name again once the error was corrected, and find that its length was no longer zero, however, that turned out not to be the case.
In Java, to say something isn't equal to something else (in terms of strings, for example), I can use ! before the variable name. Can I do this in Ruby?
You can try this:
while first_name.length == 0 do
puts "You can't leave the first name blank, try again: "
first_name = gets
end
The way while cycle works is it evaluates the statement after while keyword. If it is true then it runs the block. And after that just the same. In your case statement is first_name || first_name.length == 0 which always will be true as first_name is always present. That's the reason it will loop again each time whatever you would input.
I think your condition is just wrong : the condition
first_name || first_name.length == 0
Will be true if first_name is non-nil, this will always be the case since you are setting it to a string.
After some further digging on the Internet, I gave .chomp a go, to see what it would do, as I found out that when gets is used, it takes your input, but apparently also inserts an \n escape sequence, when Enter is pressed, meaning that my loop didn't function as required.
By using gets.chomp, in conjunction with .empty? I was able to remove this escape sequence from my input, and have the loop function correctly.
Here's the correct code, which prevents an infinite loop:
first_name = gets.chomp
while first_name.empty? do
puts "You can't leave the first name blank, try again: "
first_name = gets.chomp
end

Ruby: Why is this printed on a new line?

I have:
print 'Please enter the total amount of your bill: '
bill_amount = gets.chomp.to_f
print 'What percentage tip do you want? '
tip = gets.chomp.to_f / 100
puts "Your bill amount will be #{bill_amount * tip}"
When the final "puts" is printed, it's printed on a new line. Why is this? The previous "print something" statement was "print" which doesn't insert a new line. Even if I put "print" instead of "puts" for the end line, it still prints in a new line. What's going on? How can I make it so the end statement prints in the same line as "What percentage tip do you want"?
It is printed on the next line because you press ENTER to finish inputing your data (and therefore you make a new line). Didn't it confuse you that second print also goes to a new line?
Its printing in next line as after the user enters the value for 'Tip' , he/she has to press the 'enter' key to proceed.
The value of variable 'tip' will be free of any trailing white spaces (beacuse of .chomp).
However on the screen, the enter key has already moved the user to the next line and hence the statement prints in the next line.

Ruby: String Comparison Issues

I'm currently learning Ruby, and am enjoying most everything except a small string comparason issue.
answer = gets()
if (answer == "M")
print("Please enter how many numbers you'd like to multiply: ")
elsif (answer. == "A")
print("Please enter how many numbers you'd like to sum: ")
else
print("Invalid answer.")
print("\n")
return 0
end
What I'm doing is I'm using gets() to test whether the user wants to multiply their input or add it (I've tested both functions; they work), which I later get with some more input functions and float translations (which also work).
What happens is that I enter A and I get "Invalid answer."The same happens with M.
What is happening here? (I've also used .eql? (sp), that returns bubcus as well)
gets returns the entire string entered, including the newline, so when they type "M" and press enter the string you get back is "M\n". To get rid of the trailing newline, use String#chomp, i.e replace your first line with answer = gets.chomp.
The issue is that Ruby is including the carriage return in the value.
Change your first line to:
answer = gets().strip
And your script will run as expected.
Also, you should use puts instead of two print statements as puts auto adds the newline character.
your answer is getting returned with a carriage return appended. So input "A" is never equal to "A", but "A(return)"
You can see this if you change your reject line to print("Invalid answer.[#{answer}]"). You could also change your comparison to if (answer.chomp == ..)
I've never used gets put I think if you hit enter your variable answer will probably contain the '\n' try calling .chomp to remove it.
Add a newline when you check your answer...
answer == "M\n"
answer == "A\n"
Or chomp your string first: answer = gets.chomp

Resources