rubysl-tempfile 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0ef0738dac62e35b9f6360922f94efc28d20fd39
4
- data.tar.gz: fce5f3e54ef1085c072f867ae45bf51884d13061
3
+ metadata.gz: 0f82c47d38159bb53a71a207db4db87b6d0ace1d
4
+ data.tar.gz: 026e066a5774dbfb057f861cc78a9ae7717be308
5
5
  SHA512:
6
- metadata.gz: e1ad962fc20d677dcb0b0179803065ef8f7ec8042808549b80e6b8dc12fea148327f19ee919974a3addcbd305474ee9917d58164715190fce6ec39ad033c51c1
7
- data.tar.gz: 7b28bfb3515238570324440c5752d69d6b9def6feaa70847889e678b58bc88f0934853fa78a29f9ea665d70aa328f648a4a2dd5c458ca897b20a7ee40756db48
6
+ metadata.gz: daa1c9f4243df03b5f6266f0c451b6cba2fd1c499eab3e814575953a6c0124b3dbbe4d792c4c3e0ef11b852b41e88d24e8835ee7cb4c284027f573ee4f419b02
7
+ data.tar.gz: c6485ac719d85e2fd2ace909a61a52cccc0883752de27ab15e1ba53022745ced1d5d3388465a99366669ff4d255435577493226a7b49e7d050fb20a55e33b52e
@@ -3,5 +3,5 @@ env:
3
3
  - RUBYLIB=lib
4
4
  script: bundle exec mspec
5
5
  rvm:
6
- - 1.8.7
7
- - rbx-nightly-18mode
6
+ - 1.9.3
7
+ - rbx-nightly-19mode
@@ -1,94 +1,166 @@
1
1
  #
2
2
  # tempfile - manipulates temporary files
3
3
  #
4
- # $Id: tempfile.rb 16127 2008-04-21 09:43:44Z knu $
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. This library is written to be
11
- # thread safe.
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
- @@cleanlist = []
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
- begin
39
- Thread.critical = true
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
- tmpname = File.join(tmpdir, make_tmpname(basename, n))
43
- lock = tmpname + '.lock'
44
- n += 1
45
- end while @@cleanlist.include?(tmpname) or
46
- File.exist?(lock) or File.exist?(tmpname)
47
-
48
- Dir.mkdir(lock)
49
- rescue
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, 'r+')
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. If the optional flag is true, unlinks 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
- @clean_proc.call
193
+ unlink
120
194
  ObjectSpace.undefine_finalizer(self)
121
- @data = @tmpname = nil
122
195
  end
123
196
 
124
- # Unlinks the file. On UNIX-like systems, it is often a good idea
125
- # to unlink a temporary file immediately after creating and opening
126
- # it, because it leaves other programs zero chance to access the
127
- # file.
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
- File.unlink(@tmpname) if File.exist?(@tmpname)
132
- @@cleanlist.delete(@tmpname)
133
- @data = @tmpname = nil
134
- ObjectSpace.undefine_finalizer(self)
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
- class << self
159
- def callback(data) # :nodoc:
160
- pid = $$
161
- lambda {
162
- if pid == $$
163
- path, tmpfile, cleanlist = *data
266
+ # :stopdoc:
267
+ class Remover
268
+ def initialize(data)
269
+ @pid = $$
270
+ @data = data
271
+ end
164
272
 
165
- print "removing ", path, "..." if $DEBUG
273
+ def call(*args)
274
+ if @pid == $$
275
+ path, tmpfile = *@data
166
276
 
167
- tmpfile.close if tmpfile
277
+ STDERR.print "removing ", path, "..." if $DEBUG
168
278
 
169
- # keep this order for thread safeness
170
- File.unlink(path) if File.exist?(path)
171
- cleanlist.delete(path) if cleanlist
279
+ tmpfile.close if tmpfile
172
280
 
173
- print "done\n" if $DEBUG
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
- # If no block is given, this is a synonym for new().
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, it will be passed tempfile as an argument,
181
- # and the tempfile will automatically be closed when the block
182
- # terminates. In this case, open() returns nil.
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
- begin
188
- yield(tempfile)
189
- ensure
190
- tempfile.close
191
- end
192
-
193
- nil
319
+ begin
320
+ yield(tempfile)
321
+ ensure
322
+ tempfile.close
323
+ end
194
324
  else
195
- tempfile
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
 
@@ -1,5 +1,5 @@
1
1
  module RubySL
2
2
  module Tempfile
3
- VERSION = "1.0.0"
3
+ VERSION = "2.0.0"
4
4
  end
5
5
  end
@@ -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 = "~> 1.8.7"
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: 1.0.0
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-27 00:00:00.000000000 Z
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: 1.8.7
98
+ version: '2.0'
99
99
  required_rubygems_version: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - '>='