peritor-better 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1 @@
1
+ Distributed under the same license as Ruby itself.
@@ -0,0 +1,40 @@
1
+ = Overview
2
+
3
+ Better is a collection of better replacements Ruby standard libraries. The versions shipped with Ruby have problems, which this library intends to fix. It is my wish that this code will one day find its way back to upstream Ruby.
4
+
5
+ * Github: http://github.com/FooBarWidget/better/tree/master
6
+ * API documentation: http://better.rubyforge.org/
7
+
8
+ == Usage
9
+
10
+ Install with:
11
+
12
+ gem install better
13
+
14
+ All of the libraries in Better are drop-in replacement and have the exact same API as the original, and work on both Ruby 1.8 and 1.9. For example, instead of
15
+
16
+ require 'tempfile'
17
+ Tempfile.new(...)
18
+
19
+ you just prefix the library filename with 'better/' and the class name with 'Better::':
20
+
21
+ require 'better/tempfile'
22
+ Better::Tempfile.new(...)
23
+
24
+ Or you can even override the standard version by requiring the library with the 'better/override/' prefix:
25
+
26
+ require 'better/override/tempfile'
27
+ Tempfile # => now refers to Better::Tempfile instead of ::Tempfile
28
+
29
+ This last feature should of course be used with care.
30
+
31
+ Please refer to the individual classes for more documentation.
32
+
33
+ == Contributing
34
+
35
+ The Github repository is located at http://github.com/FooBarWidget/better/tree/master.
36
+ Is there a Ruby standard library that you think can be improved? Just fork the repository and start hacking! It doesn't matter whether you want to fix a small bug, want to write unit tests or just want to improve documentation - anything is fine.
37
+
38
+ You can contact me at:
39
+
40
+ * Hongli Lai (hongli@phusion.nl)
@@ -0,0 +1,29 @@
1
+ begin
2
+ require 'rubygems'
3
+ require 'hanna/rdoctask'
4
+ rescue LoadError
5
+ STDERR.puts "*** Warning: you do not have the Hanna rdoc template installed. The rdoc will look better if you do. You should type:"
6
+ STDERR.puts " sudo gem sources -a http://gems.github.com"
7
+ STDERR.puts " sudo gem install mislav-hanna"
8
+ STDERR.puts
9
+ require 'rake/rdoctask'
10
+ end
11
+ require 'rake/testtask'
12
+
13
+ Rake::RDocTask.new do |rd|
14
+ rd.main = "README.rdoc"
15
+ rd.title = "Better: API documentation"
16
+ rd.rdoc_files.include("README.rdoc", "LICENSE", "lib/**/*.rb")
17
+ rd.rdoc_dir = "doc"
18
+ end
19
+
20
+ Rake::TestTask.new do |t|
21
+ t.libs << "test"
22
+ t.test_files = FileList['test/*_test.rb']
23
+ t.verbose = true
24
+ end
25
+
26
+ desc "Upload documentation to Rubyforge"
27
+ task :upload => :rdoc do
28
+ sh "scp -Cr doc/* rubyforge.org:/var/www/gforge-projects/better/"
29
+ end
@@ -0,0 +1,23 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "peritor-better"
3
+ s.version = "1.0.1"
4
+ s.summary = "Collection of better replacements for Ruby standard libraries"
5
+ s.email = "hongli@phusion.nl"
6
+ s.homepage = "http://better.rubyforge.org/"
7
+ s.description = "Collection of better replacements for Ruby standard libraries."
8
+ s.has_rdoc = true
9
+ s.rubyforge_project = "better"
10
+ s.authors = ["Hongli Lai"]
11
+
12
+ s.files = [
13
+ "README.rdoc", "LICENSE", "better.gemspec", "Rakefile",
14
+ "lib/better/tempfile.rb",
15
+ "lib/better/override/tempfile.rb",
16
+ "test/test_helper.rb",
17
+ "test/example_helper.rb",
18
+ "test/tempfile_test.rb",
19
+ "test/tempfile_explicit_close_and_unlink_example.rb",
20
+ "test/tempfile_explicit_unlink_example.rb",
21
+ "test/tempfile_unlink_on_exit_example.rb"
22
+ ]
23
+ end
@@ -0,0 +1,3 @@
1
+ require 'better/tempfile'
2
+ Object.send(:remove_const, :Tempfile) if defined?(Tempfile)
3
+ ::Tempfile = Better::Tempfile
@@ -0,0 +1,395 @@
1
+ #
2
+ # tempfile - manipulates temporary files
3
+ #
4
+ # $Id$
5
+ #
6
+
7
+ require 'delegate'
8
+ require 'tmpdir'
9
+ require 'thread'
10
+
11
+ module Better
12
+
13
+ # A utility class for managing temporary files. When you create a Tempfile
14
+ # object, it will create a temporary file with a unique filename. A Tempfile
15
+ # objects behaves just like a File object, and you can perform all the usual
16
+ # file operations on it: reading data, writing data, changing its permissions,
17
+ # etc. So although this class does not explicitly document all instance methods
18
+ # supported by File, you can in fact call any File instance method on a
19
+ # Tempfile object.
20
+ #
21
+ # == Comparison to Ruby's bundled version
22
+ #
23
+ # * Much better documentation.
24
+ # * Is unit tested.
25
+ # * Ruby 1.8's version can generate "weird" path names that can confuse certain
26
+ # command line tools such as Curl. Better::Tempfile is based on Ruby 1.9's
27
+ # version and generates saner filenames.
28
+ # * Ruby 1.8's version has a bug which makes unlink-before-close (as described
29
+ # below) unusable: it raises an an exception when #close is called if the
30
+ # tempfile was unlinked before.
31
+ # * Ruby 1.9.1's version closes the file when #unlink is called. This makes
32
+ # unlink-before-close unusable.
33
+ # * Ruby's bundled version deletes the temporary file in its finalizer, even
34
+ # when #unlink was called before. As a result it may potentially delete other
35
+ # Ruby processes' temp files when it's not supposed to.
36
+ #
37
+ # Better::Tempfile is based on Ruby 1.9.2's version (SVN 24594).
38
+ #
39
+ # == Synopsis
40
+ #
41
+ # require 'better/tempfile'
42
+ #
43
+ # file = Better::Tempfile.new('foo')
44
+ # file.path # => A unique filename in the OS's temp directory,
45
+ # # e.g.: "/tmp/foo.24722.0"
46
+ # # This filename contains 'foo' in its basename.
47
+ # file.write("hello world")
48
+ # file.rewind
49
+ # file.read # => "hello world"
50
+ # file.close
51
+ # file.unlink # deletes the temp file
52
+ #
53
+ # == Good practices
54
+ #
55
+ # === Explicit close
56
+ #
57
+ # When a Tempfile object is garbage collected, or when the Ruby interpreter
58
+ # exits, its associated temporary file is automatically deleted. This means
59
+ # that's it's unnecessary to explicitly delete a Tempfile after use, though
60
+ # it's good practice to do so: not explicitly deleting unused Tempfiles can
61
+ # potentially leave behind large amounts of tempfiles on the filesystem
62
+ # until they're garbage collected. The existance of these temp files can make
63
+ # it harder to determine a new Tempfile filename.
64
+ #
65
+ # Therefore, one should always call #unlink or close in an ensure block, like
66
+ # this:
67
+ #
68
+ # file = Better::Tempfile.new('foo)
69
+ # begin
70
+ # ...do something with file...
71
+ # ensure
72
+ # file.close
73
+ # file.unlink # deletes the temp file
74
+ # end
75
+ #
76
+ # === Unlink after creation
77
+ #
78
+ # On POSIX systems, it's possible to unlink a file right after creating it,
79
+ # and before closing it. This removes the filesystem entry without closing
80
+ # the file handle, so it ensures that only the processes that already had
81
+ # the file handle open can access the file's contents. It's strongly
82
+ # recommended that you do this if you do not want any other processes to
83
+ # be able to read from or write to the Tempfile, and you do not need to
84
+ # know the Tempfile's filename either.
85
+ #
86
+ # For example, a practical use case for unlink-after-creation would be this:
87
+ # you need a large byte buffer that's too large to comfortably fit in RAM,
88
+ # e.g. when you're writing a web server and you want to buffer the client's
89
+ # file upload data.
90
+ #
91
+ # Please refer to #unlink for more information and a code example.
92
+ #
93
+ # == Minor notes
94
+ #
95
+ # Tempfile's filename picking method is both thread-safe and inter-process-safe:
96
+ # it guarantees that no other threads or processes will pick the same filename.
97
+ #
98
+ # Tempfile itself however may not be entirely thread-safe. If you access the
99
+ # same Tempfile object from multiple threads then you should protect it with a
100
+ # mutex.
101
+ class Tempfile < DelegateClass(File)
102
+ MAX_TRIES = 10 # :nodoc:
103
+ @@live_tempfiles = []
104
+ @@lock = Mutex.new
105
+
106
+ class CreationError < StandardError # :nodoc:
107
+ end
108
+
109
+ class << self
110
+ # Creates a new Tempfile.
111
+ #
112
+ # If no block is given, this is a synonym for Tempfile.new.
113
+ #
114
+ # If a block is given, then a Tempfile object will be constructed,
115
+ # and the block is run with said object as argument. The Tempfile
116
+ # oject will be automatically closed after the block terminates.
117
+ # The call returns the value of the block.
118
+ #
119
+ # In any case, all arguments (+*args+) will be passed to Tempfile.new.
120
+ #
121
+ # Better::Tempfile.open('foo', '/home/temp') do |f|
122
+ # ... do something with f ...
123
+ # end
124
+ #
125
+ # # Equivalent:
126
+ # f = Better::Tempfile.open('foo', '/home/temp')
127
+ # begin
128
+ # ... do something with f ...
129
+ # ensure
130
+ # f.close
131
+ # end
132
+ def open(*args)
133
+ tempfile = new(*args)
134
+
135
+ if block_given?
136
+ begin
137
+ yield(tempfile)
138
+ ensure
139
+ tempfile.close
140
+ end
141
+ else
142
+ tempfile
143
+ end
144
+ end
145
+
146
+ def create_finalizer_callback(info) # :nodoc:
147
+ original_pid = $$
148
+ Proc.new do
149
+ # If we forked, then don't cleanup the temp files created by
150
+ # the parent process.
151
+ if original_pid == $$
152
+ path, tmpfile, live_tempfiles = *info
153
+ begin
154
+ tmpfile.close if tmpfile
155
+ rescue IOError
156
+ raise if not $!.message.match(/^closed stream$/)
157
+ end
158
+ File.unlink(path) if path && File.exist?(path)
159
+ live_tempfiles.delete(path) if live_tempfiles
160
+ end
161
+ end
162
+ end
163
+
164
+ def make_directory(dir) # :nodoc:
165
+ Dir.mkdir(dir)
166
+ end
167
+ end
168
+
169
+ # call-seq:
170
+ # new(basename, [tmpdir = Dir.tmpdir], [options])
171
+ #
172
+ # Creates a temporary file with permissions 0600 (= only readable and
173
+ # writable by the owner) and opens it with mode "w+".
174
+ #
175
+ # The +basename+ parameter is used to determine the name of the
176
+ # temporary file. You can either pass a String or an Array with
177
+ # 2 String elements. In the former form, the temporary file's base
178
+ # name will begin with the given string. In the latter form,
179
+ # the temporary file's base name will begin with the array's first
180
+ # element, and end with the second element. For example:
181
+ #
182
+ # file = Better::Tempfile.new('hello')
183
+ # file.path # => something like: "/tmp/foo2843-8392-92849382--0"
184
+ #
185
+ # # Use the Array form to enforce an extension in the filename:
186
+ # file = Better::Tempfile.new(['hello', '.jpg'])
187
+ # file.path # => something like: "/tmp/foo2843-8392-92849382--0.jpg"
188
+ #
189
+ # The temporary file will be placed in the directory as specified
190
+ # by the +tmpdir+ parameter. By default, this is +Dir.tmpdir+ (see
191
+ # 'tmpdir.rb' in the Ruby standard library.)
192
+ # When $SAFE > 0 and the given +tmpdir+ is tainted, it uses
193
+ # '/tmp' as the temporary directory. Please note that ENV values
194
+ # are tainted by default, and +Dir.tmpdir+'s return value might
195
+ # come from environment variables (e.g. <tt>$TMPDIR</tt>).
196
+ #
197
+ # file = Better::Tempfile.new('hello', '/home/aisaka')
198
+ # file.path # => something like: "/home/aisaka/foo2843-8392-92849382--0"
199
+ #
200
+ # You can also pass an options hash. Under the hood, Better::Tempfile creates
201
+ # the temporary file using +File.open+. These options will be passed to
202
+ # +File.open+. This is mostly useful on Ruby 1.9 for specifying encoding
203
+ # options, e.g.:
204
+ #
205
+ # Better::Tempfile.new('hello', '/home/aisaka', :encoding => 'ascii-8bit')
206
+ #
207
+ # # You can also omit the 'tmpdir' parameter:
208
+ # Better::Tempfile.new('hello', :encoding => 'ascii-8bit')
209
+ #
210
+ # === Exceptions
211
+ #
212
+ # Under rare circumstances, this constructor can raise an instance of
213
+ # Better::Tempfile::CreationError. This could happen if a large number
214
+ # of threads or processes are simultaneously trying to create temp files
215
+ # and stepping on each others' toes. If Better::Tempfile.new cannot find
216
+ # a unique filename within a limited number of tries, then it will raise
217
+ # this exception.
218
+ def initialize(basename, *rest)
219
+ # I wish keyword argument settled soon.
220
+ if rest.last.respond_to?(:to_hash)
221
+ opts = rest.last.to_hash
222
+ rest.pop
223
+ else
224
+ opts = nil
225
+ end
226
+ tmpdir = rest[0] || Dir::tmpdir
227
+ if $SAFE > 0 && tmpdir.tainted?
228
+ tmpdir = '/tmp'
229
+ end
230
+
231
+ lock = tmpname = nil
232
+ n = failure = 0
233
+ @@lock.synchronize do
234
+ begin
235
+ begin
236
+ tmpname = File.join(tmpdir, make_tmpname(basename, n))
237
+ lock = tmpname + '.lock'
238
+ n += 1
239
+ end while @@live_tempfiles.include?(tmpname) ||
240
+ File.exist?(lock) ||
241
+ File.exist?(tmpname)
242
+ self.class.make_directory(lock)
243
+ rescue SystemCallError
244
+ failure += 1
245
+ retry if failure < MAX_TRIES
246
+ raise CreationError, ("cannot generate tempfile `%s'" % tmpname)
247
+ end
248
+ end
249
+
250
+ @finalizer_info = [tmpname]
251
+ @finalizer_callback = self.class.create_finalizer_callback(@finalizer_info)
252
+ ObjectSpace.define_finalizer(self, @finalizer_callback)
253
+
254
+ if opts.nil?
255
+ opts = []
256
+ else
257
+ opts = [opts]
258
+ end
259
+ @tmpfile = File.open(tmpname, File::RDWR | File::CREAT | File::EXCL, 0600, *opts)
260
+ @tmpname = tmpname
261
+ @@live_tempfiles << tmpname
262
+ @finalizer_info[1] = @tmpfile
263
+ @finalizer_info[2] = @@live_tempfiles
264
+
265
+ super(@tmpfile)
266
+
267
+ # Now we have all the File/IO methods defined, you must not
268
+ # carelessly put bare puts(), etc. after this.
269
+
270
+ Dir.rmdir(lock)
271
+ end
272
+
273
+ # Opens or reopens the file with mode "r+".
274
+ def open
275
+ @tmpfile.close if @tmpfile
276
+ @tmpfile = File.open(@tmpname, 'r+')
277
+ @finalizer_info[1] = @tmpfile
278
+ __setobj__(@tmpfile)
279
+ end
280
+
281
+ # Closes the file. If +unlink_now+ is true, then the file will be unlinked
282
+ # (deleted) after closing. Of course, you can choose to later call #unlink
283
+ # if you do not unlink it now.
284
+ def close(unlink_now = false)
285
+ if unlink_now
286
+ close!
287
+ else
288
+ _close
289
+ end
290
+ end
291
+
292
+ # Closes and unlinks (deletes) the file. Has the same effect as called
293
+ # <tt>close(true)</tt>.
294
+ def close!
295
+ _close
296
+ unlink if !unlinked?
297
+ end
298
+
299
+ # Unlinks (deletes) the file from the filesystem. One should always unlink
300
+ # the file after using it, as is explained in the "Explicit close" good
301
+ # practice section in the Tempfile overview:
302
+ #
303
+ # file = Better::Tempfile.new('foo)
304
+ # begin
305
+ # ...do something with file...
306
+ # ensure
307
+ # file.close
308
+ # file.unlink # deletes the temp file
309
+ # end
310
+ #
311
+ # === Unlink-before-close
312
+ #
313
+ # On POSIX systems it's possible to unlink a file before closing it. This
314
+ # practice is explained in detail in the Tempfile overview (section
315
+ # "Unlink after creation"); please refer there for more information.
316
+ #
317
+ # However, unlink-before-close may not be supported on non-POSIX operating
318
+ # systems. Microsoft Windows is the most notable case: unlinking a non-closed
319
+ # file will result in an error, which this method will silently ignore. If
320
+ # you want to practice unlink-before-close whenever possible, then you should
321
+ # write code like this:
322
+ #
323
+ # file = Better::Tempfile.new('foo')
324
+ # file.unlink # On Windows this silently fails.
325
+ # begin
326
+ # ... do something with file ...
327
+ # ensure
328
+ # file.close! # Closes the file handle. If the file wasn't unlinked
329
+ # # because #unlink failed, then this method will attempt
330
+ # # to do so again.
331
+ # end
332
+ def unlink
333
+ begin
334
+ if File.exist?(@tmpname) # keep this order for thread safeness
335
+ unlink_file(@tmpname)
336
+ end
337
+ @@live_tempfiles.delete(@tmpname)
338
+ @finalizer_info = @tmpname = nil
339
+ ObjectSpace.undefine_finalizer(self)
340
+ rescue Errno::EACCES
341
+ # may not be able to unlink on Windows; just ignore
342
+ end
343
+ end
344
+ alias delete unlink
345
+
346
+ # Returns whether #unlink has been called on this Tempfile, and whether it
347
+ # succeeded.
348
+ def unlinked?
349
+ @tmpname.nil?
350
+ end
351
+
352
+ # Returns the full path name of the temporary file. This will be nil if
353
+ # #unlink has been called.
354
+ def path
355
+ @tmpname
356
+ end
357
+
358
+ # Returns the size of the temporary file. As a side effect, the IO
359
+ # buffer is flushed before determining the size.
360
+ def size
361
+ if @tmpfile
362
+ @tmpfile.flush
363
+ @tmpfile.stat.size
364
+ else
365
+ 0
366
+ end
367
+ end
368
+ alias length size
369
+
370
+ protected
371
+ def _close # :nodoc:
372
+ @tmpfile.close if @tmpfile
373
+ @tmpfile = nil
374
+ @finalizer_info[1] = nil if @finalizer_info
375
+ end
376
+
377
+ private
378
+ def unlink_file(filename)
379
+ File.unlink(filename)
380
+ end
381
+
382
+ def make_tmpname(basename, n)
383
+ case basename
384
+ when Array
385
+ prefix, suffix = *basename
386
+ else
387
+ prefix, suffix = basename, ''
388
+ end
389
+
390
+ t = Time.now.strftime("%Y%m%d")
391
+ path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}-#{n}#{suffix}"
392
+ end
393
+ end
394
+
395
+ end # module Better
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
2
+ output_file = ARGV.shift
3
+ STDOUT.reopen(output_file, "w")
@@ -0,0 +1,8 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "example_helper"))
2
+ require 'better/tempfile'
3
+
4
+ file = Better::Tempfile.new('foo')
5
+ path = file.path
6
+ puts path
7
+ file.close!
8
+ File.open(path, "w").close
@@ -0,0 +1,12 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "example_helper"))
2
+ require 'better/tempfile'
3
+
4
+ file = Better::Tempfile.new('foo')
5
+ path = file.path
6
+ file.unlink
7
+ if file.unlinked?
8
+ puts path
9
+ File.open(path, "w").close
10
+ else
11
+ file.close!
12
+ end
@@ -0,0 +1,283 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+ require 'better/tempfile'
3
+ require 'thread'
4
+
5
+ class TempfileTest < Test::Unit::TestCase
6
+ include Better
7
+
8
+ def teardown
9
+ if @tempfile
10
+ @tempfile.close if !@tempfile.closed?
11
+ @tempfile.unlink if !@tempfile.unlinked?
12
+ end
13
+ end
14
+
15
+ def test_basic
16
+ @tempfile = Tempfile.new("foo")
17
+ path = @tempfile.path
18
+ @tempfile.write("hello world")
19
+ @tempfile.close
20
+ assert_equal "hello world", File.read(path)
21
+ end
22
+
23
+ def test_saves_in_dir_tmpdir_by_default
24
+ @tempfile = Tempfile.new("foo")
25
+ assert_equal Dir.tmpdir, File.dirname(@tempfile.path)
26
+ end
27
+
28
+ def test_saves_in_given_directory
29
+ subdir = File.join(Dir.tmpdir, "tempfile-test-#{rand}")
30
+ Dir.mkdir(subdir)
31
+ begin
32
+ tempfile = Tempfile.new("foo", subdir)
33
+ tempfile.close
34
+ begin
35
+ assert_equal subdir, File.dirname(tempfile.path)
36
+ ensure
37
+ tempfile.unlink
38
+ end
39
+ ensure
40
+ Dir.rmdir(subdir)
41
+ end
42
+ end
43
+
44
+ def test_basename
45
+ @tempfile = Tempfile.new("foo")
46
+ assert_match /^foo/, File.basename(@tempfile.path)
47
+ end
48
+
49
+ def test_basename_with_suffix
50
+ @tempfile = Tempfile.new(["foo", ".txt"])
51
+ assert_match /^foo/, File.basename(@tempfile.path)
52
+ assert_match /\.txt$/, File.basename(@tempfile.path)
53
+ end
54
+
55
+ def test_raises_creation_error_if_max_tries_surpassed
56
+ Tempfile.expects(:make_directory).times(Tempfile::MAX_TRIES).raises(Errno::EEXIST)
57
+ assert_raises(Tempfile::CreationError) do
58
+ @tempfile = Tempfile.new('foo')
59
+ end
60
+ end
61
+
62
+ def test_unlink_and_unlink_p
63
+ @tempfile = Tempfile.new("foo")
64
+ path = @tempfile.path
65
+ assert !@tempfile.unlinked?
66
+
67
+ @tempfile.close
68
+ assert !@tempfile.unlinked?
69
+ assert File.exist?(path)
70
+
71
+ @tempfile.unlink
72
+ assert @tempfile.unlinked?
73
+ assert !File.exist?(path)
74
+
75
+ @tempfile = nil
76
+ end
77
+
78
+ def test_unlink_makes_path_nil
79
+ @tempfile = Tempfile.new("foo")
80
+ @tempfile.close
81
+ @tempfile.unlink
82
+ assert_nil @tempfile.path
83
+ end
84
+
85
+ def test_unlink_silently_fails_on_windows
86
+ tempfile = Tempfile.new("foo")
87
+ path = tempfile.path
88
+ begin
89
+ tempfile.expects(:unlink_file).with(path).raises(Errno::EACCES)
90
+ assert_nothing_raised do
91
+ tempfile.unlink
92
+ end
93
+ assert !tempfile.unlinked?
94
+ ensure
95
+ tempfile.close
96
+ File.unlink(path)
97
+ end
98
+ end
99
+
100
+ def test_unlink_before_close_works_on_posix_systems
101
+ tempfile = Tempfile.new("foo")
102
+ begin
103
+ path = tempfile.path
104
+ tempfile.unlink
105
+ if tempfile.unlinked?
106
+ assert !File.exist?(path)
107
+ tempfile.write("hello ")
108
+ tempfile.write("world\n")
109
+ tempfile.rewind
110
+ assert_equal "hello world\n", tempfile.read
111
+ end
112
+ ensure
113
+ tempfile.close
114
+ tempfile.unlink if !tempfile.unlinked?
115
+ end
116
+ end
117
+
118
+ def test_close_and_close_p
119
+ @tempfile = Tempfile.new("foo")
120
+ assert !@tempfile.closed?
121
+ @tempfile.close
122
+ assert @tempfile.closed?
123
+ end
124
+
125
+ def test_close_with_unlink_now_true_works
126
+ @tempfile = Tempfile.new("foo")
127
+ path = @tempfile.path
128
+ @tempfile.close(true)
129
+ assert @tempfile.closed?
130
+ assert @tempfile.unlinked?
131
+ assert_nil @tempfile.path
132
+ assert !File.exist?(path)
133
+ end
134
+
135
+ def test_close_with_unlink_now_true_does_not_unlink_if_already_unlinked
136
+ @tempfile = Tempfile.new("foo")
137
+ path = @tempfile.path
138
+ @tempfile.unlink
139
+ File.open(path, "w").close
140
+ begin
141
+ @tempfile.close(true)
142
+ assert File.exist?(path)
143
+ ensure
144
+ File.unlink(path) rescue nil
145
+ end
146
+ end
147
+
148
+ def test_close_bang_works
149
+ @tempfile = Tempfile.new("foo")
150
+ path = @tempfile.path
151
+ @tempfile.close!
152
+ assert @tempfile.closed?
153
+ assert @tempfile.unlinked?
154
+ assert_nil @tempfile.path
155
+ assert !File.exist?(path)
156
+ end
157
+
158
+ def test_close_bang_does_not_unlink_if_already_unlinked
159
+ @tempfile = Tempfile.new("foo")
160
+ path = @tempfile.path
161
+ @tempfile.unlink
162
+ File.open(path, "w").close
163
+ begin
164
+ @tempfile.close!
165
+ assert File.exist?(path)
166
+ ensure
167
+ File.unlink(path) rescue nil
168
+ end
169
+ end
170
+
171
+ def test_finalizer_does_not_unlink_if_already_unlinked
172
+ filename = run_script("tempfile_explicit_close_and_unlink_example.rb").strip
173
+ assert File.exist?(filename)
174
+ File.unlink(filename)
175
+
176
+ filename = run_script("tempfile_explicit_unlink_example.rb").strip
177
+ if !filename.empty?
178
+ # POSIX unlink semantics supported, continue with test
179
+ assert File.exist?(filename)
180
+ File.unlink(filename)
181
+ end
182
+ end
183
+
184
+ def test_close_does_not_make_path_nil
185
+ @tempfile = Tempfile.new("foo")
186
+ @tempfile.close
187
+ assert_not_nil @tempfile.path
188
+ end
189
+
190
+ def test_close_flushes_buffer
191
+ @tempfile = Tempfile.new("foo")
192
+ @tempfile.write("hello")
193
+ @tempfile.close
194
+ assert 5, File.size(@tempfile.path)
195
+ end
196
+
197
+ def test_tempfile_is_unlinked_when_ruby_exits
198
+ filename = run_script("tempfile_unlink_on_exit_example.rb").strip
199
+ assert !File.exist?(filename)
200
+ end
201
+
202
+ def test_size_flushes_buffer_before_determining_file_size
203
+ @tempfile = Tempfile.new("foo")
204
+ @tempfile.write("hello")
205
+ assert 0, File.size(@tempfile.path)
206
+ assert 5, @tempfile.size
207
+ assert 5, File.size(@tempfile.path)
208
+ end
209
+
210
+ def test_size_works_if_file_is_closed
211
+ @tempfile = Tempfile.new("foo")
212
+ @tempfile.write("hello")
213
+ @tempfile.close
214
+ assert 5, @tempfile.size
215
+ end
216
+
217
+ def test_concurrency
218
+ threads = []
219
+ tempfiles = []
220
+ lock = Mutex.new
221
+ cond = ConditionVariable.new
222
+ start = false
223
+
224
+ 4.times do
225
+ threads << Thread.new do
226
+ lock.synchronize do
227
+ while !start
228
+ cond.wait(lock)
229
+ end
230
+ end
231
+ result = []
232
+ 30.times do
233
+ result << Tempfile.new('foo')
234
+ end
235
+ Thread.current[:result] = result
236
+ end
237
+ end
238
+
239
+ lock.synchronize do
240
+ start = true
241
+ cond.broadcast
242
+ end
243
+ threads.each do |thread|
244
+ thread.join
245
+ tempfiles |= thread[:result]
246
+ end
247
+ filenames = tempfiles.map { |f| f.path }
248
+ begin
249
+ assert_equal filenames.size, filenames.uniq.size
250
+ ensure
251
+ tempfiles.each do |tempfile|
252
+ tempfile.close!
253
+ end
254
+ end
255
+ end
256
+
257
+ if defined?(Encoding)
258
+ def test_tempfile_encoding_nooption
259
+ default_external = Encoding.default_external
260
+ t = Tempfile.new("TEST")
261
+ t.write("\xE6\x9D\xBE\xE6\xB1\x9F")
262
+ t.rewind
263
+ assert_equal(default_external, t.read.encoding)
264
+ end
265
+
266
+ def test_tempfile_encoding_ascii8bit
267
+ default_external = Encoding.default_external
268
+ t = Tempfile.new("TEST", :encoding => "ascii-8bit")
269
+ t.write("\xE6\x9D\xBE\xE6\xB1\x9F")
270
+ t.rewind
271
+ assert_equal(Encoding::ASCII_8BIT, t.read.encoding)
272
+ end
273
+
274
+ def test_tempfile_encoding_ascii8bit2
275
+ default_external = Encoding.default_external
276
+ t = Tempfile.new("TEST", Dir::tmpdir, :encoding => "ascii-8bit")
277
+ t.write("\xE6\x9D\xBE\xE6\xB1\x9F")
278
+ t.rewind
279
+ assert_equal(Encoding::ASCII_8BIT, t.read.encoding)
280
+ end
281
+ end
282
+ end
283
+
@@ -0,0 +1,4 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "example_helper"))
2
+ require 'better/tempfile'
3
+
4
+ puts Better::Tempfile.new('foo').path
@@ -0,0 +1,25 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
2
+ require 'rubygems'
3
+ require 'test/unit'
4
+ require 'rbconfig'
5
+ require 'better/tempfile'
6
+ require 'mocha' # must be included last, otherwise it won't work!
7
+
8
+ def run_script(script, *args)
9
+ output = Better::Tempfile.new('output')
10
+ begin
11
+ output.close
12
+
13
+ ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) + Config::CONFIG['EXEEXT']
14
+ command = [ruby, File.join(File.dirname(__FILE__), script), output.path, *args]
15
+
16
+ if system(*command)
17
+ File.read(output.path)
18
+ else
19
+ raise "Command failed: #{command.join(' ')}"
20
+ end
21
+ ensure
22
+ output.close if !output.closed?
23
+ output.unlink
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: peritor-better
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Hongli Lai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-05-21 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Collection of better replacements for Ruby standard libraries.
17
+ email: hongli@phusion.nl
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README.rdoc
26
+ - LICENSE
27
+ - better.gemspec
28
+ - Rakefile
29
+ - lib/better/tempfile.rb
30
+ - lib/better/override/tempfile.rb
31
+ - test/test_helper.rb
32
+ - test/example_helper.rb
33
+ - test/tempfile_test.rb
34
+ - test/tempfile_explicit_close_and_unlink_example.rb
35
+ - test/tempfile_explicit_unlink_example.rb
36
+ - test/tempfile_unlink_on_exit_example.rb
37
+ has_rdoc: true
38
+ homepage: http://better.rubyforge.org/
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project: better
61
+ rubygems_version: 1.3.5
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Collection of better replacements for Ruby standard libraries
65
+ test_files: []
66
+