はじめに
こんにちは、@wai-doi です。RubyKaigi 2023 が楽しみですね。
最近お仕事で、Ruby の Tempfile.open
でテンポラリファイルを作成しているコードを読んで、理解する機会がありました。
Tempfile.open
のリファレンスマニュアルを調べてみると、他にもTempfile.create
でテンポラリファイルを作成できることがわかりました。
そのとき、Tempfile.open
と Tempfile.create
微妙な挙動の違いに興味を持ったため、調べたことを紹介しようと思います。
テンポラリファイルを作る 4 つの方法
Ruby の tempfile
ライブラリには、テンポラリファイルを作るための Tempfile
クラスが含まれています。
tempfile
ライブラリは組み込みライブラリではないため、require
が必要です。
require 'tempfile'
以降のサンプルコードでは省略しています。
Tempfile
クラスを利用して、テンポラリファイルを作成するには主に 4 つの方法があることがわかりました。
Tempfile.open
でブロックなしTempfile.open
でブロックありTempfile.create
でブロックなしTempfile.create
でブロックあり
これらの方法は微妙に挙動が異なるため、1 つずつ説明していきます。 サンプルコードは Ruby 3.2 で試しました。
今回は Ruby リファレンスマニュアルを参考にしました。
Tempfile.new (Ruby 3.2 リファレンスマニュアル) *1
Tempfile.create (Ruby 3.2 リファレンスマニュアル)
Tempfile.open でブロックなし
Tempfile.open
をブロックなしで呼び出す場合は、テンポラリファイルを作成し、Tempfile
オブジェクトを返します。
テンポラリファイルは自動的にクローズされません。
プログラムが終了すると、テンポラリファイルは自動的に削除されます。
tempfile = Tempfile.open tempfile.class #=> Tempfile tempfile.closed? #=> false tempfile.close tempfile.closed? #=> true # この時点ではテンポラリファイルが存在している File.exist?(tempfile.path) #=> true # プログラムが終了すると、テンポラリファイルが削除されている
Tempfile.open でブロックあり
Tempfile.open
をブロックありで呼び出す場合は、テンポラリファイルを作成し、ブロックの最後の値を返します。ブロック変数は Tempfile
オブジェクトです。もし変数に保持したいときは、ブロックの最後をブロック変数で終わる必要があります。
テンポラリファイルは、ブロックを抜けると自動的にクローズされます。 プログラムが終了すると、テンポラリファイルは自動的に削除されます。
tempfile = Tempfile.open do |tf| tf.class #=> Tempfile tf.closed? #=> false File.exist?(tf.path) #=> true # Tempfile オブジェクトを変数に保持したいときは、ブロック変数で終わる tf end # ブロックが終わると自動的にクローズされている tempfile.closed? #=> true # この時点ではテンポラリファイルが存在している File.exist?(tempfile.path) #=> true # プログラムが終了すると、テンポラリファイルが削除されている
Tempfile.create でブロックなし
Tempfile.create
をブロックなしで呼び出す場合は、テンポラリファイルを作成し、File
オブジェクトを返します。
テンポラリファイルは自動的にクローズされません。 プログラムが終了しても、テンポラリファイルは自動的に削除されません!
file = Tempfile.create file.class #=> File file.closed? #=> false file.close file.closed? #=> true # この時点ではテンポラリファイルが存在している File.exist?(file.path) #=> true # プログラムが終了しても、テンポラリファイルが削除されず残ってしまう!
プログラムが終了してもテンポラリファイルが削除されないため、終了の前に明示的に削除する必要があります。
# 明示的にテンポラリファイルを削除しないといけない File.delete(file.path) File.exist?(file.path) #=> false
Tempfile.create でブロックあり
Tempfile.create
をブロックありで呼び出す場合は、テンポラリファイルを作成し、ブロックの最後の値を返します。ブロック変数は File
オブジェクトです。もし変数に保持したいときは、ブロックの最後をブロック変数で終わる必要があります。
テンポラリファイルは、ブロックを抜けると自動的にクローズと削除されます。
file = Tempfile.create do |f| f.class #=> File f.closed? #=> false File.exist?(f.path) #=> true # File オブジェクトを変数に保持したいときは、ブロック変数で終わる f end # ブロックが終わると自動的にクローズされている file.closed? #=> true # テンポラリファイルが削除されている File.exist?(file.path) #=> false
結果
結果を表にまとめてみました。
書き方 | ブロック変数 | 戻り値 | 自動クローズ | 自動削除 |
---|---|---|---|---|
open でブロックなし | なし | Tempfile | されない | 終了したとき |
open でブロックあり | Tempfile | ブロックの最後の値 | ブロックを抜けたとき | 終了したとき |
create でブロックなし | なし | File | されない | されない |
create でブロックあり | File | ブロックの最後の値 | ブロックを抜けたとき | ブロックを抜けたとき |
まとめ
今回はテンポラリファイルを作り4つの方法を紹介してみました。
個人的には、Tempfile.create
でブロックなしの方法で、テンポラリファイルが自動で削除されないのは意外でした。
みなさんも、テンポラリファイルが削除されるタイミングなどに注意して、 Ruby でテンポラリファイルをぜひ利用してみてください。
永和システムマネジメントでは、Ruby とアジャイルソフトウェア開発を通じてコミュニティと成長したいエンジニアを絶賛募集しています。
*1:※ new と open はエイリアスです。ブロックなしに限り、Tempfile.open は Tempfile.new と書くことができます。