Thread ignoring first input when using getch - ruby

require 'rubygems'
require 'mechanize'
require 'io/console'
flag = 0
t2 =Thread.new do
puts flag
loop do
temp = STDIN.getch
if temp=="\n"
flag = (flag+1)%2
puts flag
end
end
end
# => Some foreground code
t2.join
When i run the code i get the value of flag printed 0 as it should. But the thread does not change the value of flag on the first Enter I hit. Hitting Enter the second time changes flag to 1 although. The thread works normally toggling the value of flag on further Enter hits. Why is this happening? What have I done wrong?
Problem seems to be only with getch
as when I use gets in place of getch the problem disappears. But I cant use gets because I want the user to hit a single key without needing to press Enter after the key to give input.
For example flag should not change when the user inputs a instead of Enter and so I have used getch to make sure the input is given after a single keyboard hit.
A similar problem was described here but it isn't a duplicate.
Edit 1:
The problem seems to be with getch and not the check what do ever.
flag = 0
t2 =Thread.new do
puts flag
loop do
temp = STDIN.getch
flag = (flag+1)%2
puts flag
end
end
t2.join
Even after removing the if statement, the first Enter is ignored no matter what but other characters seem to respond to the first time. The problem is coming only when I hit Enter. It doesn't count the first Enter I hit.
ruby 2.3.3p222 (2016-11-21 revision 56859) [x64-mingw32]

I tried your code on a Windows machine and was able to re-create the problem. As you correctly guessed it has nothing to do with threading and everything with how getch works (on Windows).
If you add a p temp.inspect to your loop you will see that it is not so much that the first '\n' is swallowed, rather it is that it is somehow "held back". The best way to see this if you press Enter and another key alternatively. You will see that the inspect is "off-by-one".
A good explanation about the problem is discussed here:
https://www.rubytapas.com/2016/12/14/ruby-code-on-windows/
With that information, a simple solution (that has the added benefit to run also on Linux, not sure about Mac) is:
require 'rubygems'
require 'mechanize'
require 'io/console'
STDIN.binmode #this line added to prevent line-end translation
flag = 0
t2 =Thread.new do
puts flag
loop do
temp = STDIN.getch
if temp=="\r" # note: changed from LF to CR
flag = (flag+1)%2
puts flag
end
end
end
# => Some foreground code
t2.join
Notes:
Admittedly I don't fully get the way it works. I was expecting that Enter would cause a "\r\n" sequence in binmode, but I only see "\r". Not sure what happens to the "\n", but it seems to work reliably this way.
Also note that in the current version the program can not be terminated with Ctrl+C. You'll have to add a check for that.

Related

binding.pry appears to be debouncing and then re-displaying text input

