Class: C::Tempfile

Inherits:
File
  • Object
show all
Defined in:
lib/cast/tempfile.rb

Constant Summary collapse

MAX_TRY =
10
@@cleanlist =
[]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(basename, tmpdir = Dir::tmpdir, suffix = nil) ⇒ Tempfile

Creates a temporary file of mode 0600 in the temporary directory whose name is basename.pid.n.suffix and opens with mode "w+". A Tempfile object works just like a File object.

If tmpdir is omitted, the temporary directory is determined by Dir::tmpdir provided by 'tmpdir.rb'. When $SAFE > 0 and the given tmpdir is tainted, it uses /tmp. (Note that ENV values are tainted by default)



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/cast/tempfile.rb', line 27

def initialize(basename, tmpdir=Dir::tmpdir, suffix=nil)
  if $SAFE > 0 and tmpdir.tainted?
    tmpdir = '/tmp'
  end

  lock = nil
  tmpname = nil
  n = failure = 0

  Thread.exclusive do
    begin
      begin
        tmpname = File.join(tmpdir, make_tmpname(basename, n, suffix))
        lock = tmpname + '.lock'
        n += 1
      end while @@cleanlist.include?(tmpname) or
      File.exist?(lock) or File.exist?(tmpname)

      Dir.mkdir(lock)
    rescue
      failure += 1
      retry if failure < MAX_TRY
      raise "cannot generate tempfile `%s'" % tmpname
    end
  end

  @data = [tmpname]
  @clean_proc = Tempfile.callback(@data)
  ObjectSpace.define_finalizer(self, @clean_proc)

  @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
  @tmpname = tmpname
  @@cleanlist << @tmpname
  @data[1] = @tmpfile
  @data[2] = @@cleanlist

  super(@tmpfile)

  # Now we have all the File/IO methods defined, you must not
  # carelessly put bare puts(), etc. after this.

  Dir.rmdir(lock)
end

Class Method Details

.callback(data) ⇒ Object

:nodoc:



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/cast/tempfile.rb', line 145

def callback(data)  # :nodoc:
  pid = $$
  lambda{
    if pid == $$ 
      path, tmpfile, cleanlist = *data

      print "removing ", path, "..." if $DEBUG

      tmpfile.close if tmpfile

      # keep this order for thread safeness
      File.unlink(path) if File.exist?(path)
      cleanlist.delete(path) if cleanlist

      print "done\n" if $DEBUG
    end
  }
end

.open(*args) ⇒ Object

If no block is given, this is a synonym for new().

If a block is given, it will be passed tempfile as an argument, and the tempfile will automatically be closed when the block terminates. In this case, open() returns nil.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/cast/tempfile.rb', line 169

def open(*args)
  tempfile = new(*args)

  if block_given?
    begin
      yield(tempfile)
    ensure
      tempfile.close
    end

    nil
  else
    tempfile
  end
end

Instance Method Details

#close(unlink_now = false) ⇒ Object

Closes the file. If the optional flag is true, unlinks the file after closing.

If you don't explicitly unlink the temporary file, the removal will be delayed until the object is finalized.



95
96
97
98
99
100
101
# File 'lib/cast/tempfile.rb', line 95

def close(unlink_now=false)
  if unlink_now
    close!
  else
    _close
  end
end

#close!Object

Closes and unlinks the file.



104
105
106
107
108
# File 'lib/cast/tempfile.rb', line 104

def close!
  _close
  @clean_proc.call
  ObjectSpace.undefine_finalizer(self)
end

#openObject

Opens or reopens the file with mode "r+".



77
78
79
80
81
82
# File 'lib/cast/tempfile.rb', line 77

def open
  @tmpfile.close if @tmpfile
  @tmpfile = File.open(@tmpname, 'r+')
  @data[1] = @tmpfile
  __setobj__(@tmpfile)
end

#pathObject

Returns the full path name of the temporary file.



128
129
130
# File 'lib/cast/tempfile.rb', line 128

def path
  @tmpname
end

#sizeObject Also known as: length

Returns the size of the temporary file. As a side effect, the IO buffer is flushed before determining the size.



134
135
136
137
138
139
140
141
# File 'lib/cast/tempfile.rb', line 134

def size
  if @tmpfile
    @tmpfile.flush
    @tmpfile.stat.size
  else
    0
  end
end

Unlinks the file. On UNIX-like systems, it is often a good idea to unlink a temporary file immediately after creating and opening it, because it leaves other programs zero chance to access the file.



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/cast/tempfile.rb', line 114

def unlink
  # keep this order for thread safeness
  begin
    File.unlink(@tmpname) if File.exist?(@tmpname)
    @@cleanlist.delete(@tmpname)
    @data = @tmpname = nil
    ObjectSpace.undefine_finalizer(self)
  rescue Errno::EACCES
    # may not be able to unlink on Windows; just ignore
  end
end