file-find 0.5.1 → 0.5.3

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
  SHA256:
3
- metadata.gz: f1b89ade1ff89a891f6aa2a44fa801fa9d2758b3784074f8868a5b3319dc68a5
4
- data.tar.gz: 52f17946eb45f73bd98b0804a9696e5e4d7c2f17d9ea002a20fa109fde7e10f3
3
+ metadata.gz: f14f3ba41e3f587f6a7f5206d8a1e6111d5b160b3e8dd5bb8126077def8a6ad9
4
+ data.tar.gz: dc7baffb76c1e1aff783944e3185b05e0a605ff379a23dd0fc826c21cddf6b5d
5
5
  SHA512:
6
- metadata.gz: e6d0e1a56de403c2e7327f2d9af3ec6c68528d53f0cb0b83e40362b1ab8dddda020212cc726428ea0791130c098c1a41f53f22a65fad314d8dd9fee05fdc7fa3
7
- data.tar.gz: 6c68a1b0e85905844b44feb04036834bb39e3ccaff5b732bb64d6280bb3eb86248f92f74570e60d37704fbc4d42ea4f99f2406c6d4be61ce7c9a8e3f89f1f1bb
6
+ metadata.gz: 7bba2f630c22ff5221e59a4a6128776e834c55200bea17aa355c85ffb21c0a96eb910b4bfb2d01f6e84e49071baa3aeda01d8046f70b76d344178c3e200f10aa
7
+ data.tar.gz: 59c2635c6da46debbbadf874121577b2c1d9a2149369df7976cfaa86d50a36a35f842ef365f76d5a6ae729f6879d0fea9b2c780a8c65b0ba8609403830e8a03e
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGES.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## 0.5.3 - 4-Jan-2026
2
+ * Internal refactoring, some performance improvements.
3
+ * Dependency updates for Windows since Ruby 4+ no longer includes win32ole
4
+ in its stdlib. Use the gem instead.
5
+ * Some specs restored for Windows, and specs run with warnings enabled by default.
6
+
7
+ ## 0.5.2 - 22-Nov-2025
8
+ * Use fnmatch instead of some overworked checks for Windows paths using glob.
9
+ * Minor refactoring improvements for atime/ctime/mtime checks.
10
+ * Minor refactoring improvement for ftype check.
11
+ * Minor refactoring improvement for filetest check.
12
+ * Added MacOS and FreeBSD to the test matrix.
13
+ * The specs now run in documentation mode by default.
14
+
1
15
  ## 0.5.1 - 17-Jun-2025
2
16
  * Updated, fixed or skipped some specs on Windows.
3
17
  * Some rubocop cleanup and minor refactors.
data/MANIFEST.md CHANGED
@@ -8,4 +8,4 @@
8
8
  * certs/djberg96_pub.pem
9
9
  * lib/file-find.rb
10
10
  * lib/file/find.rb
11
- * test/test_file_find.rb
11
+ * spec/file_find_spec.rb
data/README.md CHANGED
@@ -115,7 +115,7 @@ Apache-2.0
115
115
 
116
116
  ## Copyright
117
117
 
118
- (C) 2007-2025, Daniel J. Berger, All Rights Reserved
118
+ (C) 2007-2026, Daniel J. Berger, All Rights Reserved
119
119
 
120
120
  ## Author
121
121
 
data/Rakefile CHANGED
@@ -26,6 +26,7 @@ RuboCop::RakeTask.new
26
26
 
27
27
  RSpec::Core::RakeTask.new(:spec) do |t|
28
28
  t.pattern = ['spec/file_find_spec.rb']
29
+ t.rspec_opts = '-f documentation -w'
29
30
  end
30
31
 
31
32
  # Clean up afterwards
data/file-find.gemspec CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'file-find'
5
- spec.version = '0.5.1'
5
+ spec.version = '0.5.3'
6
6
  spec.author = 'Daniel Berger'
7
7
  spec.license = 'Apache-2.0'
8
8
  spec.summary = 'A better way to find files'
@@ -25,11 +25,18 @@ Gem::Specification.new do |spec|
25
25
  }
26
26
 
27
27
  spec.add_dependency('sys-admin', '~> 1.7')
