Gets stops processing script if it encounters a apostrophe - ruby

I'm futzing with a lesson over on Codecademy, pretty basic but the lesson prompts the user for a string and then prompts them for words to hide from the string. I've already finished with the lesson but I wanted to try messing with it outside of the lesson.
What I'm finding is the following script will run to completion in the lesson's scripting and interpreter area but if I try to run the same script over at say labs.codecademy.com or repl.it
I'll get prompted for the first question and if I enter a string containing an apostrophe it won't go on to the next prompt. I'm not getting an error, but eventually I'll get a "This program is taking too long to finish" pop up.
Here's the script:
puts "Tell me something."
text = gets.chomp
puts "What would you like me to forget?"
redact = gets.chomp
words = text.split(" ")
words.each { |text|
if redact.include? text
print "REDACTED "
else
print text + " "
end
}
So for example if you were to enter I really like blueberry pie that passes, but if you were to enter They've told me I should try the blueberry pie the program gets hung up.
I'm a complete novice when it comes to Ruby, but is there a way to have gets process that sort of punctuation? Or is that not the right way to go about it? Or is it just the environment I'm trying to run this program in?

IO.gets is a system-level call: http://apidock.com/ruby/IO/gets
it well read from STDIN (standard input) which is the text that you type in a terminal window: http://en.wikipedia.org/wiki/Standard_streams
browsers per-se don't have this and it might depend on the implementation of the web-based console about how they handle calls like that. usually, there are known limitations (like this one).
as #BroiStatse pointed out, it might as well be a bug in in their browser-based implementation.

Related

ruby placeholders NOT populating in external .txt file

Here is what I currently have; the only problem being the external file loads without the placeholder text updated -- instead rather, the placeholder text just says '[NOUN]' instead of actual noun inserted from user in earlier program prompt.
Update; cleaned up with #tadmans suggestions, it is however, still not passing user input to placeholder text in external .txt file.
puts "\n\nOnce upon a time on the internet... \n\n"
puts "Name 1 website:"
print "1. "
loc=gets
puts "\n\Write 5 adjectives: "
print "1. "
adj1=gets
print "\n2. "
adj2=gets
print "\n3. "
adj3=gets
print "\n4. "
adj4=gets
print "\n5. "
adj5=gets
puts "\n\Write 2 nouns: "
print "1. "
noun1=gets
print "\n2. "
noun2=gets
puts "\n\nWrite 1 verb: "
print "1. "
verb=gets
puts "\n\nWrite 1 adverb: "
print "1. "
ptverb=gets
string_story = File.read("dynamicstory.txt")
puts string_story
Currently output is (i.e. placeholders not populated):
\n\nOnce upon a time on the internet...\n\n
One dreary evening while browsing the #{loc} website online, I stumbled accross a #{adj1} Frog creature named Earl. This frog would sit perturbed for hours at a time at the corner of my screen like Malware. One day, the frog appeared with a #{adj2} companion named Waldo that sat on the other corner of my screen. He had a #{adj3} set of ears with sharp #{noun1} inside. As the internet frogs began conversing and becoming close friends in hopes of #{noun2}, they eventually created a generic start-up together. They knew their start-up was #{adj4} but didn't seem to care and pushed through anyway. They would #{verb} on the beach with each other in the evenings after operating with shady ethics by day. They could only dream of a shiny and #{adj5} future full of gold. But then they eventually #{ptverb} and moved to Canada.\n\n
The End\n\n\n
It's important to note that the Ruby string interpolation syntax is only valid within actual Ruby code, and it does not apply in external files. Those are just plain strings.
If you want to do rough interpolation on those you'll need to restructure your program in order to make it easy to do. The last thing you want is to have to eval that string.
When writing code, always think about breaking up your program into methods or functions that have a specific function and can be used in a variety of situations. Ruby generally encourages code-reuse and promoting the "DRY principle", or "Don't Repeat Yourself".
For example, your input method boils down to this generic method:
def input(thing, count = 1)
puts "Name %d %s:" % [ count, thing ]
count.times.map do |i|
print '%d. ' % (i + 1)
gets.chomp
end
end
Where that gets input for a random thing with an arbitrary count. I'm using sprintf-style formatters here with % but you're free to use regular interpolation if that's how you like it. I just find it leads to a less cluttered string, especially when interpolating complicated chunks of code.
Next you need to organize that data into a proper container so you can access it programmatically. Using a bunch of unrelated variables is problematic. Using a Hash here makes it easy:
puts "\n\nOnce upon a time on the internet... \n\n"
words = { }
words[:website] = input('website')
words[:adjective] = input('adjectives', 5)
words[:noun] = input('nouns', 2)
words[:verb] = input('verb')
words[:adverb] = input('adverb')
Notice how you can now alter the order of these things by re-ordering the lines of code, and you can change how many of something you ask for by adjusting a single number, very easy.
The next thing to fix is your interpolation problem. Instead of using Ruby notation #{...}, which is hard to evaluate, go with something simple. In this case %verb1 and %noun2 are used:
def interpolate(string, values)
string.gsub(/\%(website|adjective|noun|verb|adverb)(\d+)/) do
values.dig($1.to_sym, $2.to_i - 1)
end
end
That looks a bit ugly, but the regular expression is used to identify those tags and $1 and $2 pull out the two parts, word and number, separately, based on the capturing done in the regular expression. This might look a bit advanced, but if you take the time to understand this method you can very quickly solve fairly complicated problems with little fuss. It's something you'll use in a lot of situations when parsing or rewriting strings.
Here's a quick way to test it:
string_story = File.read("dynamicstory.txt")
puts interpolate(string_story, words)
Where the content of your file looks like:
One dreary evening while browsing the %website1 website online,
I stumbled accross a %adjective1 Frog creature named Earl.
You could also adjust your interpolate method to pick random words.

