Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 97 additions & 48 deletions lib/tempfile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,50 +87,64 @@
# same Tempfile object from multiple threads then you should protect it with a
# mutex.
class Tempfile < DelegateClass(File)
# Creates a temporary file with permissions 0600 (= only readable and
# writable by the owner) and opens it with mode "w+".

# Creates a file in the underlying file system;
# returns a new \Tempfile object based on that file.
#
# If possible, consider instead using Tempfile.create, which:
#
# - Avoids the performance cost of delegation,
# incurred when Tempfile.new calls its superclass <tt>DelegateClass(File)</tt>.
# - Does not rely on a finalizer to close and unlink the file,
# which can be unreliable.
#
# Creates and returns file whose:
#
# - Class is \Tempfile (not \File, as in Tempfile.create).
# - Directory is the system temporary directory (system-dependent).
# - Generated filename is unique in that directory.
# - Permissions are <tt>0600</tt>;
# see {File Permissions}[https://docs.ruby-lang.org/en/master/File.html#label-File+Permissions].
# - Mode is <tt>'w+'</tt> (read/write mode, positioned at the end).
#
# The underlying file is removed when the \Tempfile object dies
# and is reclaimed by the garbage collector.
#
# It is recommended to use Tempfile.create { ... } instead when possible,
# because that method avoids the cost of delegation and does not rely on a
# finalizer to close and unlink the file, which is unreliable.
# Example:
#
# The +basename+ parameter is used to determine the name of the
# temporary file. You can either pass a String or an Array with
# 2 String elements. In the former form, the temporary file's base
# name will begin with the given string. In the latter form,
# the temporary file's base name will begin with the array's first
# element, and end with the second element. For example:
# f = Tempfile.new # => #<Tempfile:/tmp/20220505-17839-1s0kt30>
# f.class # => Tempfile
# f.path # => "/tmp/20220505-17839-1s0kt30"
# f.stat.mode.to_s(8) # => "100600"
# File.exist?(f.path) # => true
# File.unlink(f.path) #
# File.exist?(f.path) # => false
#
# file = Tempfile.new('hello')
# file.path # => something like: "/tmp/hello2843-8392-92849382--0"
# Argument +basename+, if given, may be one of:
#
# # Use the Array form to enforce an extension in the filename:
# file = Tempfile.new(['hello', '.jpg'])
# file.path # => something like: "/tmp/hello2843-8392-92849382--0.jpg"
# - A string: the generated filename begins with +basename+:
#
# The temporary file will be placed in the directory as specified
# by the +tmpdir+ parameter. By default, this is +Dir.tmpdir+.
# Tempfile.new('foo') # => #<Tempfile:/tmp/foo20220505-17839-1whk2f>
#
# file = Tempfile.new('hello', '/home/aisaka')
# file.path # => something like: "/home/aisaka/hello2843-8392-92849382--0"
# - An array of two strings <tt>[prefix, suffix]</tt>:
# the generated filename begins with +prefix+ and ends with +suffix+:
#
# You can also pass an options hash. Under the hood, Tempfile creates
# the temporary file using +File.open+. These options will be passed to
# +File.open+. This is mostly useful for specifying encoding
# options, e.g.:
# Tempfile.new(%w/foo .jpg/) # => #<Tempfile:/tmp/foo20220505-17839-58xtfi.jpg>
#
# Tempfile.new('hello', '/home/aisaka', encoding: 'ascii-8bit')
# With arguments +basename+ and +tmpdir+, the file is created in directory +tmpdir+:
#
# # You can also omit the 'tmpdir' parameter:
# Tempfile.new('hello', encoding: 'ascii-8bit')
# Tempfile.new('foo', '.') # => #<Tempfile:./foo20220505-17839-xfstr8>
#
# Note: +mode+ keyword argument, as accepted by Tempfile, can only be
# numeric, combination of the modes defined in File::Constants.
# Keyword arguments +mode+ and +options+ are passed directly to method
# {File.open}[https://docs.ruby-lang.org/en/master/File.html#method-c-open]:
#
# === Exceptions
# - The value given with +mode+ must be an integer,
# and may be expressed as the logical OR of constants defined in
# {File::Constants}[https://docs.ruby-lang.org/en/master/File/Constants.html].
# - For +options+, see {Open Options}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options].
#
# Related: Tempfile.create.
#
# If Tempfile.new cannot find a unique filename within a limited
# number of tries, then it will raise an exception.
def initialize(basename="", tmpdir=nil, mode: 0, **options)
warn "Tempfile.new doesn't call the given block.", uplevel: 1 if block_given?