I'm following along with a tutorial book, and in order to step through my code I've required pry and added a binding.pry reference in a method which gets executed:
require 'pry'
module Rulers
module Model
class FileModel
def initialize(filename)
#filename = filename
binding.pry
basename = File.split(filename)[-1]
...
When I load the code which executes this constructor method, I successfully hit my binding.pry debugger point as expected. However, when I start typing commands at the prompt, the characters I enter are printed out in the same prompt:
[7] pry(#<Rulers::Model::FileModel>)> .whic.whichh
In the above example, I typed .which. I had gotten all the way to .whic when the REPL added the characters I had typed thus far (i.e. it added .whic to .whic, resulting in .whic.whic). The last character I typed was h, which registers after .whic.whic. Finally, the last h I typed was also appended to the chars on the prompt.
Interestingly, the duplicated chars do not appear to affect the REPL's ability to understand the commands I type in. For example, the following:
[8] pry(#<Rulers::Model::FileModel>)> .which .which vim
[8] pry(#<Rulers::Model::FileModel>)> .which vim
/usr/bin/vim
In the above example, I was successful in typing .which quickly enough to not result in a muddled version of the which command, and I also pressed Enter before vim could be duplicated erroneously. If the redundant 2nd .which command were to affect my ability to return the expected output, I would expect to see an error returned. Instead, I see /usr/bin/vim as I would expect when typing which vim in my regular, non-pry command line prompt.
I have no theories on where to begin debugging this. Any ideas?

How to tell STDIN to stop reading?

So I am studying Build Awesome Command-Line Applications in Ruby. On page 81, we're supposed to use STDIN to enter more than one task into a project.
File.open(global_options[:filename], 'a+') do |todo_file|
if task_names.empty?
puts "Reading new tasks from stdin..."
task_names = STDIN.readlines.map {|a| a.chomp}
end
tasks = 0
task_names.each do |task|
todo_file.puts [task, Time.now].join(', ')
tasks+=1
end
if tasks == 0
raise "You must provide tasks on the command-line or standard input"
end
end
The usual way to enter tasks into a project it's like this $todo new "Rake leaves but with the code above we can to what's in the example below.
It does work. But how do I tell STDIN to stop listening? The example on how to use it is this...
$ todo new
Rake leaves
Take out trash
Clean garage
Put away dishes
^D
What does the ^D represent?
It’s an end-of-file character. You can type this literally on Unix systems with Ctrl+D or on Windows with Ctrl+Z. The traditional way of displaying the Ctrl modifier is with a ^ prefix, e.g., ^D.
Be aware that this closes standard input entirely. If you want to read more data after entering these lines, you’ll need to check the input itself for a different delimiter—for instance, an empty line.
You can close STDIN by pressing Ctrl-d on Unix-like systems or Ctrl-z on Windows.
What does the ^6 represent?
Are you sure it says ^6 and not ^d? If so, that's probably a typo.

Undoing auto-indentation

Wnen you use irb with auto indent mode, the end statements get indented one level extra
def foo
...
end
instead of showing the ordinary indenting convention:
def foo
...
end
because you cannot tell irb in advance that you are going to escape one level in the next line. This question has been addressed elsewhere like here or here, but neither gives a satisfactory answer. They just suggest giving up.
However, if we can minimally overwrite some irb methods so that auto indent will insert white spaces not in the prompt area but at the beginning of the line you type in, then by default, irb will still be inserting spaces, but we will be able to erase some spaces with backspace. Is this possible?
Or, if that is not realistic, then is it possible to make irb erase the last line from the screen and redisplay it with proper indentation right after you press Enter on a line including end?
Rewriting the last line is possible. Doing it in irb is difficult due to its lack of documentation and consistent api across versions. An irb alternative, ripl, has already solved this issue for itself with an auto-indent plugin. If you want to give ripl and its auto-indenting a try:
$ gem install ripl-auto_indent
$ echo "require 'ripl/auto_indent'" >> ~/.riplrc
# Auto-indent away
$ ripl
>> def foo
>> puts "it's auto-magic!"
>> end

Ruby Inputs - Weird?

I'm a bit confused with the input's of Ruby.
Whenever I try to get input, it doesn't register the 'Backspace' key. Also, it never accepts the 'Enter' first time. I always have to push 'Enter' after my input usually 3 times before it actually inputs it.
For example,
view source
print?
1 my_var = gets.chomp
If I wanted to enter 'Hello', I would have to type it followed by pressing the return key 3 times before it actually entered it.
Now I did find a way to solve this using...
view source
print?
1 STDOUT.flush
2 my_var = gets.chomp
But...
1) This seems wierd having to enter that EVERY time before I want input?
2) It still doesn't solve the problem of registering backspace.
If I was to type directly 'Hello World' but with two accidental keystrokes such as this: Hello Worpold
Even if I used the backspace so it appeared as I was entering: Hello World
If I then went on to 'puts' or 'print' that it would output: Hello Worpold
Know how I can fix it so it accepts backspace and know any other ways of missing out STDOUT.flush?
Thanks in advance
Use the readline module.
What environment are you running Ruby in ? If you're unsure, check with ruby -v
>ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [i386-mswin32]
I'm running v1.8.6 on Windows XP
a = gets
puts "I just got #{a}"
Saved the above snippet to c:\temp.rb and run it with ruby c:\temp.rb
Backspace key works, I can correct strings before pressing enter once to confirm my input.

How to get a single character without pressing enter?

How can I get a single keyboard character from the terminal with Ruby without pressing enter?
I tried Curses::getch, but that didn't really work for me.
Since ruby 2.0.0, there is a 'io/console' in the stdlib with this feature
require 'io/console'
STDIN.getch
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/2999
#!/usr/bin/ruby
begin
system("stty raw -echo")
str = STDIN.getc
ensure
system("stty -raw echo")
end
p str.chr
(Tested on my OS X system, may not be portable to all Ruby platforms). See http://www.rubyquiz.com/quiz5.html for some additional suggestions, including for Windows.
#Jay gave a great answer, but there are two problems:
You can mess up default tty state;
You ignore control characters (^C for SIGINT, etc).
A simple fix for that is to save previous tty state and use following parameters:
-icanon - disable canonical input (ERASE and KILL processing);
isig - enable the checking of characters against the special control characters INTR, QUIT, and SUSP.
In the end you would have a function like this:
def get_char
state = `stty -g`
`stty raw -echo -icanon isig`
STDIN.getc.chr
ensure
`stty #{state}`
end
Raw mode (stty raw -echo) unfortunately causes control-C to get sent in as a character, not as a SIGINT. So if you want blocking input like above, but allow the user to hit control-C to stop the program while it's waiting, make sure to do this:
Signal.trap("INT") do # SIGINT = control-C
exit
end
And if you want non-blocking input -- that is, periodically check if the user has pressed a key, but in the meantime, go do other stuff -- then you can do this:
require 'io/wait'
def char_if_pressed
begin
system("stty raw -echo") # turn raw input on
c = nil
if $stdin.ready?
c = $stdin.getc
end
c.chr if c
ensure
system "stty -raw echo" # turn raw input off
end
end
while true
c = char_if_pressed
puts "[#{c}]" if c
sleep 1
puts "tick"
end
Note that you don't need a special SIGINT handler for the non-blocking version since the tty is only in raw mode for a brief moment.
Note: This is and old answer and the solution no longer works on most systems.
But the answer could still be useful for some environments, where the other methods don't work. Please read the comments below.
First you have to install highline:
gem install highline
Then try if the highline method works for you:
require "highline/system_extensions"
include HighLine::SystemExtensions
print "Press any key:"
k = get_character
puts k.chr
And if you are building curses application, you need to call
nocbreak
http://www.ruby-doc.org/stdlib-1.9.3/libdoc/curses/rdoc/Curses.html#method-c-cbreak

Resources