How to stop program from exiting itself out?

my question regards the programming language of Ruby and the Ruby program.
After using this code:
print "What's your first name?"
first_name = gets.chomp
first_name.capitalize!
print "What's your last name?"
last_name = gets.chomp
last_name.capitalize!
print "What city are you from?"
city = gets.chomp
city.capitalize!
print "What state or province are you from?"
state = gets.chomp
state.upcase!
print "Your name is #{first_name} #{last_name} and you're from #{city}, #{state}!"
After the program asks for the state or province and I enter in a value, the program automatically exits itself out.
I am using RubyInstaller for Windows (version Ruby 1.9.3-p545).
I used Notepad++ to type the code and saved it under the Ruby .rb extension.
I ran the program by double-clicking the file from where I saved it.
Thanks! :)
One easy way is to add this at the end:
puts "Press RETURN when you're done."
gets
Well, it is the end of the program, there are no more user questions to be answered at that point. So what do you want your program to do after it asks you for state/province and repeats your data to you?
You might want to change print to puts though.
EDIT: As Michael and Leo say in comments, if you start your program by double-clicking the file in Windows, it will open a terminal, run your program, and close the terminal as soon as the program is done. The program is repeating the information back in a sentence with the valoues in the sentence; but that information is on your screen only for a split second, before Windows closes your program's terminal. Leo and Michael each suggest a way (run from terminal or insert a sleep statement); another way similar to Leo's is having another gets at the end and asking the user to hit Enter when they are done reading.
have you tried from your command to run
irb code.rb
Assuming your file name is code.rb
Also as suggests try puts instead of print
Infinite loop still works
while true
'something'
end
print"What's your first name?"
first_name=gets.chomp
first_name.capitalize!
print"What's your last name?"
last_name=gets.chomp
last_name.capitalize!
print"What city are you from?"
city=gets.chomp
city.capitalize!
print"What state are you from?"
state=gets.chomp
state.upcase!
puts"Your name is #{first_name},your last name is #{last_name}, and your from #{city},#{state}!"
Works

What is the best way to get keyboard events (input without press 'enter') in a Ruby console application?

