rubysl-tempfile 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/lib/rubysl/tempfile/tempfile.rb +246 -109
- data/lib/rubysl/tempfile/version.rb +1 -1
- data/rubysl-tempfile.gemspec +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f82c47d38159bb53a71a207db4db87b6d0ace1d
|
4
|
+
data.tar.gz: 026e066a5774dbfb057f861cc78a9ae7717be308
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: daa1c9f4243df03b5f6266f0c451b6cba2fd1c499eab3e814575953a6c0124b3dbbe4d792c4c3e0ef11b852b41e88d24e8835ee7cb4c284027f573ee4f419b02
|
7
|
+
data.tar.gz: c6485ac719d85e2fd2ace909a61a52cccc0883752de27ab15e1ba53022745ced1d5d3388465a99366669ff4d255435577493226a7b49e7d050fb20a55e33b52e
|
data/.travis.yml
CHANGED
@@ -1,94 +1,166 @@
|
|
1
1
|
#
|
2
2
|
# tempfile - manipulates temporary files
|
3
3
|
#
|
4
|
-
# $Id: tempfile.rb
|
4
|
+
# $Id: tempfile.rb 31768 2011-05-28 23:31:24Z yugui $
|
5
5
|
#
|
6
6
|
|
7
7
|
require 'delegate'
|
8
8
|
require 'tmpdir'
|
9
|
+
require 'thread'
|
9
10
|
|
10
|
-
# A class for managing temporary files.
|
11
|
-
#
|
11
|
+
# A utility class for managing temporary files. When you create a Tempfile
|
12
|
+
# object, it will create a temporary file with a unique filename. A Tempfile
|
13
|
+
# objects behaves just like a File object, and you can perform all the usual
|
14
|
+
# file operations on it: reading data, writing data, changing its permissions,
|
15
|
+
# etc. So although this class does not explicitly document all instance methods
|
16
|
+
# supported by File, you can in fact call any File instance method on a
|
17
|
+
# Tempfile object.
|
18
|
+
#
|
19
|
+
# == Synopsis
|
20
|
+
#
|
21
|
+
# require 'tempfile'
|
22
|
+
#
|
23
|
+
# file = Tempfile.new('foo')
|
24
|
+
# file.path # => A unique filename in the OS's temp directory,
|
25
|
+
# # e.g.: "/tmp/foo.24722.0"
|
26
|
+
# # This filename contains 'foo' in its basename.
|
27
|
+
# file.write("hello world")
|
28
|
+
# file.rewind
|
29
|
+
# file.read # => "hello world"
|
30
|
+
# file.close
|
31
|
+
# file.unlink # deletes the temp file
|
32
|
+
#
|
33
|
+
# == Good practices
|
34
|
+
#
|
35
|
+
# === Explicit close
|
36
|
+
#
|
37
|
+
# When a Tempfile object is garbage collected, or when the Ruby interpreter
|
38
|
+
# exits, its associated temporary file is automatically deleted. This means
|
39
|
+
# that's it's unnecessary to explicitly delete a Tempfile after use, though
|
40
|
+
# it's good practice to do so: not explicitly deleting unused Tempfiles can
|
41
|
+
# potentially leave behind large amounts of tempfiles on the filesystem
|
42
|
+
# until they're garbage collected. The existance of these temp files can make
|
43
|
+
# it harder to determine a new Tempfile filename.
|
44
|
+
#
|
45
|
+
# Therefore, one should always call #unlink or close in an ensure block, like
|
46
|
+
# this:
|
47
|
+
#
|
48
|
+
# file = Tempfile.new('foo')
|
49
|
+
# begin
|
50
|
+
# ...do something with file...
|
51
|
+
# ensure
|
52
|
+
# file.close
|
53
|
+
# file.unlink # deletes the temp file
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# === Unlink after creation
|
57
|
+
#
|
58
|
+
# On POSIX systems, it's possible to unlink a file right after creating it,
|
59
|
+
# and before closing it. This removes the filesystem entry without closing
|
60
|
+
# the file handle, so it ensures that only the processes that already had
|
61
|
+
# the file handle open can access the file's contents. It's strongly
|
62
|
+
# recommended that you do this if you do not want any other processes to
|
63
|
+
# be able to read from or write to the Tempfile, and you do not need to
|
64
|
+
# know the Tempfile's filename either.
|
65
|
+
#
|
66
|
+
# For example, a practical use case for unlink-after-creation would be this:
|
67
|
+
# you need a large byte buffer that's too large to comfortably fit in RAM,
|
68
|
+
# e.g. when you're writing a web server and you want to buffer the client's
|
69
|
+
# file upload data.
|
70
|
+
#
|
71
|
+
# Please refer to #unlink for more information and a code example.
|
72
|
+
#
|
73
|
+
# == Minor notes
|
74
|
+
#
|
75
|
+
# Tempfile's filename picking method is both thread-safe and inter-process-safe:
|
76
|
+
# it guarantees that no other threads or processes will pick the same filename.
|
77
|
+
#
|
78
|
+
# Tempfile itself however may not be entirely thread-safe. If you access the
|
79
|
+
# same Tempfile object from multiple threads then you should protect it with a
|
80
|
+
# mutex.
|
12
81
|
class Tempfile < DelegateClass(File)
|
13
|
-
MAX_TRY = 10
|
14
|
-
|
15
|
-
|
16
|
-
# Creates a temporary file of mode 0600 in the temporary directory,
|
17
|
-
# opens it with mode "w+", and returns a Tempfile object which
|
18
|
-
# represents the created temporary file. A Tempfile object can be
|
19
|
-
# treated just like a normal File object.
|
20
|
-
#
|
21
|
-
# The basename parameter is used to determine the name of a
|
22
|
-
# temporary file. If an Array is given, the first element is used
|
23
|
-
# as prefix string and the second as suffix string, respectively.
|
24
|
-
# Otherwise it is treated as prefix string.
|
25
|
-
#
|
26
|
-
# If tmpdir is omitted, the temporary directory is determined by
|
27
|
-
# Dir::tmpdir provided by 'tmpdir.rb'.
|
28
|
-
# When $SAFE > 0 and the given tmpdir is tainted, it uses
|
29
|
-
# /tmp. (Note that ENV values are tainted by default)
|
30
|
-
def initialize(basename, tmpdir=Dir::tmpdir)
|
31
|
-
if $SAFE > 0 and tmpdir.tainted?
|
32
|
-
tmpdir = '/tmp'
|
33
|
-
end
|
34
|
-
|
35
|
-
lock = nil
|
36
|
-
n = failure = 0
|
82
|
+
MAX_TRY = 10 # :nodoc:
|
83
|
+
include Dir::Tmpname
|
37
84
|
|
38
|
-
|
39
|
-
|
85
|
+
# call-seq:
|
86
|
+
# new(basename, [tmpdir = Dir.tmpdir], [options])
|
87
|
+
#
|
88
|
+
# Creates a temporary file with permissions 0600 (= only readable and
|
89
|
+
# writable by the owner) and opens it with mode "w+".
|
90
|
+
#
|
91
|
+
# The +basename+ parameter is used to determine the name of the
|
92
|
+
# temporary file. You can either pass a String or an Array with
|
93
|
+
# 2 String elements. In the former form, the temporary file's base
|
94
|
+
# name will begin with the given string. In the latter form,
|
95
|
+
# the temporary file's base name will begin with the array's first
|
96
|
+
# element, and end with the second element. For example:
|
97
|
+
#
|
98
|
+
# file = Tempfile.new('hello')
|
99
|
+
# file.path # => something like: "/tmp/hello2843-8392-92849382--0"
|
100
|
+
#
|
101
|
+
# # Use the Array form to enforce an extension in the filename:
|
102
|
+
# file = Tempfile.new(['hello', '.jpg'])
|
103
|
+
# file.path # => something like: "/tmp/hello2843-8392-92849382--0.jpg"
|
104
|
+
#
|
105
|
+
# The temporary file will be placed in the directory as specified
|
106
|
+
# by the +tmpdir+ parameter. By default, this is +Dir.tmpdir+.
|
107
|
+
# When $SAFE > 0 and the given +tmpdir+ is tainted, it uses
|
108
|
+
# '/tmp' as the temporary directory. Please note that ENV values
|
109
|
+
# are tainted by default, and +Dir.tmpdir+'s return value might
|
110
|
+
# come from environment variables (e.g. <tt>$TMPDIR</tt>).
|
111
|
+
#
|
112
|
+
# file = Tempfile.new('hello', '/home/aisaka')
|
113
|
+
# file.path # => something like: "/home/aisaka/hello2843-8392-92849382--0"
|
114
|
+
#
|
115
|
+
# You can also pass an options hash. Under the hood, Tempfile creates
|
116
|
+
# the temporary file using +File.open+. These options will be passed to
|
117
|
+
# +File.open+. This is mostly useful for specifying encoding
|
118
|
+
# options, e.g.:
|
119
|
+
#
|
120
|
+
# Tempfile.new('hello', '/home/aisaka', :encoding => 'ascii-8bit')
|
121
|
+
#
|
122
|
+
# # You can also omit the 'tmpdir' parameter:
|
123
|
+
# Tempfile.new('hello', :encoding => 'ascii-8bit')
|
124
|
+
#
|
125
|
+
# === Exceptions
|
126
|
+
#
|
127
|
+
# If Tempfile.new cannot find a unique filename within a limited
|
128
|
+
# number of tries, then it will raise an exception.
|
129
|
+
def initialize(basename, *rest)
|
130
|
+
@data = []
|
131
|
+
@clean_proc = Remover.new(@data)
|
132
|
+
ObjectSpace.define_finalizer(self, @clean_proc)
|
40
133
|
|
134
|
+
create(basename, *rest) do |tmpname, n, opts|
|
135
|
+
lock = tmpname + '.lock'
|
136
|
+
mode = File::RDWR|File::CREAT|File::EXCL
|
137
|
+
perm = 0600
|
138
|
+
if opts
|
139
|
+
mode |= opts.delete(:mode) || 0
|
140
|
+
opts[:perm] = perm
|
141
|
+
perm = nil
|
142
|
+
else
|
143
|
+
opts = perm
|
144
|
+
end
|
145
|
+
self.class.mkdir(lock)
|
41
146
|
begin
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
failure += 1
|
51
|
-
retry if failure < MAX_TRY
|
52
|
-
raise "cannot generate tempfile `%s'" % tmpname
|
53
|
-
ensure
|
54
|
-
Thread.critical = false
|
147
|
+
@data[1] = @tmpfile = File.open(tmpname, mode, opts)
|
148
|
+
@data[0] = @tmpname = tmpname
|
149
|
+
ensure
|
150
|
+
self.class.rmdir(lock)
|
151
|
+
end
|
152
|
+
@mode = mode & ~(File::CREAT|File::EXCL)
|
153
|
+
perm or opts.freeze
|
154
|
+
@opts = opts
|
55
155
|
end
|
56
156
|
|
57
|
-
@data = [tmpname]
|
58
|
-
@clean_proc = Tempfile.callback(@data)
|
59
|
-
ObjectSpace.define_finalizer(self, @clean_proc)
|
60
|
-
|
61
|
-
@tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
|
62
|
-
@tmpname = tmpname
|
63
|
-
@@cleanlist << @tmpname
|
64
|
-
@data[1] = @tmpfile
|
65
|
-
@data[2] = @@cleanlist
|
66
|
-
|
67
157
|
super(@tmpfile)
|
68
|
-
|
69
|
-
# Now we have all the File/IO methods defined, you must not
|
70
|
-
# carelessly put bare puts(), etc. after this.
|
71
|
-
|
72
|
-
Dir.rmdir(lock)
|
73
158
|
end
|
74
159
|
|
75
|
-
def make_tmpname(basename, n)
|
76
|
-
case basename
|
77
|
-
when Array
|
78
|
-
prefix, suffix = *basename
|
79
|
-
else
|
80
|
-
prefix, suffix = basename, ''
|
81
|
-
end
|
82
|
-
|
83
|
-
t = Time.now.strftime("%Y%m%d")
|
84
|
-
path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
|
85
|
-
end
|
86
|
-
private :make_tmpname
|
87
|
-
|
88
160
|
# Opens or reopens the file with mode "r+".
|
89
161
|
def open
|
90
162
|
@tmpfile.close if @tmpfile
|
91
|
-
@tmpfile = File.open(@tmpname,
|
163
|
+
@tmpfile = File.open(@tmpname, @mode, @opts)
|
92
164
|
@data[1] = @tmpfile
|
93
165
|
__setobj__(@tmpfile)
|
94
166
|
end
|
@@ -100,8 +172,9 @@ class Tempfile < DelegateClass(File)
|
|
100
172
|
end
|
101
173
|
protected :_close
|
102
174
|
|
103
|
-
# Closes the file.
|
104
|
-
# after closing.
|
175
|
+
# Closes the file. If +unlink_now+ is true, then the file will be unlinked
|
176
|
+
# (deleted) after closing. Of course, you can choose to later call #unlink
|
177
|
+
# if you do not unlink it now.
|
105
178
|
#
|
106
179
|
# If you don't explicitly unlink the temporary file, the removal
|
107
180
|
# will be delayed until the object is finalized.
|
@@ -113,25 +186,57 @@ class Tempfile < DelegateClass(File)
|
|
113
186
|
end
|
114
187
|
end
|
115
188
|
|
116
|
-
# Closes and unlinks the file.
|
189
|
+
# Closes and unlinks (deletes) the file. Has the same effect as called
|
190
|
+
# <tt>close(true)</tt>.
|
117
191
|
def close!
|
118
192
|
_close
|
119
|
-
|
193
|
+
unlink
|
120
194
|
ObjectSpace.undefine_finalizer(self)
|
121
|
-
@data = @tmpname = nil
|
122
195
|
end
|
123
196
|
|
124
|
-
# Unlinks the file
|
125
|
-
#
|
126
|
-
#
|
127
|
-
#
|
197
|
+
# Unlinks (deletes) the file from the filesystem. One should always unlink
|
198
|
+
# the file after using it, as is explained in the "Explicit close" good
|
199
|
+
# practice section in the Tempfile overview:
|
200
|
+
#
|
201
|
+
# file = Tempfile.new('foo')
|
202
|
+
# begin
|
203
|
+
# ...do something with file...
|
204
|
+
# ensure
|
205
|
+
# file.close
|
206
|
+
# file.unlink # deletes the temp file
|
207
|
+
# end
|
208
|
+
#
|
209
|
+
# === Unlink-before-close
|
210
|
+
#
|
211
|
+
# On POSIX systems it's possible to unlink a file before closing it. This
|
212
|
+
# practice is explained in detail in the Tempfile overview (section
|
213
|
+
# "Unlink after creation"); please refer there for more information.
|
214
|
+
#
|
215
|
+
# However, unlink-before-close may not be supported on non-POSIX operating
|
216
|
+
# systems. Microsoft Windows is the most notable case: unlinking a non-closed
|
217
|
+
# file will result in an error, which this method will silently ignore. If
|
218
|
+
# you want to practice unlink-before-close whenever possible, then you should
|
219
|
+
# write code like this:
|
220
|
+
#
|
221
|
+
# file = Tempfile.new('foo')
|
222
|
+
# file.unlink # On Windows this silently fails.
|
223
|
+
# begin
|
224
|
+
# ... do something with file ...
|
225
|
+
# ensure
|
226
|
+
# file.close! # Closes the file handle. If the file wasn't unlinked
|
227
|
+
# # because #unlink failed, then this method will attempt
|
228
|
+
# # to do so again.
|
229
|
+
# end
|
128
230
|
def unlink
|
129
231
|
# keep this order for thread safeness
|
232
|
+
return unless @tmpname
|
130
233
|
begin
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
234
|
+
if File.exist?(@tmpname)
|
235
|
+
File.unlink(@tmpname)
|
236
|
+
end
|
237
|
+
# remove tmpname from remover
|
238
|
+
@data[0] = @data[2] = nil
|
239
|
+
@tmpname = nil
|
135
240
|
rescue Errno::EACCES
|
136
241
|
# may not be able to unlink on Windows; just ignore
|
137
242
|
end
|
@@ -139,6 +244,7 @@ class Tempfile < DelegateClass(File)
|
|
139
244
|
alias delete unlink
|
140
245
|
|
141
246
|
# Returns the full path name of the temporary file.
|
247
|
+
# This will be nil if #unlink has been called.
|
142
248
|
def path
|
143
249
|
@tmpname
|
144
250
|
end
|
@@ -149,52 +255,83 @@ class Tempfile < DelegateClass(File)
|
|
149
255
|
if @tmpfile
|
150
256
|
@tmpfile.flush
|
151
257
|
@tmpfile.stat.size
|
258
|
+
elsif @tmpname
|
259
|
+
File.size(@tmpname)
|
152
260
|
else
|
153
261
|
0
|
154
262
|
end
|
155
263
|
end
|
156
264
|
alias length size
|
157
265
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
266
|
+
# :stopdoc:
|
267
|
+
class Remover
|
268
|
+
def initialize(data)
|
269
|
+
@pid = $$
|
270
|
+
@data = data
|
271
|
+
end
|
164
272
|
|
165
|
-
|
273
|
+
def call(*args)
|
274
|
+
if @pid == $$
|
275
|
+
path, tmpfile = *@data
|
166
276
|
|
167
|
-
|
277
|
+
STDERR.print "removing ", path, "..." if $DEBUG
|
168
278
|
|
169
|
-
|
170
|
-
File.unlink(path) if File.exist?(path)
|
171
|
-
cleanlist.delete(path) if cleanlist
|
279
|
+
tmpfile.close if tmpfile
|
172
280
|
|
173
|
-
|
281
|
+
# keep this order for thread safeness
|
282
|
+
if path
|
283
|
+
File.unlink(path) if File.exist?(path)
|
174
284
|
end
|
175
|
-
|
285
|
+
|
286
|
+
STDERR.print "done\n" if $DEBUG
|
287
|
+
end
|
176
288
|
end
|
289
|
+
end
|
290
|
+
# :startdoc:
|
177
291
|
|
178
|
-
|
292
|
+
class << self
|
293
|
+
# Creates a new Tempfile.
|
294
|
+
#
|
295
|
+
# If no block is given, this is a synonym for Tempfile.new.
|
179
296
|
#
|
180
|
-
# If a block is given,
|
181
|
-
# and the
|
182
|
-
#
|
297
|
+
# If a block is given, then a Tempfile object will be constructed,
|
298
|
+
# and the block is run with said object as argument. The Tempfile
|
299
|
+
# oject will be automatically closed after the block terminates.
|
300
|
+
# The call returns the value of the block.
|
301
|
+
#
|
302
|
+
# In any case, all arguments (+*args+) will be passed to Tempfile.new.
|
303
|
+
#
|
304
|
+
# Tempfile.open('foo', '/home/temp') do |f|
|
305
|
+
# ... do something with f ...
|
306
|
+
# end
|
307
|
+
#
|
308
|
+
# # Equivalent:
|
309
|
+
# f = Tempfile.open('foo', '/home/temp')
|
310
|
+
# begin
|
311
|
+
# ... do something with f ...
|
312
|
+
# ensure
|
313
|
+
# f.close
|
314
|
+
# end
|
183
315
|
def open(*args)
|
184
316
|
tempfile = new(*args)
|
185
317
|
|
186
318
|
if block_given?
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
nil
|
319
|
+
begin
|
320
|
+
yield(tempfile)
|
321
|
+
ensure
|
322
|
+
tempfile.close
|
323
|
+
end
|
194
324
|
else
|
195
|
-
|
325
|
+
tempfile
|
196
326
|
end
|
197
327
|
end
|
328
|
+
|
329
|
+
def mkdir(*args)
|
330
|
+
Dir.mkdir(*args)
|
331
|
+
end
|
332
|
+
def rmdir(*args)
|
333
|
+
Dir.rmdir(*args)
|
334
|
+
end
|
198
335
|
end
|
199
336
|
end
|
200
337
|
|
data/rubysl-tempfile.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
17
|
spec.require_paths = ["lib"]
|
18
18
|
|
19
|
-
spec.required_ruby_version = "~>
|
19
|
+
spec.required_ruby_version = "~> 2.0"
|
20
20
|
|
21
21
|
spec.add_development_dependency "bundler", "~> 1.3"
|
22
22
|
spec.add_development_dependency "rake", "~> 10.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubysl-tempfile
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Shirai
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-08-
|
11
|
+
date: 2013-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -95,7 +95,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
95
95
|
requirements:
|
96
96
|
- - ~>
|
97
97
|
- !ruby/object:Gem::Version
|
98
|
-
version:
|
98
|
+
version: '2.0'
|
99
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - '>='
|