'gets' doesn't wait for user input - ruby

I'm attempting to develop a program on repl.it using its Ruby platform. Here's what I've got:
puts "Copy the entire request page and paste it into this console, then
hit ENTER."
request_info = gets("Edit Confirmation").chomp.gsub!(/\s+/m, '
').strip.split(" ")
puts "What is your name?"
your_name = gets.chomp
puts "Thanks, #{your_name}!"
The way I've got it, the user pastes a multi-line request, which ends with "Edit Confirmation", and then it splits the request, word-by-word, into its own array for me to parse and pull the relevant data.
But I can't seem to use the gets command a second time after initially inquiring the user for a multi-line input at the start. Any other gets command I attempt to use after that is skipped, and the program ends.

Your code is doing something quite unusual: By passing a string to the gets method, you are actually changing the input separator:
gets(sep, limit [, getline_args]) → string or nil
Reads the next “line'' from the I/O stream; lines are separated by sep.
The reason why your code is not working as you expect is because a trailing "\n" character is left in the input buffer - so calling gets a second time instantly returns this string.
Perhaps the easiest way to resolve this would just be to absorb this character in the first gets call:
request_info = gets("Edit Confirmation\n").chomp.gsub!(/\s+/m, ' ').strip.split(" ")
For a "complex" multi-line input like this, it would be more common to pass a file name parameter to the ruby script, and read this file rather than pasting it into the terminal.
Or, you could use gets(nil) to read until an EOF character and ask the user to press CTRL+D to signify the end of the multi-line input.

Related

Can interpolation be done on print output and in the middle of the output, is it possible to use gets in Ruby? (expecting reply from Ruby folks)

print """Input 1: (here I want *gets* to have some input)
Input 2:
"""
is it possible to get input in the middle of the printed 2 or more lines string in Ruby?
I can't tell exactly from your question, but I think this is what you are after:
This solution uses something called ANSI escape codes, which are "magic" sequences of characters you can use to control the terminal. You can do a variety of things with them, most commonly change output colour, but here we're using them to move the cursor around.
This works on my Mac, and should work on Linux - I'm not sure if this will work on Windows, you may have to enable ANSI escape sequences manually.
The general approach is:
Print the prompts
Move the cursor up to the line of the first prompt
Move the cursor forward to the end of that prompt
Accept input with gets
Move the cursor forward to the end of the second prompt
Accept input again
Here's a test script I wrote to do this, with enough comments to hopefully explain what is going on. I've changed the prompts from your original question so that they each have different lengths, to test that this solution can handle that.
prompt_1 = "First Input:"
prompt_2 = "Another Input:"
print "#{prompt_1}\n#{prompt_2}"
# Move the cursor back up to the line with the first prompt on
print "\033[1A"
# Move the cursor back to the start of the line, so we know we're in column zero
# Then move it to after the prompt
print "\033[1000D"
print "\033[#{prompt_1.length + 1}C"
# Get input
first_number = gets
# Hitting Enter after `gets` will have moved us to the next line already
# Move the cursor to the end of the second prompt
print "\033[1000D"
print "\033[#{prompt_2.length + 1}C"
# Get input again
second_number = gets
# Print output
puts "Addition is: #{first_number.strip.to_i + second_number.strip.to_i}"

What does $/ mean in Ruby?

I was reading about Ruby serialization (http://www.skorks.com/2010/04/serializing-and-deserializing-objects-with-ruby/) and came across the following code. What does $/ mean? I assume $ refers to an object?
array = []
$/="\n\n"
File.open("/home/alan/tmp/blah.yaml", "r").each do |object|
array << YAML::load(object)
end
$/ is a pre-defined variable. It's used as the input record separator, and has a default value of "\n".
Functions like gets uses $/ to determine how to separate the input. For example:
$/="\n\n"
str = gets
puts str
So you have to enter ENTER twice to end the input for str.
Reference: Pre-defined variables
This code is trying to read each object into an array element, so you need to tell it where one ends and the next begins. The line $/="\n\n" is setting what ruby uses to to break apart your file into.
$/ is known as the "input record separator" and is the value used to split up your file when you are reading it in. By default this value is set to new line, so when you read in a file, each line will be put into an array. What setting this value, you are telling ruby that one new line is not the end of a break, instead use the string given.
For example, if I have a comma separated file, I can write $/="," then if I do something like your code on a file like this:
foo, bar, magic, space
I would create an array directly, without having to split again:
["foo", " bar", " magic", " space"]
So your line will look for two newline characters, and split on each group of two instead of on every newline. You will only get two newline characters following each other when one line is empty. So this line tells Ruby, when reading files, break on empty lines instead of every line.
I found in this page something probably interesting:
http://www.zenspider.com/Languages/Ruby/QuickRef.html#18
$/ # The input record separator (eg #gets). Defaults to newline.
The $ means it is a global variable.
This one is however special as it is used by Ruby. Ruby uses that variable as a input record separator
For a full list with the special global variables see:
http://www.rubyist.net/~slagell/ruby/globalvars.html

Is there a way to do multi-line input in ruby AND single-line input at the same time (conditional user input)?

I face the following problem:
I have an irb-like shell where a user can give instructions.
For this I read in user input, both via Readline if the Readline
module is available, or through default $stdin.gets.
By default $stdin.gets will end at a \n character, if the user
hit enter. This works fine for most cases, but now I have the
problem that a user must be allowed to input a "'" character.
If you can not see this character, I literally mean the '
apostrophe right above the '#' character on most keyboards.
If he does so, then the record separator must change to "'" to,
so that the user can do multiline input.
The reason is that the desired input shall be something like:
data '
foo
bar
bla
'
Whereas normally it would be:
data foo
data far
data bla
Which is fine for single-line assignment, but not good for
multiline assignment as nobody wants to preface via "data"
on each line, hence why I want the "'" character to be the
delimiter in ONLY this case, otherwise default to \n newline.
It could be any other character too by the way, I just need
conditional user input.
Have you tried this?
$/ = "'"
user_input = STDIN.gets
puts user_input
It's not the same as what you wanted but it will continue taking inputs until you give the apostrophe mark.

Stop getting input with gets when specific character, similar chomp

In ruby I can stop getting inputs with:
name = gets.chomp
puts "Hello my name is " + name
How can I stop getting the input when a user types in END.
For example:
I hate it to write END
The input is terminated by the new line character that the user inputs. On standard console, you cannot terminate the input in other ways.
If you really want to do that introducing some heavy mechanism, then you should use curses, and particularly use getch.
How about
puts while gets.chomp != "END"

Why does Ruby's 'gets' includes the closing newline?

I never need the ending newline I get from gets. Half of the time I forget to chomp it and it is a pain in the....
Why is it there?
Like puts (which sounds similar), it is designed to work with lines, using the \n character.
gets takes an optional argument that is used for "splitting" the input (or "just reading till it arrives). It defaults to the special global variable $/, which contains a \n by default.
gets is a pretty generic method for readings streams and includes this separator. If it would not do it, parts of the stream content would be lost.
var = gets.chomp
This puts it all on one line for you.
If you look at the documentation of IO#gets, you'll notice that the method takes an optional parameter sep which defaults to $/ (the input record separator). You can decide to split input on other things than newlines, e.g. paragraphs ("a zero-length separator reads the input a paragraph at a time (two successive newlines in the input separate paragraphs)"):
>> gets('')
dsfasdf
fasfds
dsafadsf #=> "dsfasdf\nfasfds\n\n"
From a performance perspective, the better question would be "why should I get rid of it?". It's not a big cost, but under the hood you have to pay to chomp the string being returned. While you may never have had a case where you need it, you've surely had plenty of cases where you don't care -- gets s; puts stuff() if s =~ /y/i, etc. In those cases, you'll see a (tiny, tiny) performance improvement by not chomping.
How I auto-detect line endings:
# file open in binary mode
line_ending = 13.chr + 10.chr
check = file.read(1000)
case check
when /\r\n/
# already set
when /\n/
line_ending = 10.chr
when /\r/
line_ending = 13.chr
end
file.rewind
while !file.eof?
line = file.gets(line_ending).chomp
...
end

Resources