I've been looking for this answer in the internet for a while and have found other people asking the same thing, even here. So this post will be a presentation of my case and a response to the "solutions" that I have found.
I am such new in Ruby, but for learning purposes I decided to create a gem, here.
I am trying to implement a keyboard navigation to this program, that will allow the user use short-cuts to select what kind of request he want to see. And in the future, arrow navigations, etc.
My problem: I can't find a consistent way to get the keyboard events from the user's console with Ruby.
Solutions that I have tried:
Highline gem: Seems do not support this feature anymore. Anyway it uses the STDIN, keep reading.
STDIN.getch: I need to run it in a parallel loop, because at the same time that the user can use a short-cut, more data can be created and the program needs to show it. And well, I display formated text in the console, (Rails log). When this loop is running, my text lost the all the format.
Curses: Cool but I need to set position(x,y) to display my text every time? It will get confusing.
Here is where I am trying to do it.
You may note that I am using "stty -raw echo" (turns raw off) before show my text and "stty raw -echo" (turns raw on) after. That keeps my text formated.
But my key listener loop is not working. I mean, It works in sometimes but is not consistent. If a press a key twice it don't work anymore and sometimes it stops alone too.
Let me put one part of the code here:
def run
# Two loops run in parallel using Threads.
# stream_log loops like a normal stream in the file, but it also parser the text.
# break it into requests and store in #requests_queue.
# stream_parsed_log stream inside the #requests_queue and shows it in the screen.
#requests_queue = Queue.new
#all_requests = Array.new
# It's not working yet.
Thread.new { listen_keyboard }
Thread.new { stream_log }
stream_parsed_log
end
def listen_keyboard
# not finished
loop do
char = STDIN.getch
case char
when 'q'
puts "Exiting."
exit
when 'a'
#types_to_show = ['GET', 'POST', 'PUT', 'DELETE', 'ASSET']
requests_to_show = filter_to_show(#all_requests)
command = true
when 'p'
#types_to_show = ['POST']
requests_to_show = filter_to_show(#all_requests)
command = true
end
clear_screen if command
#requests_queue += requests_to_show if command
command = false
end
end
I need a light in my path, what should I do?
That one was my mistake.
It's just a logic error in another part of code that was running in another thread so the ruby don't shows the error by default. I used ruby -d and realized what was wrong. This mistake was messing my keyboard input.
So now it's fixed and I am using STDIN.getch with no problem.
I just turn the raw mode off before show any string. And everything is ok.
You can check here, or in the gem itself.
That's it.

Why does gets return zero in Ruby

I have a simple method
def save_logline
print "What's the name of the movie"
movie_name = gets.strip
print "And what is your precious logline?"
logline = gets.strip
File::open(movie_name + '.txt', 'w') do |f|
f.write(logline)
end
end
Anytime I run the code, I receive the first prompt where I insert the name of the movie, but once I press enter the second print is called, giving me this message.
And what is your precious logline?=> 0
The file is written but I'm prevented from inserting anything into the text file. What's wrong with my logic and how do I fix this? I am using irb in RubyMine with Ubuntu.
EDIT: It appears that due to other users response to my code working and my own test on a different development machine my code is just not working on that one machine. The only thing I could say is that I was using RubyMine's irb console. The question now must be is it a RubyMine issue or is there a bigger problem with my dev machine?
FINAL EDIT: I checked on my second comp that has RubyMine, it appears that it does not work in that RubyMine's irb console either. I'm going to issue the bug to RubyMine and give credit to Jeremy in a day or two unless someone knows why RubyMine's irb is acting funky.
Your code works. The 0 is the return value of f.write. It's saying it wrote zero bytes to your file, because you didn't give it any input.
Edit: Maybe you pressed enter twice by accident...

Multi-line input problems when using STDIN.gets

Having looked at this question, I have the following code:
$/ = "\0"
answer = STDIN.gets
Now, I was hoping that this would allow the user to:
enter a multi-line input, terminating by pressing Ctrl-D.
enter a single line input, terminating by pressing Ctrl-D.
enter a "nothing" input, terminating by pressing Ctrl-D.
However, the behaviour I actually see is that:
The user can enter a multi-line input fine.
The user can not enter a single line input, unless they hit Ctrl-D twice.
The user can enter a "nothing" input if they hit Ctrl-D straight away.
So, why does the single line situation (i.e. if the user has entered some text but no newline and then hit Ctrl-D) require two presses of Ctrl-D? And why does it work then if the user enters nothing? (I have noted that if they enter nothing and hit Ctrl-D, I don't get an empty string but the nil class - I discovered this when trying to call .empty? on the result, since it suddenly failed horribly. If there is a way to get it to return an empty string as well, that would be nice. I prefer checking .empty? to ==, and don't particularly want to define .empty? for the nil class.)
EDIT: Since I really would like to know the "correct way" to do this in Ruby, I am offering a bounty of 200 rep. I will also accept answers that give another way of entering terminal multi-line input with a sensible "submit" procedure - I will be the judge of 'suitable'. For example, we're currently using two "\n"s, but that's not suitable, as it blocks paragraphs and is unintuitive.
The basic problem is the terminal itself. See many of the related links to the right of your post. To get around this you need to put the terminal in a raw state. The following worked for me on a Solaris machine:
#!/usr/bin/env ruby
# store the old stty settings
old_stty = `stty -g`
# Set up the terminal in non-canonical mode input processing
# This causes the terminal to process one character at a time
system "stty -icanon min 1 time 0 -isig"
answer = ""
while true
char = STDIN.getc
break if char == ?\C-d # break on Ctrl-d
answer += char.chr
end
system "stty #{old_stty}" # restore stty settings
answer
I'm not sure if the storing and restoring of the stty settings is necessary but I've seen other people do it.
When reading STDIN from a terminal device you are working in a slightly different mode to reading STDIN from a file or a pipe.
When reading from a tty Control-D (EOF) only really sends EOF if the input buffer is empty. If it is not empty it returns data to the read system call but does not send EOF.
The solution is to use some lower level IO and read a character at a time. The following code (or somethings similar) will do what you want
#!/usr/bin/env ruby
answer = ""
while true
begin
input = STDIN.sysread(1)
answer += input
rescue EOFError
break
end
end
puts "|#{answer.class}|#{answer}|"
The results of running this code with various inputs are as follows :-
INPUT This is a line<CR><Ctrl-D>
|String|This is a line
|
INPUT This is a line<Ctrl-D>
|String|This is a line|
INPUT<Ctrl-D>
|String||

Resources