見出し画像

【続々】DOS画面での入力処理は面倒臭いですよね。

wait_press_key(explanation, regulations, allow_only_enter = false)

.gets_with_conditionの兄弟になります。指定のキーを押すと、エコー無しでキーを返すように.gets_with_conditionにラップしたものです。

標準添付ライブラリの io/consoleを使用します。
ANSI::EscapeSequenceと.gets_with_conditionも必須です。

該当モジュールは、こんな感じです。

module CommonModule

private

  def wait_press_key(explanation, regulations, allow_only_enter = false)
    # wait until pushing the specified key
    # note: require 'io/console'
    # retval: pushing char
    std_in ||= $stdin.clone
    std_in.binmode                                                        # binmode changing to permanent

    i_con = { IO: (-> { std_in.raw(&:getch).chomp }),                     # { for regular use, for mini test measures }
              StringIO: (-> { std_in.getc.chomp }) }[std_in.class.to_s.to_sym]

    gets_with_condition(explanation, allow_only_enter, i_con) do |s|
      s = s.upcase
      regulations.include?(s)
    end
  end
end

コード中にいくつか面白くないところがあります。
まず最初に、機能を実現するためにio/consoleのgetchを使用していますが、
[ENTER]の挙動が、そのままでは、windowsのDOS画面ではおかしくなります。そのために、.binmodeを指定していますが、rubyのリファレンスマニュアルを読むとこうあります。

「binmode -> self
ストリームをバイナリモードにします。MSDOS などバイナリモードの 存在する OS でのみ有効です。そうでない場合このメソッドは何もしませ ん。バイナリモードから通常のモードに戻す方法は再オープンしかありま せん。

そのため、通常のIOのクローンから、このメソッドを適用しています。
それが、
std_in ||= $stdin.clone
std_in.binmode
です。たぶん、メモリー上にずっと残っていると思われますが、あとはGCに任せるしかないです。

次に
i_con = { IO: (-> { std_in.raw(&:getch).chomp }),
    StringIO: (-> { std_in.getc.chomp }) }[std_in.class.to_s.to_sym]
ですが、本来は、
    i_con = -> { std_in.raw(&:getch).chomp }
だけで十分です。stringioを使った.stdio_coverageを使うとMiniTestはテストでコケてしまいます。
std_in.raw(&:getch).chompの.rawメソッドが、stringioクラスには存在しないからです。そのため、2種類のラムダをハッシュに用意してIOのクラス名で差し替えています。テストのために本来のメソッドの中にテスト環境を前以て埋め込むのは本末転倒ではありますが、低レベル関数に属するようなメソッドなので、テストでコケる度に対応するのは面倒ですので、このような仕様にしています。
どうせラッパーにして見えなくしてあるんだからいいやとポンコツ君は、妥協することにしました。

使い方は、こんな感じで簡単です。

ESC = ANSI::EscapeSequence        # 適切な場所で、必要な呪文を唱える
require 'io/console'              # 適切な場所で、必要な呪文を唱える
include CommonModule              # 適切な場所で、必要な呪文を唱える

answer = wait_press_key('このプログラムを終了しますか?(Y/N) ===> ', %[Y N]).upcase
print "\n#{answer}"

ポンコツだからって、構やしない。動けば正義さ!


この記事が気に入ったらサポートをしてみませんか?