Read more

Prefer using Dir.mktmpdir when dealing with temporary directories in Ruby

Emanuel
September 12, 2022Software engineer at makandra GmbH

Ruby's standard library includes a class for creating temporary directories. Similar to Tempfile Show archive.org snapshot it creates a unique directory name.

Illustration UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
Read more Show archive.org snapshot

Note:

  • You need to use a block or take care of the cleanup manually
  • You can create a prefix and suffix e.g. Dir.mktmpdir(['foo', 'bar']) => /tmp/foo20220912-14561-3g93n1bar
  • You can choose a different base directory than Dir.tmpdir e.g. Dir.mktmpdir('foo', Rails.root.join('tmp')) => /home/user/rails_example/tmp/foo20220912-14561-pyr8qd. This might be necessary when your tests are running on CI. For this you might also want to commit tmp/.gitkeep to git so the tmp dir inside your project is present.

Example

Dir.mktmpdir('exports')
=> "/tmp/exports20220912-14561-pobh0a"

Improving your parallel tests with Dir.mktmpdir

When creating directories in parallel tests manually, you normally need to handle these issues:

  1. When multiple processes read and write to a directory with the same name e.g. tmp/some-test-folder you might end up in flaky specs.
  2. There is no guarantee that a folder is always removed e.g. in case your test run crashes. Following tests might see outdated files from a previous test run.

#mktmpdir solves these. However, you should still remove them after you're done. Here are three options:

Option 1: Using a block

it 'does something with a temporary directory' do
  Dir.mktmpdir('exports') do |export_folder_path|
    # ...
  end
end

Option 2: Ensuring the removal of the folder

it 'does something with a temporary directory' do
  export_path = Dir.mktmpdir('exports')
  # ...
ensure
  FileUtils.remove_entry(export_path)
end

Option 3: Using a before/after block

before { @export_path = Dir.mktmpdir('exports') }
after do
  if @export_path.present?
    FileUtils.remove_entry(@export_path)
  end
end

it 'does something with a temporary directory' do
  # ...
end

Option 4: Without Dir.mktmpdir

Use something like "#{Rails.env}#{ENV['TEST_ENV_NUMBER']}" in you directory path when using Dir.mkdir.


Further reading:

Emanuel
September 12, 2022Software engineer at makandra GmbH
Posted by Emanuel to makandra dev (2022-09-12 10:45)