Expand Down Expand Up @@ -325,26 +339,61 @@ def open(*args, **kw)
end
end

# Creates a temporary file as a usual File object (not a Tempfile).
# It does not use finalizer and delegation, which makes it more efficient and reliable.
# Creates a file in the underlying file system;
# returns a new \File object based on that file.
#
# If no block is given, this is similar to Tempfile.new except
# creating File instead of Tempfile. In that case, the created file is
# not removed automatically. You should use File.unlink to remove it.
# With no block given and no arguments, creates and returns file whose:
#
# If a block is given, then a File object will be constructed,
# and the block is invoked with the object as the argument.
# The File object will be automatically closed and
# the temporary file is removed after the block terminates,
# releasing all resources that the block created.
# The call returns the value of the block.
# - Class is {File}[https://docs.ruby-lang.org/en/master/File.html] (not \Tempfile).
# - Directory is the system temporary directory (system-dependent).
# - Generated filename is unique in that directory.
# - Permissions are <tt>0600</tt>;
# see {File Permissions}[https://docs.ruby-lang.org/en/master/File.html#label-File+Permissions].
# - Mode is <tt>'w+'</tt> (read/write mode, positioned at the end).
#
# In any case, all arguments (+basename+, +tmpdir+, +mode+, and
# <code>**options</code>) will be treated the same as for Tempfile.new.
# With no block, the file is not removed automatically,
# and so should be explicitly removed.
#
# Tempfile.create('foo', '/home/temp') do |f|
# # ... do something with f ...
# end
# Example:
#
# f = Tempfile.create # => #<File:/tmp/20220505-9795-17ky6f6>
# f.class # => File
# f.path # => "/tmp/20220505-9795-17ky6f6"
# f.stat.mode.to_s(8) # => "100600"
# File.exist?(f.path) # => true
# File.unlink(f.path)
# File.exist?(f.path) # => false
#
# Argument +basename+, if given, may be one of:
#
# - A string: the generated filename begins with +basename+:
#
# Tempfile.create('foo') # => #<File:/tmp/foo20220505-9795-1gok8l9>
#
# - An array of two strings <tt>[prefix, suffix]</tt>:
# the generated filename begins with +prefix+ and ends with +suffix+:
#
# Tempfile.create(%w/foo .jpg/) # => #<File:/tmp/foo20220505-17839-tnjchh.jpg>
#
# With arguments +basename+ and +tmpdir+, the file is created in directory +tmpdir+:
#
# Tempfile.create('foo', '.') # => #<File:./foo20220505-9795-1emu6g8>
#
# Keyword arguments +mode+ and +options+ are passed directly to method
# {File.open}[https://docs.ruby-lang.org/en/master/File.html#method-c-open]:
#
# - The value given with +mode+ must be an integer,
# and may be expressed as the logical OR of constants defined in
# {File::Constants}[https://docs.ruby-lang.org/en/master/File/Constants.html].
# - For +options+, see {Open Options}[https://docs.ruby-lang.org/en/master/IO.html#class-IO-label-Open+Options].
#
# With a block given, creates the file as above, passes it to the block,
# and returns the block's value;
# before the return, the file object is closed and the underlying file is removed:
#
# Tempfile.create {|file| file.path } # => "/tmp/20220505-9795-rkists"
#
# Related: Tempfile.new.
#
def Tempfile.create(basename="", tmpdir=nil, mode: 0, **options)
tmpfile = nil
Expand Down