Rubyでパス文字列を組み立てるときはPathnameクラスを使おう
はじめに
Rubyで文字列操作だけでパス文字列を組み立てようとすると苦しいコードになりがちですが、Pathname
クラスを使うといい感じに書けることがわかったのでまとめました。
Pathnameとは
Rubyの組み込みクラスで、パス名をオブジェクト指向っぽく扱うことができます。
例えば、以下のようなインスタンスメソッドが用意されています。
directory?
:self
がディレクトリであればtrue
を返すsize
:self
のファイルサイズを返すsymlink?
:self
がシンボリックリンクである時にtrue
を返す
用意されているメソッドを見ればわかるように、自身の表すパスの情報を取得したりするときに便利です。
しかし、一番のポイントは、Pathname
のインスタンスはパス名を表しているだけであり、存在しないファイルのパス名も扱えるということです。
なので、システムとは無関係のパス文字列を組み立てる際にも利用することができます。
Pathnameを使わないコード
文字列操作だけでパス文字列を組み立てようとすると、苦しいコードになりがちです。
例えば以下のような感じ。
path = "users/#{item.user.id}/" path += if item.image? 'images' elsif item.movie? 'movies' else 'misc' end path += "/#{item.name}.#{item.extension}" puts path #=> users/46/photos/nogizaka.png
各階層を区切る/
をいちいちつけているのでとても読みにくいコードになっています。
また、/
をつける位置に気をつけないとすぐ不正なパス文字列になりそうです。
上のコードを改善案としてすぐ思いつくのは、各階層のファイル名を要素として持つ配列を用意し、これを最後にjoin('/')
などで結合して返すことです。
が、要素の中に'/path'
のような文字列が入っていたりすると、/
が重なって不正なパス文字列になってしまう可能性があるので、ベストな方法とはいえません。
Pathnameを使ったコード
上のコードをPathname
使って書き直すと以下のようになります。
require 'pathname' # Railsでは不要 path = Pathname.new('users').join(item.user.id.to_s) path += if item.image? 'images' elsif item.movie? 'movies' else 'misc' end path += "#{item.name}.#{item.extension}" puts path #=> users/46/photos/nogizaka.png
+
演算子や#join
を使うことで、/
を意識せずにパス文字列を組み立てることができます。
おわりに
実は、Rails.root
で返ってくるのもPathname
のインスタンスだったりします。