Skip to content

Commit 721cbf6

Browse files
hsbtclaude
andcommitted
Implement Pathname.mktmpdir in builtin Pathname
Move mktmpdir from lib/pathname.rb (which delegated to Dir.mktmpdir) into pathname_builtin.rb with a pure Ruby implementation that resolves temporary directories from environment variables and Etc.systmpdir without requiring the tmpdir library. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 45cea18 commit 721cbf6

2 files changed

Lines changed: 71 additions & 43 deletions

File tree

lib/pathname.rb

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,46 +9,3 @@
99
#
1010
# For documentation, see class Pathname.
1111
#
12-
13-
class Pathname # * tmpdir *
14-
# call-seq:
15-
# Pathname.mktmpdir -> new_pathname
16-
# Pathname.mktmpdir {|pathname| ... } -> object
17-
#
18-
# Creates:
19-
#
20-
# - A temporary directory via Dir.mktmpdir.
21-
# - A \Pathname object that contains the path to that directory.
22-
#
23-
# With no block given, returns the created pathname;
24-
# the caller should delete the created directory when it is no longer needed
25-
# (FileUtils.rm_r is a convenient method for the deletion):
26-
#
27-
# pathname = Pathname.mktmpdir
28-
# dirpath = pathname.to_s
29-
# Dir.exist?(dirpath) # => true
30-
# # Do something with the directory.
31-
# require 'fileutils'
32-
# FileUtils.rm_r(dirpath)
33-
#
34-
# With a block given, calls the block with the created pathname;
35-
# on block exit, automatically deletes the created directory and all its contents;
36-
# returns the block's exit value:
37-
#
38-
# pathname = Pathname.mktmpdir do |p|
39-
# # Do something with the directory.
40-
# p
41-
# end
42-
# Dir.exist?(pathname.to_s) # => false
43-
def self.mktmpdir
44-
require 'tmpdir' unless defined?(Dir.mktmpdir)
45-
if block_given?
46-
Dir.mktmpdir do |dir|
47-
dir = self.new(dir)
48-
yield dir
49-
end
50-
else
51-
self.new(Dir.mktmpdir)
52-
end
53-
end
54-
end

pathname_builtin.rb

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,43 @@ def rmtree
14001400
self
14011401
end
14021402

1403+
# call-seq:
1404+
# Pathname.mktmpdir -> new_pathname
1405+
# Pathname.mktmpdir {|pathname| ... } -> object
1406+
#
1407+
# Creates a temporary directory and returns a Pathname for it.
1408+
#
1409+
# The previous implementation delegated to Dir.mktmpdir and required
1410+
# the tmpdir library. This builtin implementation removes that dependency.
1411+
#
1412+
# With no block given, returns the created pathname;
1413+
# the caller should delete the created directory when it is no longer needed:
1414+
#
1415+
# pathname = Pathname.mktmpdir
1416+
# Dir.exist?(pathname.to_s) # => true
1417+
#
1418+
# With a block given, calls the block with the created pathname;
1419+
# on block exit, automatically deletes the created directory and all its contents;
1420+
# returns the block's exit value:
1421+
#
1422+
# Pathname.mktmpdir do |dir|
1423+
# # Do something with dir.
1424+
# end
1425+
#
1426+
def self.mktmpdir
1427+
tmpdir = self.new(resolve_tmpdir)
1428+
path = tmpdir.__send__(:make_tmpdir)
1429+
if block_given?
1430+
begin
1431+
yield path
1432+
ensure
1433+
path.rmtree if path.exist?
1434+
end
1435+
else
1436+
path
1437+
end
1438+
end
1439+
14031440
private
14041441

14051442
def remove_entry(path)
@@ -1413,6 +1450,40 @@ def remove_entry(path)
14131450
File.unlink(path)
14141451
end
14151452
end
1453+
1454+
def self.resolve_tmpdir
1455+
['TMPDIR', 'TMP', 'TEMP'].each do |name|
1456+
dir = ENV[name] rescue next
1457+
next if dir.nil? || dir.empty?
1458+
dir = File.expand_path(dir)
1459+
stat = File.stat(dir) rescue next
1460+
next unless stat.directory?
1461+
next unless File.writable?(dir)
1462+
next if stat.world_writable? && !stat.sticky?
1463+
return dir
1464+
end
1465+
systmpdir = (defined?(Etc.systmpdir) ? Etc.systmpdir : '/tmp')
1466+
return systmpdir if File.directory?(systmpdir) && File.writable?(systmpdir)
1467+
'/tmp'
1468+
end
1469+
1470+
def make_tmpdir
1471+
t = Time.now.strftime("%Y%m%d")
1472+
n = nil
1473+
begin
1474+
name = "d#{t}-#{$$}-#{random_name}#{n ? "-#{n}" : ''}"
1475+
path = File.join(@path, name)
1476+
Dir.mkdir(path, 0700)
1477+
rescue Errno::EEXIST
1478+
n = (n || 0) + 1
1479+
retry
1480+
end
1481+
self.class.new(path)
1482+
end
1483+
1484+
def random_name
1485+
Random.urandom(4).unpack1("L").%(36**6).to_s(36)
1486+
end
14161487
end
14171488

14181489
class Pathname

0 commit comments

Comments
 (0)