getsメソッドで入力された内容から処理する

昨日書いてなかった・・・
皆勤な日記が崩れてしまったー。
でも3日坊主にはなってないからいいか。

今回は、コマンドラインで入力を受け付けて処理〜ってのを、
あんまりやったことがなかったので、それをまとめておく。

結構初歩的ですが。
自分が実際に使った実装をするノウハウを持っていなかったので、そのためにって感じです。

  #入力メッセージstr : (入力)
  #入力したされた内容を返す
  def please_input(str = "")
    print "\n#{str} : "
    input_str = gets.chomp.toutf8
    return input_str
  end  

  #カテゴリー(Fixnum)に応じた選択内容項目を配列で返す
  #意味合いを持たせるならシンボルの方がいいと思いますが、例・まとめ用ということで
  def get_item(category)
    case category.to_i
      when 1 then ["item_a", "item_b", "item_c"]
      when 2 then ["item_11", "item_22", "item_33", "item_44"]
      when 3 then ["item_X", "item_Y", "item_Z"]
      when 4 then ["item_one", "item_two", "item_three", "item_four", "item_five"]
      else raise("get_item method inner. category case error!")
    end
  end

  #カテゴリーに応じた選択内容項目の配列を
  #"1 : 項目A,   2: 項目B,   3: 項目C"みたいな文字列にして返す
  def select_item_output(category)
    items = get_item(category)
      
    #非常に汚いロジックだけど開き直った
    num = 0
    return items.map{|item| "#{num+=1} : #{item}"}.join(",\t")
  end


  select_items = Hash.new

  #カテゴリー1〜4を順番に選択させる
  (1..4).each do |category_num|
    puts "\n下記に表示するカテゴリー#{category_num}の中から、当てはまる物を選び半角数字で入力してください。"
    puts "選択しない・当てはまる物がない場合は:noneと入力ください"

    puts select_item_output(category_num)
    category_range = (1 .. get_item(category_num).size)

    input_str = please_input("カテゴリー#{category_num}から選択")
    input_num = input_str.to_i
    
    while (!category_range.include?(input_num) && input_str != ":none")
      if input_str.empty?
        puts "入力不正 : 入力されていません。\nもう一度 1 〜 #{category_range.max} の半角数字で選び、入力してください。"
      else
        puts "入力不正 : #{input_str} はカテゴリー#{category_num}の番号一覧にありません。\nもう一度 1 〜 #{category_range.max} の半角数字で選び、入力してください。"
      end
      input_str = please_input("カテゴリー#{category_num}から選択")
      input_num = input_str.to_i
    end

    select_items[category_num] = input_num == 0 ? nil : get_item(1)[input_num - 1]
    puts "入力確認 : #{input_str} - #{select_items[category_num]}"
  end

  p select_items

割と即席なのでここ共通化したり、
もっとスマートに出来るだろうって所は多いのですが、
こうやれば出来るってのが分かったのでよし。

この辺↓とか2回書いてるし、メソッドに切り離して2つ戻り値返した方がよさそうだし。
input_str = please_input("カテゴリー#{category_num}から選択")
input_num = input_str.to_i

2〜3回メソッドを読んで配列を得るのと、
1度変数に結果を代入してそれを使う(ただしローカル変数が1個増える)のとでは、
どっちがいいんだろう。
同じ物を参照したり処理するなら変数に結果入れた方が良好だと思いつつ、
簡素な結果を返すメソッドの結果のために、変数1個増やすのもどうなのかなとたまに思う。
(この例だと結果を変数に代入しといた方が可読性は高くなりそうですけれどねw)


単純に入力必須かつ、半角数字で範囲からチェックするだけでいいなら
whileのところを
until category_range.include?(input_num)
にした方が可読性高いと思う。
数字選択かつ、未選択可。
しかし入力時に誤ってEnter押して空文字で未選択と判断すると入力ミスが出る場合が想定されるので、未選択にするならpassとか:noneとかこちらで指定する別の文字列打ってね!って仕様にした。
その結果whileの判定が若干読みにくくなって、ちょっとショボーンとした。


元々書いてた内容をちょいちょい書き換えたので、確実に動く保証は出来ません。
irbでさっきやった分には動きましたが、変なところに\tとか\n入ってるかも。

それとwindowsコマンドプロンプトに表示するならkconvとかrequireして、.tosjisとかかけないと日本語でないと思われる。



何か簡単な対話式の入力スクリプトとか、
入力に応じて動作を変えるスクリプト書く場合があれば覚えといて損ないかなって感じ。

最初に動作させる処理項目メニューを出して、
どっか定数とかに
MENU_LIST ={
番号(Fixnum) => {
:menu_str => "処理内容を意味する文字列",
:call_method => "実行するメソッド"
},
番号(Fixnum) => {
:menu_str => "処理内容を意味する文字列",
:call_method => "実行するメソッド"
}
}
と定義させといて、
これを元に入力に応じて
eval("MENU_LIST[:call_method]")
とかも出来るし。

evalじゃなくて、sendメソッドでも呼べるかな?
ただ、
"self.メソッド名"みたいに定義したり、
場合によってクラスメソッド呼んだり混ざって定義してたら、
この場合はevalの方が安定かな。

この元コードはそういう実装も含めたり、結構汚くなってます。
結構上手く作らないとゴチャゴチャする良い例orz
とても酷くて見せられないので、
入力部分だけ少し改変して載せてます(それでも無駄があったり色々ひどいですが)


以上、昨日書こうかなと思った内容でした。