ゲームっぽい何かを作りながらRubyを勉強する その2 〜ファイル保存〜

More than 1 year has passed since last update.

「ゲームっぽい何かを作りながらRubyを勉強する」シリーズ、第2回は入出力ファイルの保存先についてです。

前回、セーブデータを保存する簡単なプログラムを書いてtest-unitで実行するということを試してみました。
実際に動いてファイルが出力されたのは良いのですが、そのままではファイルの出力先がプログラムの実行時のカレントディレクトリ、という残念な感じでした。

今回は、そのファイルの出力先問題を解決すべく、出力ファイルはすべてプロジェクト直下のdataディレクトりに保存されるようプログラムを修正してみます。

シリーズ一覧

本文

まず、常にdataディレクトリにファイルを保存するためにはどのような要素が必要かを考えてみます。

  1. プログラム実行時のカレントディレクトリを取得する
  2. カレントディレクトリとdataディレクトリ名、ファイル名を連結する。

これだけですね。

では一つずつ見ていきます。

プログラム実行時のカレントディレクトリを取得する

まず、このプロジェクトの実行は必ずプロジェクト直下と決めておきます。
これによって、プログラム実行時のカレントディレクトリを固定し、そのパスをプログラム中で取得することで、どのプログラムファイルから実行されても同じ場所にファイルを保存できるようにします。

プログラム中でカレントディレクトリを取得するには以下のメソッドを使います。

Dir.pwd

念のため、このメソッドで実際に取得できる文字列がどのようなものか、irbを使って確認します。

$ pwd
/usr/local/workdir/ruby/mygame

$ irb
irb(main):001:0>p Dir.pwd
"/usr/local/workdir/ruby/mygame"
=> "/usr/local/workdir/ruby/mygame"

Bashでpwdコマンドを実行した時と同じ文字列が取得できますね。

参考

Ruby 2.3.0 リファレンスマニュアル > ライブラリ一覧 > 組み込みライブラリ > Dirクラス

カレントディレクトリとdataディレクトリ名、ファイル名を連結する。

さて、Dir.pwdでカレントディレクトリが取得できたら、あとはdataとファイル名を連結していくだけなのですが、この時以下のように単純に+で連結するのはイケていないです。

Dir.pwd + '/data/' + file_name

パスを区切るための/がそれぞれの変数についているのかどうか、そもそも区切り文字は/でいいのか(Windowsでは¥)など、いろいろと考慮しなければならない点が出てくるからです。考慮しなければならない、ということは懸念点も増え、実装量も増え、バグが生まれる原因になってしまいます。

こんな時、JavaのPathsクラスのように、大体の言語は「ファイルパスを組み立てる」ためのAPIが用意されています。RubyではPathnameというクラスが使えます。

というわけで、先ほどの例をPathnameを使って書き直したのがこちらです。

Pathname.new(Dir.pwd).join(DATA_DIR, file_name).to_s

/も区切り文字も全く使わず、配列だけでパスをすべて表せていますね。これなら環境依存のバグも少なそうです。

参考

Ruby 2.3.0 リファレンスマニュアル > ライブラリ一覧 > pathnameライブラリ > Pathnameクラス

結果

以上で、「セーブファイルは常にdataディレクトリ内に保存する」という仕組みを実現することができました。
修正後のsave_manager.rbは以下になります。

save_manager.rb
require 'pathname'

module MyGame
  # module for managing game data
  module SaveManager
    DATA_DIR = 'data'

    # save given data into given file at data/
    def self.save(file_name, data)
      File.open(Pathname.new(Dir.pwd).join(DATA_DIR, file_name).to_s, 'w') do |f|
        f.puts(data)
      end
    end
  end
end

まとめ

今回はファイル入出力時のパス指定について調べてみました。
言語に関係なく、ファイルの入出力は実行環境によって結果が変わらないようにするのが重要かと思います。文字列の加算でも動いてしまいますが、マシンのOSが違う可能性のある他の人の環境でもちゃんと動くことを意識することで、より安定したプログラムになるのではないかと思います。

なお、この記事は、以下の時点までの変更を元に書いています。

https://github.com/chooyan-eng/mygame/tree/6292d92bf183126a224e0b3cf51d226116c0fbe4