28
+
29
+ if Gem.win_platform?
30
+ spec.add_dependency('win32ole')
31
+ spec.add_dependency('win32-registry') # Until sys-admin gets updated properly
32
+ end
33
+
28
34
  spec.add_development_dependency('rspec', '~> 3.9')
29
35
  spec.add_development_dependency('fakefs', '~> 3.0')
30
36
  spec.add_development_dependency('rake')
31
37
  spec.add_development_dependency('rubocop')
32
38
  spec.add_development_dependency('rubocop-rspec')
39
+ spec.add_development_dependency('sys-uname')
33
40
 
34
41
  spec.description = <<-EOF
35
42
  The file-find library provides a better, more object oriented approach
data/lib/file/find.rb CHANGED
@@ -13,7 +13,7 @@ end
13
13
  # files on your filesystem.
14
14
  class File::Find
15
15
  # The version of the file-find library
16
- VERSION = '0.5.1'
16
+ VERSION = '0.5.3'
17
17
 
18
18
  # :stopdoc:
19
19
  VALID_OPTIONS = %w[
@@ -111,7 +111,7 @@ class File::Find
111
111
  attr_accessor :mtime
112
112
 
113
113
  # The name pattern used to limit file searches. The patterns that are legal
114
- # for Dir.glob are legal here. The default is '*', i.e. everything.
114
+ # for File.fnmatch are legal here. The default is '*', i.e. everything.
115
115
  #
116
116
  attr_accessor :name
117
117
 
@@ -212,7 +212,7 @@ class File::Find
212
212
  #
213
213
  def find
214
214
  results = [] unless block_given?
215
- paths = @path.is_a?(String) ? [@path] : @path # Ruby 1.9.x compatibility
215
+ paths = Array(@path)
216
216
  queue = paths.dup
217
217
 
218
218
  if @prune
@@ -225,116 +225,77 @@ class File::Find
225
225
  until queue.empty?
226
226
  path = queue.shift
227
227
  begin
228
- Dir.foreach(path) do |file|
229
- next if file == '.'
230
- next if file == '..'
228
+ Dir.children(path).each do |basename|
229
+ # Fast rejections before expensive operations
230
+ next if prune_regex && prune_regex.match(basename)
231
231
 
232
- if prune_regex && prune_regex.match(file)
233
- next
234
- end
235
-
236
- file = File.join(path, file)
232
+ # Check name pattern early to potentially skip stat
233
+ # (but we still need to stat directories to add them to queue)
234
+ matches_name = File.fnmatch?(@name, basename)
237
235
 
236
+ file = File.join(path, basename)
238
237
  stat_method = @follow ? :stat : :lstat
238
+
239
239
  # Skip files we cannot access, stale links, etc.
240
240
  begin
241
241
  stat_info = File.send(stat_method, file)
242
242
  rescue Errno::ENOENT, Errno::EACCES
243
243
  next
244
244
  rescue Errno::ELOOP
245
- if stat_method.to_s != 'lstat'
245
+ if stat_method != :lstat
246
246
  stat_method = :lstat # Handle recursive symlinks
247
247
  retry
248
248
  end
249
249
  end
250
250
 
251
- # We need to escape any brackets in the directory name, unless already escaped.
252
- temp = File.dirname(file).gsub(/(?<!\\)([\[\]])/, '\\\\\1')
253
- glob = File.join(temp, @name)
254
-
255
- # Dir[] doesn't like backslashes
256
- if File::ALT_SEPARATOR
257
- file.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
258
- glob.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
259
- end
260
-
261
- if @mount && stat_info.dev != @filesystem
262
- next
263
- end
251
+ # Skip non-directories that don't match name pattern early
252
+ next if !stat_info.directory? && !matches_name
264
253
 
265
- if @links && stat_info.nlink != @links
266
- next
267
- end
254
+ next if @mount && stat_info.dev != @filesystem
255
+ next if @links && stat_info.nlink != @links
268
256
 
257
+ # Calculate depth early if needed for maxdepth/mindepth checks
269
258
  if @maxdepth || @mindepth
270
259
  file_depth = file.split(File::SEPARATOR).reject(&:empty?).length
271
260
  current_base_path = [@path].flatten.find{ |tpath| file.include?(tpath) }
272
261
  path_depth = current_base_path.split(File::SEPARATOR).length
273
-
274
262
  depth = file_depth - path_depth
275
-
276
- if @maxdepth && (depth > @maxdepth)
277
- if File.directory?(file) && !(paths.include?(file) && depth > @maxdepth)
278
- queue << file
279
- end
280
- next
281
- end
282
-
283
- if @mindepth && (depth < @mindepth)
284
- if File.directory?(file) && !(paths.include?(file) && depth < @mindepth)
285
- queue << file
286
- end
287
- next
288
- end
289
263
  end
290
264
 
291
- # Add directories back onto the list of paths to search unless
292
- # they've already been added
293
- #
265
+ # Early termination: don't even add directories to queue if beyond maxdepth
294
266
  if stat_info.directory? && !paths.include?(file)
295
- queue << file
267
+ if @maxdepth && depth && depth >= @maxdepth
268
+ # At maxdepth, don't traverse into this directory
269
+ else
270
+ queue << file
271
+ end
296
272
  end
297
273
 
298
- next unless Dir[glob].include?(file)
299
-
300
- unless @filetest.empty?
301
- file_test = true
274
+ # Skip files that are beyond maxdepth or before mindepth
275
+ if @maxdepth && depth && depth > @maxdepth
276
+ next
277
+ end
302
278
 
303
- @filetest.each do |array|
304
- meth = array[0]
305
- bool = array[1]
279
+ if @mindepth && depth && depth < @mindepth
280
+ next
281
+ end
306
282
 
307
- unless File.send(meth, file) == bool
308
- file_test = false
309
- break
310
- end
311
- end
283
+ # For directories, always traverse but only yield if name matches
284
+ # For regular files, we already checked name above
285
+ next if stat_info.directory? && !matches_name
312
286
 
313
- next unless file_test
287
+ if !@filetest.empty? && !@filetest.all? { |meth, bool| File.send(meth, file) == bool }
288
+ next
314
289
  end
315
290
 
316
291
  if @atime || @ctime || @mtime
317
- date1 = Date.parse(Time.now.to_s)
318
-
319
- if @atime
320
- date2 = Date.parse(stat_info.atime.to_s)
321
- next unless (date1 - date2).numerator == @atime
322
- end
323
-
324
- if @ctime
325
- date2 = Date.parse(stat_info.ctime.to_s)
326
- next unless (date1 - date2).numerator == @ctime
327
- end
328
-
329
- if @mtime
330
- date2 = Date.parse(stat_info.mtime.to_s)
331
- next unless (date1 - date2).numerator == @mtime
332
- end
292
+ now = Date.today
293
+ next if @atime && (now - stat_info.atime.to_date).to_i != @atime
294
+ next if @ctime && (now - stat_info.ctime.to_date).to_i != @ctime
295
+ next if @mtime && (now - stat_info.mtime.to_date).to_i != @mtime
333
296
  end
334
297
 
335
- if @ftype && File.ftype(file) != @ftype
336
- next
337
- end
298
+ next if @ftype && stat_info.ftype != @ftype
338
299
 
339
300
  if @group
340
301
  if @group.is_a?(String)
@@ -9,6 +9,7 @@
9
9
  require 'rspec'
10
10
  require 'file-find'
11
11
  require 'sys-admin'
12
+ require 'sys-uname'
12
13
  require 'tmpdir'
13
14
  require 'pp' # Goofy workaround for FakeFS bug
14
15
  require 'fakefs/spec_helpers'
@@ -16,23 +17,24 @@ require 'fakefs/spec_helpers'
16
17
  RSpec.describe File::Find do
17
18
  include FakeFS::SpecHelpers
18
19
 
19
- let(:windows) { File::ALT_SEPARATOR }
20
- let(:elevated) { windows and Win32::Security.elevated_security? }
21
- let(:ruby_file) { 'file_find_test.rb' }
22
- let(:doc_file) { 'file_find_test.doc' }
20
+ let(:windows) { Sys::Platform.windows? }
21
+ let(:macos) { Sys::Platform.mac? }
22
+ let(:elevated) { windows and Win32::Security.elevated_security? }
23
+ let(:ruby_file) { 'file_find_test.rb' }
24
+ let(:doc_file) { 'file_find_test.doc' }
23
25
 
24
26
  let(:rule) { described_class.new }
25
27
  let(:txt_rule) { described_class.new(:name => '*.txt') }
26
28
 
27
29
  before(:all) do
28
30
  @loguser = Sys::Admin.get_user(Sys::Admin.get_login)
29
- group = File::ALT_SEPARATOR ? 'Users' : @loguser.gid
31
+ group = Sys::Platform.windows? ? 'Users' : @loguser.gid
30
32
  @logroup = Sys::Admin.get_group(group)
31
33
  end
32
34
 
33
35
  context 'constants', :constants do
34
36
  example 'version constant is set to expected value' do
35
- expect(File::Find::VERSION).to eq('0.5.1')
37
+ expect(File::Find::VERSION).to eq('0.5.3')
36
38
  expect(File::Find::VERSION).to be_frozen
37
39
  end
38
40
  end
@@ -209,6 +211,8 @@ RSpec.describe File::Find do
209
211
  # TODO: Update example for Windows
210
212
  example 'find with numeric group id works as expected' do
211
213
  skip 'group example skipped on MS Windows' if windows
214
+ skip 'group example skipped on Mac in CI' if macos && ENV['CI']
215
+
212
216
  rule = described_class.new(:name => '*.doc', :group => @loguser.gid)
213
217
  expect(rule.find).to eq([File.expand_path(doc_file)])
214
218
  end
@@ -216,6 +220,7 @@ RSpec.describe File::Find do
216
220
  # TODO: Update example for Windows
217
221
  example 'find with string group id works as expected' do
218
222
  skip 'group example skipped on MS Windows' if windows
223
+ skip 'group example skipped on Mac in CI' if macos && ENV['CI']
219
224
 
220
225
  rule = described_class.new(:name => '*.doc', :group => @logroup.name)
221
226
  expect(rule.find).to eq([File.expand_path(doc_file)])
@@ -269,7 +274,7 @@ RSpec.describe File::Find do
269
274
  end
270
275
 
271
276
  example 'links method returns expected result' do
272
- skip if windows && !elevated
277
+ skip if windows # && !elevated # TODO: Adjust for drive letter.
273
278
 
274
279
  rule1 = described_class.new(:name => '*.rb', :links => 2)
275
280
  rule2 = described_class.new(:name => '*.doc', :links => 1)
@@ -331,19 +336,16 @@ RSpec.describe File::Find do
331
336
  end
332
337
 
333
338
  example 'find with maxdepth 2 returns expected results' do
334
- pending if windows
335
339
  rule.maxdepth = 2
336
340
  expect(rule.find.map{ |e| File.basename(e) }).to eq(['a.foo'])
337
341
  end
338
342
 
339
343
  example 'find with maxdepth 3 returns expected results' do
340
- pending if windows
341
344
  rule.maxdepth = 3
342
345
  expect(rule.find.map{ |e| File.basename(e) }).to contain_exactly('a.foo', 'b.foo', 'c.foo')
343
346
  end
344
347
 
345
348
  example 'find with nil maxdepth option returns everything' do
346
- pending if windows
347
349
  rule.maxdepth = nil
348
350
  results = ['a.foo', 'b.foo', 'c.foo', 'd.foo', 'e.foo', 'f.foo']
349
351
  expect(rule.find.map{ |e| File.basename(e) }).to match_array(results)
@@ -362,7 +364,6 @@ RSpec.describe File::Find do
362
364
  end
363
365
 
364
366
  example 'find with maxdepth 3 returns expected results for directories' do
365
- pending if windows
366
367
  rule.pattern = 'a3'
367
368
  rule.maxdepth = 3
368
369
  expect(rule.find.map{ |e| File.basename(e) }).to eq(['a3'])
@@ -394,35 +395,30 @@ RSpec.describe File::Find do
394
395
  end
395
396
 
396
397
  example 'find with mindepth option returns expected results at depth 0' do
397
- pending if windows
398
398
  rule.mindepth = 0
399
399
  array = ['a.min', 'b.min', 'c.min', 'd.min', 'e.min', 'f.min', 'z.min']
400
400
  expect(rule.find.map{ |e| File.basename(e) }).to match_array(array)
401
401
  end
402
402
 
403
403
  example 'find with mindepth option returns expected results at depth 1' do
404
- pending if windows
405
404
  rule.mindepth = 1
406
405
  array = ['a.min', 'b.min', 'c.min', 'd.min', 'e.min', 'f.min', 'z.min']
407
406
  expect(rule.find.map{ |e| File.basename(e) }).to match_array(array)
408
407
  end
409
408
 
410
409
  example 'find with mindepth option returns expected results at depth 2' do
411
- pending if windows
412
410
  rule.mindepth = 2
413
411
  array = ['a.min', 'b.min', 'c.min', 'd.min', 'e.min', 'f.min']
414
412
  expect(rule.find.map{ |e| File.basename(e) }).to match_array(array)
415
413
  end
416
414
 
417
415
  example 'find with mindepth option returns expected results at depth 3' do
418
- pending if windows
419
416
  rule.mindepth = 3
420
417
  array = ['b.min', 'c.min', 'd.min', 'e.min', 'f.min']
421
418
  expect(rule.find.map{ |e| File.basename(e) }).to match_array(array)
422
419
  end
423
420
 
424
421
  example 'find with mindepth option returns expected results at depth 4' do
425
- pending if windows
426
422
  rule.mindepth = 4
427
423
  array = ['d.min', 'e.min', 'f.min']
428
424
  expect(rule.find.map{ |e| File.basename(e) }).to match_array(array)
@@ -565,6 +561,7 @@ RSpec.describe File::Find do
565
561
 
566
562
  example 'user method works with numeric id as expected' do
567
563
  pending if windows # TODO: Get this working on Windows
564
+ skip 'user example skipped on Mac in CI' if macos && ENV['CI']
568
565
 
569
566
  if windows && elevated
570
567
  uid = @loguser.gid # Windows assigns the group if any member is an admin
@@ -577,7 +574,7 @@ RSpec.describe File::Find do
577
574
  end
578
575
 
579
576
  example 'user method works with string as expected' do
580
- pending if windows # TODO: Get this working on Windows
577
+ skip 'user example skipped on Mac in CI' if macos && ENV['CI']
581
578
 
582
579
  skip if windows && elevated
583
580
  rule = described_class.new(:name => '*.doc', :user => @loguser.name)
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,11 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: file-find
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Berger
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain:
11
10
  - |
@@ -35,7 +34,7 @@ cert_chain:
35
34
  ORVCZpRuCPpmC8qmqxUnARDArzucjaclkxjLWvCVHeFa9UP7K3Nl9oTjJNv+7/jM
36
35
  WZs4eecIcUc4tKdHxcAJ0MO/Dkqq7hGaiHpwKY76wQ1+8xAh
37
36
  -----END CERTIFICATE-----
38
- date: 2025-06-18 00:00:00.000000000 Z
37
+ date: 1980-01-02 00:00:00.000000000 Z
39
38
  dependencies:
40
39
  - !ruby/object:Gem::Dependency
41
40
  name: sys-admin
@@ -121,6 +120,20 @@ dependencies:
121
120
  - - ">="
122
121
  - !ruby/object:Gem::Version
123
122
  version: '0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: sys-uname
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
124
137
  description: |2
125
138
  The file-find library provides a better, more object oriented approach
126
139
  to finding files. It allows you to find files based on a variety of
@@ -155,7 +168,6 @@ metadata:
155
168
  rubygems_mfa_required: 'true'
156
169
  github_repo: https://github.com/djberg96/file-find
157
170
  funding_uri: https://github.com/sponsors/djberg96
158
- post_install_message:
159
171
  rdoc_options: []
160
172
  require_paths:
161
173
  - lib
@@ -170,8 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
170
182
  - !ruby/object:Gem::Version
171
183
  version: '0'
172
184
  requirements: []
173
- rubygems_version: 3.5.22
174
- signing_key:
185
+ rubygems_version: 3.6.9
175
186
  specification_version: 4
176
187
  summary: A better way to find files
177
188
  test_files:
metadata.gz.sig CHANGED
Binary file