読者です 読者をやめる 読者になる 読者になる

RubyでWebアプリ作ろうぜ!

気ままに楽しくStudy中

「作りながら学ぶRuby入門第2版」第3部>課題

Ruby

第3部>課題: P187(第14章)

第3部の課題は、練習で使ったファイル「ex1401.rb」に検索機能を追加するというもの。与えられた条件自体は、これまでの練習の応用レベルなので難しくありませんでしたが、意図しない箇所でErrorが発生してしまい若干ビビリました。

触っているうちになんとなくErrorの原因が予測できたので調べてみると、やはり原因はローカル変数のスコープでした。これ結構、Ruby入門者はハマる箇所じゃないのかな〜?別記事にて補足します。

別記事にて補足する予定だったローカル変数のスコープについて、m-kawatoさんのブログ「m-kawato@hatena_diary」に「これだ!」という記事がありましたので、リンクを張らせていただきました。私同様、Ruby入門者の方は一読をお薦めします。

作りながら学ぶRuby入門 第2版

作りながら学ぶRuby入門 第2版

課題のプログラムは、まずranメソッドに選択肢とsearchBookInfosを追加してから、searchBookInfosの処理を書きました。「ex1401.rb」に追加した箇所は85行目以降です。

検索機能は、入力された検索条件検と全て一致した蔵書データの一覧を表示する仕様です。書籍名の比較だけ練習でパターンマッチを使ってみました。索機能って奥が深そうですね〜。色々と欲張って盛り込もうとしましたが、ハマる気配がしたのでやめておきました。もっと上手で綺麗なコードが書けるようになりたいです。頑張るぞ。

なお、Rubyのバージョンは1.9.3-p194です。

# coding: utf-8
require 'date'

class BookInfo
  # Bookinfoクラスのインスタンスを初期化する
  def initialize( title, author, page, pdate )
    @title = title
    @author = author
    @page = page
    @pdate = pdate # publish_date
  end

  # 最初に検討する属性に対するアクセサを提供する
  attr_accessor :title, :author, :page, :pdate

  # Bookinfoクラスのインスタンスの文字列表現を返す
  def to_s
    "#{@title}, #{@author}, #{@page}, #{@pdate}"
  end

  # 蔵書データを書式をつけて出力する操作を追加する
  # 項目の区切り文字を引数に指定することができる
  # 引数を省略した場合は改行を区切り文字にする
  def toFormattedString( sep = "\n" )
    "書籍名: #{@title}#{sep}著者名: #{@author}#{sep}ページ数: #{@page}ページ#{sep}発刊日: #{@pdate}#{sep}"
  end
end

# BookInfoManagerクラスを定義する
class BookInfoManager
  def initialize
    @book_infos = {}
  end

  # 蔵書データをセットアップする
  def setUp
    # 複数冊の蔵書データを登録する
    @book_infos["Yamada2005"] = BookInfo.new(
      "実践アジャイル ソフトウェア開発法とプロジェクト管理",
      "山田 正樹",
      248,
      Date.new( 2005, 1, 25 ) )
    @book_infos["Ooba2006"] = BookInfo.new(
      "入門LEGO MINDSTORMS NXT レゴブロックで作る動くロボット",
      "大庭 慎一郎",
      164,
      Date.new( 2006, 12, 23 ) )
  end

  # 蔵書データを登録する
  def addBookInfo
    # 蔵書データ1件分のインスタンスを作成する
    book_info = BookInfo.new( "", "", 0, Date.new )
    # 登録するデータを項目ごとに入力する
    print "\n"
    print "キー: " 
    key = gets.chomp 
    print "書籍名: "    
    book_info.title = gets.chomp 
    print "著者名: "
    book_info.author = gets.chomp 
    print "ページ数: "
    book_info.page = gets.chomp.to_i
    print "発刊年: " 
    year = gets.chomp.to_i
    print "発刊月: " 
    month = gets.chomp.to_i
    print "発刊日: "
    day = gets.chomp.to_i
    book_info.pdate = Date.new( year, month, day )
    # 作成した蔵書データ1件分をハッシュに登録する
    @book_infos[key] = book_info
  end

  # 蔵書データの一覧を表示する
  def listAllBookInfos
    puts "\n------------------------------"
    @book_infos.each { |key, info|
      print info.toFormattedString
      puts "\n------------------------------"
    }

  end
  
  def searchBookInfos
    # 検索データ1件分のインスタンスを作成する
    book_info = BookInfo.new( "", "", 0, nil )
    # 検索するデータを項目ごとに入力する
    print "\n" 
    print "書籍名: " 
    book_info.title = gets.chomp
    print "著者名: "
    book_info.author = gets.chomp
    print "ページ数: "
    book_info.page = gets.chomp.to_i
    print "発刊年: " 
    year = gets.chomp.to_i
    print "発刊月: " 
    month = gets.chomp.to_i
    print "発刊日: "
    day = gets.chomp.to_i
    if year > 0 && month > 0 && day > 0
      book_info.pdate = Date.new( year, month, day )
    end
            
    # 分かりやすい名前の変数に検索データを代入する
    search = book_info
    # 該当する蔵書データを格納するハッシュを作る
    found_book = {}
    
    # 蔵書データを1軒ずつ取り出して検索データ(条件)と比較する
    @book_infos.each { |key, info|
      search_flag = 1 # 検索条件と一致しているか
      input_check = 0 # 検索条件が未入力か
      
      if search.title != ""
        search_flag = 0 if search.title =~ /[^"#{info.title}"]/
      else
        input_check += 1
      end
      if search.author != ""
        search_flag = 0 if search.author != info.author
      else
        input_check += 1
      end
      if search.page != 0
        search_flag = 0 if search.page != info.page
      else
        input_check += 1
      end
      if search.pdate != nil
        search_flag = 0 if search.pdate != info.pdate
      else
        input_check += 1
      end
      
      # 比較が全て一致した蔵書データをハッシュに格納する
      # 検索条件が全て未入力の場合は格納しない
      found_book[key] = info if search_flag == 1 && input_check <4
      }
    puts "\n------------------------------"
    if found_book.size > 0
      # 比較が全て一致した蔵書データを表示する 
      found_book.each { |key, info|
        print info.toFormattedString
        puts "\n------------------------------"
      }
    else
      print "条件に一致する蔵書はありません"
      puts "\n------------------------------"
    end 
  end
  
  # 処理の選択と選択後の処理を繰り返す
  def run
    while true
      # 機能選択画面を表示する
      print "
1. 蔵書データの登録
2. 蔵書データの表示
3. 蔵書データの検索
9. 終了
番号を選んでください(1,2,3,9):"
      
      # 文字の入力を待つ
      num = gets.chomp
      case
      when '1' == num
        # 蔵書データの登録
        addBookInfo
      when '2' == num
        # 蔵書データの表示
        listAllBookInfos
      when '3' == num
        # 蔵書データの検索
        searchBookInfos
      when '9' == num
        # アプリケーションの終了
        break;
      else
        # 処理選択待ち画面に戻る
      end
    end
  end

end

# アプリケーションのインスタンスを作る
book_info_manager = BookInfoManager.new

# BookInfoManagerの蔵書データをセットアップする
book_info_manager.setUp

# BookInfoManagerの処理の選択と選択後の処理を繰り返す
book_info_manager.run