Skip to content

kritoke/crtemp

 
 

Repository files navigation

crtemp.cr

Creates a temporary directory with atomic mkdtemp/mkstemp support.

Features

  • Atomic creation: Uses mkdtemp on Unix and CreateFileA on Windows for race-free temp directory/file creation
  • Secure permissions: Directories created with 0o700, files with 0o600 (owner-only)
  • Cross-platform: Works on Unix/Linux/macOS and Windows
  • Result-based API: All operations return Result types for explicit error handling
  • Automatic cleanup: Crtemp removes contents on #close, block form auto-cleans

Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      crtemp:
        github: kritoke/crtemp
  2. Run shards install

Usage

require "crtemp"

Dir.mktmpdir (block form uses FunctionalCrtemp)

dir = Dir.mktmpdir(*args)

Creates a temporary directory. Arguments are passed to File.tempname as-is. See File.tempname.

The returning object is Crtemp. It removes all entries when #close-d.

With block, the created directory will be removed after block is left. The block form now uses a functional helper under the hood to guarantee cleanup.

Dir.mktmpdir do |dir|
  # work with dir
end

Functional API

This library now exposes a small functional-style API via FunctionalCrtemp. It keeps resource management explicit and returns Result values for fallible operations.

Block-style (guaranteed cleanup):

FunctionalCrtemp.with_crtemp do |path|
  # use path (directory is removed after the block)
end

Non-block creation (explicit handle, success wrapped in a Result):

res = FunctionalCrtemp.create
if res.success?
  info = res.value!
  # info.path is the directory path
  info.close
else
  STDERR.puts res.error!.message
end

create_tempfile_result now returns a TempdirResult::Result(String, Crtemp::Error):

res = FunctionalCrtemp.create
info = res.value!
file_res = info.create_tempfile("myprefix_")
if file_res.success?
  path = file_res.value!
  # use the file
else
  STDERR.puts file_res.error!.message
end
info.close

Migration note

Existing code that used Crtemp.new or Dir.mktmpdir can keep working. Dir.mktmpdir still supports the block form and the non-block form now returns a value-like FunctionalCrtemp::Info (the non-block Dir.mktmpdir will raise on creation failure). If you prefer non-raising, switch to FunctionalCrtemp.create which returns an explicit Result you can handle.

Note: The deprecated create_tempfile method has been removed. Use create_tempfile_result instead for all temporary file creation.

Migration Guide

Before (v1.2.1 of original tempdir library):

require "tempdir"

# Using deprecated create_tempfile method
tempdir = Tempdir.new
path = tempdir.create_tempfile("prefix_")
puts path if path

# Or with exception handling
begin
  path = tempdir.create_tempfile("prefix_", raise_on_failure: true)
rescue ex : Tempdir::TempfileError
  puts ex.message
end

After (v0.1.0 crtemp):

require "crtemp"

# Using Result-based create_tempfile_result
crtemp = Crtemp.new  
result = crtemp.create_tempfile_result("prefix_")
if result.success?
  path = result.value!
  puts path
else
  # handle error
  puts result.error!.message
end

# Block form remains unchanged and is recommended
Dir.mktmpdir do |dir|
  result = dir.create_tempfile_result("prefix_")
  if result.success?
    # use result.value!
  end
end

For new code, prefer the functional API:

# Functional approach with automatic cleanup
FunctionalCrtemp.with_crtemp do |dir_path|
  # work with dir_path
  file_result = FunctionalCrtemp.create.then { |info| info.create_tempfile("prefix_") }
  if file_result.success?
    # use file_result.value!
  end
end

Crtemp

The temporary directory class based on Dir.

This class only rewrites the #close method to remove entries in the directory.

create_tempfile_result

Create a secure tempfile inside the crtemp using atomic mkstemp:

Dir.mktmpdir do |dir|
  result = dir.create_tempfile_result("myfile_", data: Slice(UInt8).new([1, 2, 3]))
  if result.success?
    path = result.value!
    # path is the created file path with 0o600 permissions
  end
end

The data parameter is optional. If provided, writes the bytes atomically. The file is created with owner-only permissions (0o600) where supported.

Security

  • Directories are created with 0o700 permissions (owner-only).
  • Files created via create_tempfile_result use mkstemp for atomic creation and are set to 0o600 (owner read/write only).
  • On Unix, uses mkdtemp/mkstemp for atomic creation avoiding TOCTOU races.
  • On Windows, falls back to CreateFileA with CREATE_NEW flag for atomic semantics.

Development

crystal spec

Contributing

  1. Fork it (https://github.com/kritoke/crtemp/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

License

MIT

About

Crystal library for creating temporary directories

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages

  • Crystal 90.0%
  • Nix 10.0%