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のインスタンスだったりします。