Hitsuji_monのブログ~ 村上春樹のあれ ~

文学好きな組み込み系エンジニア

【#100DaysOfCode】Day13 「作りながら学ぶRuby入門」17章

オブジェクトを保存する方法として、今回は

  • データベースとして保存

する方法を用いる。

Day13

「作りながら学ぶRuby入門」17章 PStore:データベースの扱い

蔵書データのファイル保存

  • CSVファイルで保存 (前記事)
  • PStoreでデータベースとして保存 (本記事)

これまでのハッシュの代わりみたいな振る舞い

ハッシュの場合 データベースの場合
メモリ上のファイルをキーと値の組で管理 メモリの外のファイルもキーと値で指定して管理

こんな感じ。

まずはデータベースへの書き込み・読み込みオブジェクト

# 「作りながら学ぶRuby入門」17章
# PStoreにデータを書き込んでから、読み込む
require 'pstore'

# 文字列を作成
juice = "orange juice"
# 配列にデータを作成
fruits = ["apple", "banana", "cherry", "fig", "grape"]

# PStoreデータベースを作成する
db = PStore.new('fruitdb')

# PStoreにデータを書き込む
db.transaction do
    # 文字列をPStoreに保存する
    db["drink"] = juice
    # 配列をPStoreに保存する
    db["fruits"] = fruits
end                                 # PStoreに保存されるのは、transactionメソッドを抜ける時

# PStoreからデータを読み込んで表示する
db.transaction(true) do
    puts "drink: #{db["drink"]}"
    puts "fruits: #{db["fruits"].join(",")}"
end

# PStoreが読み込みモードの時に書き込もうとすると、
# エラーになる
db.transaction(true) do
    db["drink"] = "grape juice"
end

データベース使用の流れ

  • DBのインスタンスを作成する
    • このとき、引数でDB名を指定しておく
  • DBへの書き込み
    • DBハッシュ.transaction do
  • DBからの読み込み
    • DBハッシュ.transaction(true) do

読み込みモード中に書き込もうとすると、ちゃんとエラーを出す


これが基本的なデータベースの扱い方。
まんまこれまでのハッシュの扱いと同じで助かる。
ライブラリのインポートが追加されただけ。

ということで、
【データベース機能を追加した蔵書管理アプリケーション】
のコードが以下。

# 「作りながら学ぶRuby入門」 17章
# PStoreを使って、データベースからの読み込み・書き込み・削除を行う

require 'date'
require 'pstore'

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

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

    # BookInfoクラスのインスタンスの文字列表現を返す
    def to_s
        "#@title, #@author, #@page, #@p_date"
    end
        
    # 蔵書データを書式をつけて出力する
    def toFormattedString( sep = "\n")
        "書籍名: #{@title}#{sep}, 著者名: #{@author}#{sep}, ページ数: #{@page}ページ#{sep}
         発刊日: #{@p_date}#{sep}"
    end
end

# BookInfoManagerクラスを定義する
class BookInfoManager
    def initialize( pstore_name )
        # PStoreデータベースファイルを指定して、初期化
        @db = PStore.new(pstore_name)
    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.p_date = Date.new( year, month, day )

        # 作成した蔵書データ1件分をPStoreデータベースに登録する
        @db.transaction do
            # 蔵書データをPStoreに保存する
            @db[key] = book_info
        end
    end

    # PStoreからデータベース情報を読み込み、一覧を表示
    def listAllBookInfos
        puts "\n-----------------------------------------------"
        @db.transaction(true) do    
            # rootsがキーの配列を返し、eachでそれを1件ずつ処理
            @db.roots.each{ |key|
                # 得られたキーを使ってPStoreから蔵書データ(BookInfo)を取得
                # それを書式をつけて出力する
                puts "キー: #{key}"
                print @db[key].toFormattedString
                puts "\n-----------------------------------------------"
            }
        end
    end

    # 蔵書データを1件ずつデータベースから削除する
    def delBookInfo
        # キーを指定してもらう
        print "\n"
        print "キーを指定してください: "
        key = gets.chomp

        # 削除対象データを確認してから、削除する
        @db.transaction do
            if @db.root?(key)
                print @db[key].toFormattedString
                print "\n削除しますか? (Y/yなら削除を実行します): "
                # 読み込んだ値を大文字に揃える
                yesno = gets.chomp.upcase
                if /^Y$/ =~ yesno
                    # Yが1文字の時だけ,PStoreデータベースから削除する
                    @db.delete(key)
                    puts "\nデータベースから削除しました"
                end
            end
        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
                # 蔵書データの削除
                delBookInfo
            when '9' == num
                # アプリケーションの終了
                break;
            else
                # 処理選択待ち画面に戻る
            end
        end
    end


end


# ここからがアプリケーションを動かす本体

# アプリケーションのインスタンスを作る
# 蔵書データのPstoreデータベースを指定している
book_info_manager = BookInfoManager.new("book_info.db")

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


データベースクラス関係のポイント

表示クラス listAllBookInfos

  • 配列のキーを指定して、表示処理を1件ずつ行う
  • 得られたキーを使って、PStoreからデータベース情報を取得

削除クラス delBookInfos

  • データベースハッシュに (削除対象キー) が含まれているか判定
  • 正規表現を使って、削除処理を選択
    • /^Y$/ 文頭から文末に Y が含まれていれば処理を実行


今日はゴリゴリ進んでよかった