ESM アジャイル事業部 開発者ブログ

永和システムマネジメント アジャイル事業部の開発者ブログです。

Tempfile.open と Tempfile.create の違い

はじめに

こんにちは、@wai-doi です。RubyKaigi 2023 が楽しみですね。

最近お仕事で、Ruby の Tempfile.open でテンポラリファイルを作成しているコードを読んで、理解する機会がありました。 Tempfile.open のリファレンスマニュアルを調べてみると、他にもTempfile.create でテンポラリファイルを作成できることがわかりました。

そのとき、Tempfile.openTempfile.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 とアジャイルソフトウェア開発を通じてコミュニティと成長したいエンジニアを絶賛募集しています。

agile.esm.co.jp

*1:※ new と open はエイリアスです。ブロックなしに限り、Tempfile.open は Tempfile.new と書くことができます。