Skip to content

Commit

Permalink
Use ftype
Browse files Browse the repository at this point in the history
  • Loading branch information
robotdana committed Nov 22, 2023
1 parent 223ec20 commit aa8f16e
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
strategy:
fail-fast: false
matrix:
ruby: [2.7, '3.0', 3.1, 3.2, 3.3, ruby-head, jruby-9.4, jruby-head]
ruby: [2.7, '3.0', 3.1, 3.2, ruby-head, jruby-9.4, jruby-head]
platform: [ubuntu, windows, macos]
continue-on-error: ${{ endsWith(matrix.ruby, 'head') }}
runs-on: ${{matrix.platform}}-latest
Expand Down
45 changes: 18 additions & 27 deletions lib/path_list/candidate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ class PathList
# @api private
# The object that gets passed to all {PathList::Matcher} subclasses #match
class Candidate
# :nocov:
if ::RUBY_PLATFORM == 'java' && ::RbConfig::CONFIG['host_os'].match?(/mswin|mingw/)
Autoloader.autoload(self)
include JrubyWindowsFix
end
# :nocov:

attr_reader :full_path

# @param full_path [String] resolved absolute path
Expand All @@ -18,6 +25,7 @@ def initialize(full_path, directory = nil, shebang = nil)

@child_candidates = nil
@children = nil
@ftype = nil
end

# @return [String] full path downcased
Expand Down Expand Up @@ -54,33 +62,16 @@ def children
end
end

# :nocov:
if ::RUBY_PLATFORM == 'java' && ::RbConfig::CONFIG['host_os'].match?(/mswin|mingw/)
# @return [Boolean] whether this path is a directory (false for symlinks to directories)
def directory?
return @directory unless @directory.nil?

@directory = if ::File.symlink?(@full_path)
warn 'Symlink lstat'
warn lstat.inspect
false
else
lstat&.directory? || false
end
end
# :nocov:
else
# @return [Boolean] whether this path is a directory (false for symlinks to directories)
def directory?
return @directory unless @directory.nil?
# @return [Boolean] whether this path is a directory (false for symlinks to directories)
def directory?
return @directory unless @directory.nil?

@directory = lstat&.directory? || false
end
@directory = ftype == 'directory'
end

# @return [Boolean] whether this path exists
def exists?
lstat ? true : false
ftype != 'error'
end

alias_method :original_inspect, :inspect # leftovers:keep
Expand Down Expand Up @@ -112,7 +103,7 @@ def shebang
''
end
rescue ::IOError, ::SystemCallError
@lstat ||= nil
@ftype ||= 'error'
''
ensure
file&.close
Expand All @@ -121,12 +112,12 @@ def shebang

private

def lstat
return @lstat if defined?(@lstat)
def ftype
return @ftype if @ftype

@lstat = ::File.lstat(@full_path)
@ftype = ::File.ftype(@full_path)
rescue ::SystemCallError
@lstat = nil
@ftype = 'error'
end
end
end
26 changes: 26 additions & 0 deletions lib/path_list/candidate/jruby_windows_fix.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

# :nocov:
class PathList
class Candidate
# @api private
module JrubyWindowsFix
private

# https://github.com/jruby/jruby/issues/8018
# ftype follows symlinks on jruby on windows. it shouldn't.
def ftype
return @ftype if @ftype

@ftype = if ::File.symlink?(@full_path)
'link'
else
::File.ftype(@full_path)
end
rescue ::SystemCallError
@ftype = 'error'
end
end
end
end
# :nocov:
27 changes: 12 additions & 15 deletions spec/candidate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@
end

describe '#parent' do
before { allow(File).to receive_messages(exist?: nil, lstat: nil, directory?: nil) }
before { allow(File).to receive_messages(exist?: nil, ftype: nil, directory?: nil) }

it 'returns a candidate for the parent with preset directory value' do
expect(candidate.parent).to be_like described_class.new('/path/from/root', true)
expect(candidate.parent).to have_attributes(
directory?: true
)
expect(File).not_to have_received(:directory?)
expect(File).not_to have_received(:lstat)
expect(File).not_to have_received(:ftype)
end

context 'when the path is /' do
Expand All @@ -54,35 +54,35 @@
before { create_file_list 'foo' }

it 'is memoized when true' do
allow(File).to receive(:lstat).and_call_original
allow(File).to receive(:ftype).and_call_original

expect(candidate.exists?).to be true
expect(File).to have_received(:lstat).once
expect(File).to have_received(:ftype).once
expect(candidate.exists?).to be true
expect(File).to have_received(:lstat).once
expect(File).to have_received(:ftype).once
end
end

context 'when the file does not exist' do
let(:full_path) { './foo' }

it 'is memoized when false' do
allow(File).to receive(:lstat).and_call_original
allow(File).to receive(:ftype).and_call_original

expect(candidate.exists?).to be false
expect(File).to have_received(:lstat).with('./foo').once
expect(File).to have_received(:ftype).with('./foo').once
expect(candidate.exists?).to be false
expect(File).to have_received(:lstat).with('./foo').once
expect(File).to have_received(:ftype).with('./foo').once
end

it 'is false when there is an error' do
allow(File).to receive(:lstat).and_call_original
allow(File).to receive(:lstat).with(full_path).and_raise(Errno::EACCES)
allow(File).to receive(:ftype).and_call_original
allow(File).to receive(:ftype).with(full_path).and_raise(Errno::EACCES)

expect(candidate.exists?).to be false
expect(File).to have_received(:lstat).with('./foo').once
expect(File).to have_received(:ftype).with('./foo').once
expect(candidate.exists?).to be false
expect(File).to have_received(:lstat).with('./foo').once
expect(File).to have_received(:ftype).with('./foo').once
end
end
end
Expand Down Expand Up @@ -139,9 +139,6 @@
create_symlink('foo' => 'foo_target')

candidate = described_class.new(File.expand_path('foo'))
expect(File.symlink?('./foo')).to be true
expect(File.stat('./foo')).to have_attributes(directory?: false, symlink?: true)
expect(candidate.send(:lstat)).to have_attributes(directory?: false, symlink?: true)
expect(candidate).not_to be_directory
end
end
Expand Down
4 changes: 2 additions & 2 deletions spec/path_list_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
it 'copes with being given fs root' do
whatever_file_we_get = subject.each('/').first
expect(whatever_file_we_get).not_to start_with('/')
# use lstat because it could be a symlink to nowhere and File.exist? will be sad
expect(File.lstat("/#{whatever_file_we_get}")).to be_a File::Stat
# use symlink? because it could be a symlink to nowhere and File.exist? would return false
expect { File.symlink?("/#{whatever_file_we_get}") || File.exist?("/#{whatever_file_we_get}") }.not_to raise_error
end

it 'copes with being given nonsense root' do
Expand Down

0 comments on commit aa8f16e

Please sign in to comment.