file-find 0.1.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.
- data/CHANGES +2 -0
- data/MANIFEST +7 -0
- data/README +64 -0
- data/lib/file/find.rb +206 -0
- data/test/tc_find.rb +125 -0
- metadata +53 -0
data/CHANGES
ADDED
data/MANIFEST
ADDED
data/README
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
= Description
|
2
|
+
This is a drop-in replacement for the find module currently in the standard
|
3
|
+
library. It is modeled on a typical 'find' command found on most Unix systems.
|
4
|
+
|
5
|
+
= Synopsis
|
6
|
+
rule = File::Find.new(
|
7
|
+
:name => "*.rb",
|
8
|
+
:follow => false,
|
9
|
+
:path => ['/usr/local/lib', '/opt/local/lib']
|
10
|
+
)
|
11
|
+
|
12
|
+
rule.find{ |f|
|
13
|
+
puts f
|
14
|
+
}
|
15
|
+
|
16
|
+
= Installation
|
17
|
+
rake test (optional)
|
18
|
+
rake install (non-gem) or rake install_gem (gem)
|
19
|
+
|
20
|
+
= Rationale
|
21
|
+
The current find module in the standard library is inadequate. It is, quite
|
22
|
+
frankly, not much more than a plain Dir.glob call. This package provides an
|
23
|
+
interface based on options typically available on your command line 'find'
|
24
|
+
command, thus allowing you much greater control over how you find your files.
|
25
|
+
|
26
|
+
I am aware of the find2 package by Motoyuki Kasahara, but it supports very
|
27
|
+
few options, hasn't been updated in over six years and isn't packaged properly.
|
28
|
+
|
29
|
+
= Options
|
30
|
+
* atime
|
31
|
+
* ctime
|
32
|
+
* follow
|
33
|
+
* ftype
|
34
|
+
* inum
|
35
|
+
* group
|
36
|
+
* name
|
37
|
+
* path
|
38
|
+
* size
|
39
|
+
* user
|
40
|
+
|
41
|
+
See the RDoc documentation for more details about these options.
|
42
|
+
|
43
|
+
= Future Plans
|
44
|
+
More options will be added as time permits, and requests will definitely be
|
45
|
+
considered. Please log any feature requests on the project page at
|
46
|
+
http://www.rubyforge.org/projects/shards.
|
47
|
+
|
48
|
+
Some specific things I plan on adding:
|
49
|
+
|
50
|
+
* Support for limiting depth.
|
51
|
+
* Greater flexibility with the size option (and perhaps others).
|
52
|
+
|
53
|
+
= Bugs
|
54
|
+
None that I'm aware of. Please log any bug reports on the project page at
|
55
|
+
http://www.rubyforge.org/projects/shards.
|
56
|
+
|
57
|
+
= License
|
58
|
+
Ruby's
|
59
|
+
|
60
|
+
= Copyright
|
61
|
+
(C) 2007, Daniel J. Berger, All Rights Reserved
|
62
|
+
|
63
|
+
= Author
|
64
|
+
Daniel J. Berger
|
data/lib/file/find.rb
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
class File::Find
|
4
|
+
# The version of this package
|
5
|
+
VERSION = '0.1.0'
|
6
|
+
|
7
|
+
# :stopdoc:
|
8
|
+
VALID_OPTIONS = %w/
|
9
|
+
atime
|
10
|
+
ctime
|
11
|
+
follow
|
12
|
+
ftype
|
13
|
+
inum
|
14
|
+
group
|
15
|
+
name
|
16
|
+
path
|
17
|
+
size
|
18
|
+
user
|
19
|
+
/
|
20
|
+
# :startdoc:
|
21
|
+
|
22
|
+
# The starting path(s) for the search. The default is the current directory.
|
23
|
+
# This can be a single path or an array of paths.
|
24
|
+
#
|
25
|
+
attr_accessor :path
|
26
|
+
|
27
|
+
# The list of options passed to the constructor and/or used by the
|
28
|
+
# File::Find#find method.
|
29
|
+
#
|
30
|
+
attr_accessor :options
|
31
|
+
|
32
|
+
# Limits searches by file access time, where the value you supply is the
|
33
|
+
# number of days back from the time that the File::Find#find method was
|
34
|
+
# called. Note that the File::Find#find method itself alters the access
|
35
|
+
# times.
|
36
|
+
#
|
37
|
+
attr_accessor :atime
|
38
|
+
|
39
|
+
# Limits searches by file change time, where the value you supply is the
|
40
|
+
# number of days back from the time that the File::Find#find method was
|
41
|
+
# called.
|
42
|
+
#
|
43
|
+
attr_accessor :ctime
|
44
|
+
|
45
|
+
# Limits searches to files that belong to a specific group ID.
|
46
|
+
#
|
47
|
+
attr_accessor :group
|
48
|
+
|
49
|
+
# Controls the behavior of how symlinks are followed. If set to true, then
|
50
|
+
# follows the file pointed to. If false, it considers the symlink itself.
|
51
|
+
#
|
52
|
+
attr_accessor :follow
|
53
|
+
|
54
|
+
# Limits searches to specific types of files. The possible values here are
|
55
|
+
# those returned by the File.ftype method.
|
56
|
+
#
|
57
|
+
attr_accessor :ftype
|
58
|
+
|
59
|
+
# Limits search to a file with a specific inode number. Ignored on MS
|
60
|
+
# Windows.
|
61
|
+
#
|
62
|
+
attr_accessor :inum
|
63
|
+
|
64
|
+
# The name pattern used to limit file searches. The patterns that are legal
|
65
|
+
# for Dir.glob are legal here.
|
66
|
+
#
|
67
|
+
attr_accessor :name
|
68
|
+
|
69
|
+
# Limits searches to files that match the size, in bytes.
|
70
|
+
#
|
71
|
+
attr_accessor :size
|
72
|
+
|
73
|
+
# Limits searches to files that belong to a specific user ID.
|
74
|
+
#
|
75
|
+
attr_accessor :user
|
76
|
+
|
77
|
+
alias pattern name
|
78
|
+
|
79
|
+
# Creates and returns a new File::Find object. The options set for this
|
80
|
+
# object serve as the rules for determining what files the File::Find#find
|
81
|
+
# method will search for.
|
82
|
+
#
|
83
|
+
def initialize(options = {})
|
84
|
+
@options = options
|
85
|
+
|
86
|
+
@atime = nil
|
87
|
+
@ctime = nil
|
88
|
+
@ftype = nil
|
89
|
+
@group = nil
|
90
|
+
@follow = true
|
91
|
+
@inum = nil
|
92
|
+
@name = nil
|
93
|
+
@size = nil
|
94
|
+
@user = nil
|
95
|
+
|
96
|
+
validate_and_set_options(options) unless options.empty?
|
97
|
+
|
98
|
+
@path ||= Dir.pwd
|
99
|
+
end
|
100
|
+
|
101
|
+
# Executes the find based on the rules you set for the File::Find object.
|
102
|
+
# In block form, yields each file in turn that matches the specified rules.
|
103
|
+
# In non-block form it will return an array of matches instead.
|
104
|
+
#
|
105
|
+
def find
|
106
|
+
results = [] unless block_given?
|
107
|
+
paths = [@path]
|
108
|
+
|
109
|
+
catch(:loop) do
|
110
|
+
paths.each{ |path|
|
111
|
+
Dir.foreach(path){ |file|
|
112
|
+
next if file == '.'
|
113
|
+
next if file == '..'
|
114
|
+
|
115
|
+
file = File.join(path, file)
|
116
|
+
|
117
|
+
stat_method = @follow ? :lstat : :stat
|
118
|
+
|
119
|
+
# Skip files we cannot access, stale links, etc.
|
120
|
+
begin
|
121
|
+
stat_info = File.send(stat_method, file)
|
122
|
+
rescue Errno::ENOENT, Errno::EACCES
|
123
|
+
next
|
124
|
+
rescue Errno::ELOOP
|
125
|
+
stat_method = :lstat # Handle recursive symlinks
|
126
|
+
retry
|
127
|
+
end
|
128
|
+
|
129
|
+
# Add directories back onto the list of paths to search unless
|
130
|
+
# they've already been added.
|
131
|
+
#
|
132
|
+
# TODO: Add depth handling.
|
133
|
+
if stat_info.directory?
|
134
|
+
unless paths.include?(file)
|
135
|
+
paths << file
|
136
|
+
next
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
if @atime
|
141
|
+
date1 = Date.parse(Time.now.to_s)
|
142
|
+
date2 = Date.parse(stat_info.atime.to_s)
|
143
|
+
next unless (date1 - date2).numerator == @atime
|
144
|
+
end
|
145
|
+
|
146
|
+
if @ctime
|
147
|
+
date1 = Date.parse(Time.now.to_s)
|
148
|
+
date2 = Date.parse(stat_info.ctime.to_s)
|
149
|
+
next unless (date1 - date2).numerator == @ctime
|
150
|
+
end
|
151
|
+
|
152
|
+
if @ftype
|
153
|
+
next unless File.ftype(file) == @ftype
|
154
|
+
end
|
155
|
+
|
156
|
+
if @group
|
157
|
+
next unless stat_info.gid == @group
|
158
|
+
end
|
159
|
+
|
160
|
+
unless RUBY_PLATFORM.match('mswin')
|
161
|
+
if @inum
|
162
|
+
next unless stat_info.ino == @inum
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
if @name
|
167
|
+
glob = File.join(File.dirname(file), @name)
|
168
|
+
next unless Dir[glob].include?(file)
|
169
|
+
end
|
170
|
+
|
171
|
+
# TODO: Allow more flexible syntax here, e.g. "> 1024".
|
172
|
+
if @size
|
173
|
+
next unless stat_info.size == @size
|
174
|
+
end
|
175
|
+
|
176
|
+
if @user
|
177
|
+
next unless stat_info.uid == @user
|
178
|
+
end
|
179
|
+
|
180
|
+
if block_given?
|
181
|
+
yield file
|
182
|
+
else
|
183
|
+
results << file
|
184
|
+
end
|
185
|
+
}
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
block_given? ? nil : results
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
|
194
|
+
# This validates that the keys are valid. If they are, it sets the value
|
195
|
+
# of that key's corresponding method to the given value.
|
196
|
+
#
|
197
|
+
def validate_and_set_options(options)
|
198
|
+
options.each do |key, value|
|
199
|
+
key = key.to_s.downcase
|
200
|
+
unless VALID_OPTIONS.include?(key)
|
201
|
+
raise ArgumentError, "invalid option '#{key}'"
|
202
|
+
end
|
203
|
+
send("#{key}=", value)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
data/test/tc_find.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
######################################################################
|
2
|
+
# tc_find.rb
|
3
|
+
#
|
4
|
+
# Test case for the File::Find package.
|
5
|
+
######################################################################
|
6
|
+
Dir.chdir '..' if File.basename(Dir.pwd) == 'test'
|
7
|
+
$:.unshift File.join(Dir.pwd, 'lib')
|
8
|
+
|
9
|
+
require 'test/unit'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'file/find'
|
12
|
+
|
13
|
+
class TC_File_Find < Test::Unit::TestCase
|
14
|
+
def setup
|
15
|
+
@file1 = 'test1.rb'
|
16
|
+
@file2 = 'test1.txt'
|
17
|
+
@link1 = 'link1'
|
18
|
+
|
19
|
+
File.open(@file1, 'w'){}
|
20
|
+
File.open(@file2, 'w'){}
|
21
|
+
File.symlink(@file1, @link1)
|
22
|
+
|
23
|
+
@rule1 = File::Find.new(:name => '*.rb')
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_path
|
27
|
+
assert_respond_to(@rule1, :path)
|
28
|
+
assert_respond_to(@rule1, :path=)
|
29
|
+
assert_equal(Dir.pwd, @rule1.path)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_options
|
33
|
+
assert_respond_to(@rule1, :options)
|
34
|
+
assert_respond_to(@rule1, :options=)
|
35
|
+
assert_equal({:name => '*.rb'}, @rule1.options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_atime_basic
|
39
|
+
assert_respond_to(@rule1, :atime)
|
40
|
+
assert_respond_to(@rule1, :atime=)
|
41
|
+
assert_nil(@rule1.atime)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_atime
|
45
|
+
rule1 = File::Find.new(:name => "*.rb", :atime => 0)
|
46
|
+
rule2 = File::Find.new(:name => "*.rb", :atime => 1)
|
47
|
+
|
48
|
+
assert_equal(false, rule1.find.empty?)
|
49
|
+
assert_equal(true, rule2.find.empty?)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_ctime_basic
|
53
|
+
assert_respond_to(@rule1, :ctime)
|
54
|
+
assert_respond_to(@rule1, :ctime=)
|
55
|
+
assert_nil(@rule1.ctime)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_ctime
|
59
|
+
rule1 = File::Find.new(:name => "*.rb", :ctime => 0)
|
60
|
+
rule2 = File::Find.new(:name => "*.rb", :ctime => 1)
|
61
|
+
|
62
|
+
assert_equal(false, rule1.find.empty?)
|
63
|
+
assert_equal(true, rule2.find.empty?)
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_ftype_basic
|
67
|
+
assert_respond_to(@rule1, :ftype)
|
68
|
+
assert_respond_to(@rule1, :ftype=)
|
69
|
+
assert_nil(@rule1.ftype)
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_ftype
|
73
|
+
rule1 = File::Find.new(:name => "*.rb", :ftype => "file")
|
74
|
+
rule2 = File::Find.new(:name => "*.rb", :ftype => "characterSpecial")
|
75
|
+
|
76
|
+
assert_equal(false, rule1.find.empty?)
|
77
|
+
assert_equal(true, rule2.find.empty?)
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_group_basic
|
81
|
+
assert_respond_to(@rule1, :group)
|
82
|
+
assert_respond_to(@rule1, :group=)
|
83
|
+
assert_nil(@rule1.group)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_inum_basic
|
87
|
+
assert_respond_to(@rule1, :inum)
|
88
|
+
assert_respond_to(@rule1, :inum=)
|
89
|
+
assert_nil(@rule1.inum)
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_follow_basic
|
93
|
+
assert_respond_to(@rule1, :follow)
|
94
|
+
assert_respond_to(@rule1, :follow=)
|
95
|
+
assert_equal(true, @rule1.follow)
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_name_basic
|
99
|
+
assert_respond_to(@rule1, :name)
|
100
|
+
assert_respond_to(@rule1, :name=)
|
101
|
+
assert_equal('*.rb', @rule1.name)
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_size_basic
|
105
|
+
assert_respond_to(@rule1, :size)
|
106
|
+
assert_respond_to(@rule1, :size=)
|
107
|
+
assert_nil(@rule1.size)
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_user_basic
|
111
|
+
assert_respond_to(@rule1, :user)
|
112
|
+
assert_respond_to(@rule1, :user=)
|
113
|
+
assert_nil(@rule1.user)
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_expected_errors
|
117
|
+
assert_raises(Errno::ENOENT){ File::Find.new(:path => '/bogus/dir').find }
|
118
|
+
end
|
119
|
+
|
120
|
+
def teardown
|
121
|
+
FileUtils.rm_rf(@file1)
|
122
|
+
FileUtils.rm_rf(@file2)
|
123
|
+
FileUtils.rm_rf(@link1)
|
124
|
+
end
|
125
|
+
end
|
metadata
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.2
|
3
|
+
specification_version: 1
|
4
|
+
name: file-find
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2007-04-24 00:00:00 -06:00
|
8
|
+
summary: A better way to find files
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: djberg96@gmail.com
|
12
|
+
homepage: http://www.rubyforge.org/projects/shards
|
13
|
+
rubyforge_project:
|
14
|
+
description: A better way to find files
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Daniel Berger
|
31
|
+
files:
|
32
|
+
- lib/file
|
33
|
+
- lib/file/find.rb
|
34
|
+
- test/tc_find.rb
|
35
|
+
- README
|
36
|
+
- CHANGES
|
37
|
+
- MANIFEST
|
38
|
+
test_files:
|
39
|
+
- test/tc_find.rb
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
extra_rdoc_files:
|
43
|
+
- README
|
44
|
+
- CHANGES
|
45
|
+
- MANIFEST
|
46
|
+
executables: []
|
47
|
+
|
48
|
+
extensions: []
|
49
|
+
|
50
|
+
requirements: []
|
51
|
+
|
52
|
+
dependencies: []
|
53
|
+
|