file-find 0.3.4 → 0.3.5

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.
Files changed (7) hide show
  1. data/CHANGES +4 -0
  2. data/README +75 -73
  3. data/Rakefile +23 -35
  4. data/file-find.gemspec +20 -21
  5. data/lib/file/find.rb +481 -481
  6. data/test/test_file_find.rb +60 -45
  7. metadata +39 -17
data/CHANGES CHANGED
@@ -1,3 +1,7 @@
1
+ == 0.3.5 - 15-Jul-2011
2
+ * Fixed a bug with the :follow option.
3
+ * Gemspec, Rakefile and test cleanup.
4
+
1
5
  == 0.3.4 - 19-Sep-2009
2
6
  * Fixed a packaging bug. Thanks go to Gabriel Horner for the spot.
3
7
  * Added the 'gem' task to the Rakefile for building the gem. Removed the
data/README CHANGED
@@ -1,108 +1,110 @@
1
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.
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
4
 
5
5
  = Synopsis
6
- rule = File::Find.new(
7
- :pattern => "*.rb",
8
- :follow => false,
9
- :path => ['/usr/local/lib', '/opt/local/lib']
10
- )
6
+ require 'file/find'
11
7
 
12
- rule.find{ |f|
13
- puts f
14
- }
8
+ rule = File::Find.new(
9
+ :pattern => "*.rb",
10
+ :follow => false,
11
+ :path => ['/usr/local/lib', '/opt/local/lib']
12
+ )
13
+
14
+ rule.find{ |f|
15
+ puts f
16
+ }
15
17
 
16
18
  = Installation
17
- * rake test (optional)
18
- * rake install (non-gem) -OR- rake install_gem (gem)
19
+
20
+ gem install file-find
19
21
 
20
22
  = 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 library 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
23
 
26
- I am aware of the find2 library by Motoyuki Kasahara, but it supports very
27
- few options, hasn't been updated in over six years and isn't packaged properly.
24
+ The current find module in the standard library is inadequate. It is, quite
25
+ frankly, not much more than a plain Dir.glob call. This library provides an
26
+ interface based on options typically available on your command line 'find'
27
+ command, thus allowing you much greater control over how you find your files.
28
28
 
29
29
  = Options
30
- * atime
31
- * ctime
32
- * follow
33
- * ftype
34
- * inum (except Windows)
35
- * group (name or id)
36
- * maxdepth
37
- * mindepth
38
- * mount
39
- * mtime
40
- * name (or 'pattern')
41
- * path
42
- * perm (except Windows)
43
- * prune
44
- * size
45
- * user (name or id)
46
-
47
- In addition to the above options, FileTest methods such as 'readable?' and
48
- 'writable?' may be used as keys, with true or false for their values.
49
-
50
- See the RDoc documentation for more details about these options.
30
+ * atime
31
+ * ctime
32
+ * follow
33
+ * ftype
34
+ * inum (except Windows)
35
+ * group (name or id)
36
+ * maxdepth
37
+ * mindepth
38
+ * mount
39
+ * mtime
40
+ * name (or 'pattern')
41
+ * path
42
+ * perm (except Windows)
43
+ * prune
44
+ * size
45
+ * user (name or id)
46
+
47
+ In addition to the above options, FileTest methods such as 'readable?' and
48
+ 'writable?' may be used as keys, with true or false for their values.
49
+
50
+ See the RDoc documentation for more details about these options.
51
51
 
52
52
  = Future Plans
53
- More options will be added as time permits, and requests will definitely be
54
- considered. Please log any feature requests on the project page at
55
- http://www.rubyforge.org/projects/shards.
53
+ More options will be added as time permits, and requests will definitely be
54
+ considered. Please log any feature requests on the project page at
55
+ http://www.rubyforge.org/projects/shards.
56
56
 
57
- Some specific things I plan on adding:
57
+ Some specific things I plan on adding:
58
58
 
59
- * exec
60
- * links
61
- * support for :user and :group on MS Windows
59
+ * exec
60
+ * links
61
+ * support for :user and :group on MS Windows
62
62
 
63
63
  = Options I won't support
64
- Generally speaking, anything that would require mucking around with C code
65
- or is just too difficult to implement in a cross platform manner will not be
66
- supported. These include the following options:
64
+ Generally speaking, anything that would require mucking around with C code
65
+ or is just too difficult to implement in a cross platform manner will not be
66
+ supported. These include the following options:
67
67
 
68
- * acl/xattr - Way too difficult to implement in a cross platform manner, and
69
- a rarely used option in practice.
68
+ * acl/xattr - Way too difficult to implement in a cross platform manner, and
69
+ a rarely used option in practice.
70
70
 
71
- * cpio/ncpio - I will not shell out to this or any other 3rd party application.
71
+ * cpio/ncpio - I will not shell out to this or any other 3rd party
72
+ application.
72
73
 
73
- * ls/print - Use Ruby's builtin printing methods to print as you see fit.
74
+ * ls/print - Use Ruby's builtin printing methods to print as you see fit.
74
75
 
75
- * ok - This is not interactive software.
76
+ * ok - This is not interactive software.
76
77
 
77
78
  = Known Issues
78
- The 'perm' option does not work on MS Windows, even for its limited subset of
79
- permissions, i.e. 664 and 666. This is arguably a bug in Ruby's
80
- File::Stat.mode method on MS Windows.
79
+ The 'perm' option does not work on MS Windows, even for its limited subset of
80
+ permissions, i.e. 664 and 666. This is arguably a bug in Ruby's
81
+ File::Stat.mode method on MS Windows.
81
82
 
82
- The 'user' and 'group' options are not currently supported on MS Windows.
83
- This can be supported, but will require changes in the win32-file and
84
- win32-file-stat libraries (which would then become dependencies).
83
+ The 'user' and 'group' options are not currently supported on MS Windows.
84
+ This can be supported, but will require changes in the win32-file and
85
+ win32-file-stat libraries (which would then become dependencies).
85
86
 
86
- There are 3 test failures with JRuby, all related to the 'perm' option. I
87
- have not been able to reduce them to a simple test case and discern the
88
- exact cause of the failures, though I suspect a bug in the JRuby
89
- implementation of File.chmod.
87
+ There are 3 test failures with JRuby, all related to the 'perm' option. I
88
+ have not been able to reduce them to a simple test case and discern the
89
+ exact cause of the failures, though I suspect a bug in the JRuby
90
+ implementation of File.chmod.
90
91
 
91
92
  = Bugs
92
- None that I'm aware of. Please log any bug reports on the project page at
93
- http://www.rubyforge.org/projects/shards.
93
+ None that I'm aware of beyond the ones mentioned in the Known Issues. Please
94
+ log any bug reports on the project page at
95
+ http://www.rubyforge.org/projects/shards.
94
96
 
95
97
  = Acknowledgements
96
- * Richard Clamp's File::Find::Rule Perl module for additional ideas and
97
- inspiration.
98
- * Bill Kleb for ideas regarding name, group and perm enhancements.
99
- * Hal Fulton for his implementation of symbolic permissions.
98
+ * Richard Clamp's File::Find::Rule Perl module for additional ideas and
99
+ inspiration.
100
+ * Bill Kleb for ideas regarding name, group and perm enhancements.
101
+ * Hal Fulton for his implementation of symbolic permissions.
100
102
 
101
103
  = License
102
- Artistic 2.0
104
+ Artistic 2.0
103
105
 
104
106
  = Copyright
105
- (C) 2007-2009, Daniel J. Berger, All Rights Reserved
107
+ (C) 2007-2011, Daniel J. Berger, All Rights Reserved
106
108
 
107
109
  = Author
108
- Daniel J. Berger
110
+ Daniel J. Berger
data/Rakefile CHANGED
@@ -1,46 +1,34 @@
1
1
  require 'rake'
2
+ require 'rake/clean'
2
3
  require 'rake/testtask'
3
4
 
4
- desc "Cleanup .test-result files if present"
5
- task :clean do
6
- rm_rf '.test-result' if File.exists?('.test-result')
5
+ CLEAN.include("**/*.gem", "**/*.rbc", "**/link*")
7
6
 
8
- Dir.foreach(Dir.pwd){ |file|
9
- if File.directory?(file)
10
- Dir.chdir(file){
11
- rm_rf '.test-result' if File.exists?('.test-result')
12
- }
13
- end
14
- }
15
- end
16
-
17
- desc "Install the file-find library (non-gem)"
18
- task :install do
19
- dest = File.join(Config::CONFIG['sitelibdir'], 'file')
20
- Dir.mkdir(dest) unless File.exists? dest
21
- cp 'lib/file/find.rb', dest, :verbose => true
22
- end
23
-
24
- desc "Install the file-find library as a gem"
25
- task :install_gem do
26
- ruby 'file-find.gemspec'
27
- file = Dir["*.gem"].first
28
- sh "gem install #{file}"
29
- end
30
-
31
- desc 'Create a gem'
32
- task :gem do
33
- spec = eval(IO.read('file-find.gemspec'))
34
- if RUBY_PLATFORM.match('java')
7
+ namespace :gem do
8
+ desc 'Create the file-find gem'
9
+ task :create => [:clean] do
10
+ spec = eval(IO.read('file-find.gemspec'))
11
+ if RUBY_PLATFORM.match('java')
35
12
  spec.platform = Gem::Platform::CURRENT
36
- else
13
+ else
37
14
  spec.add_dependency('sys-admin', '>= 1.5.2')
38
- end
15
+ end
39
16
 
40
- Gem::Builder.new(spec).build
17
+ Gem::Builder.new(spec).build
18
+ end
19
+
20
+ desc "Install the file-find gem"
21
+ task :install => [:create] do
22
+ ruby 'file-find.gemspec'
23
+ file = Dir["*.gem"].first
24
+ sh "gem install #{file}"
25
+ end
41
26
  end
42
27
 
43
28
  Rake::TestTask.new do |t|
44
- t.warning = true
45
- t.verbose = true
29
+ task :test => 'clean'
30
+ t.warning = true
31
+ t.verbose = true
46
32
  end
33
+
34
+ task :default => :test
@@ -1,27 +1,26 @@
1
1
  require 'rubygems'
2
2
 
3
- Gem::Specification.new do |gem|
4
- gem.name = 'file-find'
5
- gem.version = '0.3.4'
6
- gem.author = 'Daniel Berger'
7
- gem.license = 'Artistic 2.0'
8
- gem.summary = 'A better way to find files'
9
- gem.email = 'djberg96@gmail.com'
10
- gem.homepage = 'http://www.rubyforge.org/projects/shards'
11
- gem.platform = Gem::Platform::RUBY
12
- gem.files = Dir['**/*'].reject{ |f| f.include?('CVS') }
13
- gem.test_file = 'test/test_file_find.rb'
14
- gem.has_rdoc = true
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'file-find'
5
+ spec.version = '0.3.5'
6
+ spec.author = 'Daniel Berger'
7
+ spec.license = 'Artistic 2.0'
8
+ spec.summary = 'A better way to find files'
9
+ spec.email = 'djberg96@gmail.com'
10
+ spec.homepage = 'http://www.rubyforge.org/projects/shards'
11
+ spec.platform = Gem::Platform::RUBY
12
+ spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
13
+ spec.test_file = 'test/test_file_find.rb'
15
14
 
16
- gem.rubyforge_project = 'shards'
17
- gem.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
15
+ spec.rubyforge_project = 'shards'
16
+ spec.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
18
17
 
19
- gem.add_development_dependency('test-unit', '>= 2.0.3')
18
+ spec.add_development_dependency('test-unit', '>= 2.1.1')
20
19
 
21
- gem.description = <<-EOF
22
- The file-find library provides a better, more object oriented approach
23
- to finding files. It allows you to find files based on a variety of
24
- properties, such as access time, size, owner, etc. You can also limit
25
- directory depth.
26
- EOF
20
+ spec.description = <<-EOF
21
+ The file-find library provides a better, more object oriented approach
22
+ to finding files. It allows you to find files based on a variety of
23
+ properties, such as access time, size, owner, etc. You can also limit
24
+ directory depth.
25
+ EOF
27
26
  end
@@ -4,501 +4,501 @@ require 'rbconfig'
4
4
  # For alternate implementations of Ruby, such as JRuby, that cannot
5
5
  # build C extensions fall back to the Etc module.
6
6
  begin
7
- require 'sys/admin'
7
+ require 'sys/admin'
8
8
  rescue LoadError
9
- require 'etc'
9
+ require 'etc'
10
10
  end
11
11
 
12
12
  class File::Find
13
- # The version of the file-find library
14
- VERSION = '0.3.4'
15
-
16
- # :stopdoc:
17
- VALID_OPTIONS = %w/
18
- atime
19
- ctime
20
- follow
21
- ftype
22
- inum
23
- group
24
- links
25
- maxdepth
26
- mindepth
27
- mount
28
- mtime
29
- name
30
- pattern
31
- path
32
- perm
33
- prune
34
- size
35
- user
36
- /
37
- # :startdoc:
38
-
39
- # The starting path(s) for the search. The default is the current directory.
40
- # This can be a single path or an array of paths.
41
- #
42
- attr_accessor :path
43
-
44
- # The list of options passed to the constructor and/or used by the
45
- # File::Find#find method.
46
- #
47
- attr_accessor :options
48
-
49
- # Limits searches by file access time, where the value you supply is the
50
- # number of days back from the time that the File::Find#find method was
51
- # called.
52
- #
53
- attr_accessor :atime
54
-
55
- # Limits searches by file change time, where the value you supply is the
56
- # number of days back from the time that the File::Find#find method was
57
- # called.
58
- #
59
- attr_accessor :ctime
60
-
61
- # Limits searches to files that belong to a specific group, where the
62
- # group can be either a group name or ID.
63
- #
64
- # Not currently supported on MS Windows.
65
- #
66
- attr_accessor :group
67
-
68
- # An array of two element arrays for storing FileTest methods and their
69
- # boolean value.
70
- #
71
- attr_accessor :filetest
72
-
73
- # Controls the behavior of how symlinks are followed. If set to true (the
74
- # default), then follows the file pointed to. If false, it considers the
75
- # symlink itself.
76
- #
77
- attr_accessor :follow
78
-
79
- # Limits searches to specific types of files. The possible values here are
80
- # those returned by the File.ftype method.
81
- #
82
- attr_accessor :ftype
83
-
84
- # Limits search to a file with a specific inode number. Ignored on MS
85
- # Windows.
86
- #
87
- attr_accessor :inum
88
-
89
- # Limits search to files with the specified number of links.
90
- #
91
- attr_accessor :links
92
-
93
- # Limits search to a maximum depth into the tree relative to the starting
94
- # search directory.
95
- #
96
- attr_accessor :maxdepth
97
-
98
- # Limits searches to a minimum depth into the tree relative to the starting
99
- # search directory.
100
- #
101
- attr_accessor :mindepth
102
-
103
- # Limits searches to the same filesystem as the specified directory. For
104
- # Windows users, this refers to the volume.
105
- #
106
- attr_reader :mount
107
-
108
- # Limits searches by file modification time, where the value you supply is
109
- # the number of days back from the time that the File::Find#find method was
110
- # called.
111
- #
112
- attr_accessor :mtime
113
-
114
- # The name pattern used to limit file searches. The patterns that are legal
115
- # for Dir.glob are legal here. The default is '*', i.e. everything.
116
- #
117
- attr_accessor :name
118
-
119
- # Limits searches to files which have permissions that match the octal
120
- # value that you provide. For purposes of this comparison, only the user,
121
- # group, and world settings are used. Do not use a leading 0 in the values
122
- # that you supply, e.g. use 755 not 0755.
123
- #
124
- # You may optionally use symbolic permissions, e.g. "g+rw", "u=rwx", etc.
125
- #
126
- # Not currently supported on MS Windows.
127
- #
128
- attr_accessor :perm
129
-
130
- # Skips files or directories that match the string provided as an argument.
131
- #
132
- attr_accessor :prune
133
-
134
- # If the value passed is an integer, this option limits searches to files
135
- # that match the size, in bytes, exactly. If a string is passed, you can
136
- # use the standard comparable operators to match files, e.g. ">= 200" would
137
- # limit searches to files greater than or equal to 200 bytes.
138
- #
139
- attr_accessor :size
140
-
141
- # Limits searches to files that belong to a specific user, where the user
142
- # can be either a user name or an ID.
143
- #
144
- # Not currently supported on MS Windows.
145
- #
146
- attr_accessor :user
147
-
148
- # The file that matched previously in the current search.
149
- #
150
- attr_reader :previous
151
-
152
- alias pattern name
153
- alias pattern= name=
154
-
155
- # Creates and returns a new File::Find object. The options set for this
156
- # object serve as the rules for determining what files the File::Find#find
157
- # method will search for.
158
- #
159
- # In addition to the standard list of valid options, you may also use
160
- # FileTest methods as options, setting their value to true or false.
161
- #
162
- # Example:
163
- #
164
- # rule = File::Find.new(
165
- # :name => "*.rb",
166
- # :follow => false,
167
- # :path => ['/usr/local/lib', '/opt/local/lib'],
168
- # :readable? => true
169
- # )
170
- #
171
- def initialize(options = {})
172
- @options = options
173
-
174
- @atime = nil
175
- @ctime = nil
176
- @ftype = nil
177
- @group = nil
178
- @follow = true
179
- @inum = nil
180
- @links = nil
181
- @mount = nil
182
- @mtime = nil
183
- @perm = nil
184
- @prune = nil
185
- @size = nil
186
- @user = nil
187
-
188
- @previous = nil
189
- @maxdepth = nil
190
- @mindepth = nil
191
- @filetest = []
192
-
193
- validate_and_set_options(options) unless options.empty?
194
-
195
- @filesystem = File.stat(@mount).dev if @mount
196
-
197
- @path ||= Dir.pwd
198
- @name ||= '*'
199
- end
200
-
201
- # Executes the find based on the rules you set for the File::Find object.
202
- # In block form, yields each file in turn that matches the specified rules.
203
- # In non-block form it will return an array of matches instead.
204
- #
205
- # Example:
206
- #
207
- # rule = File::Find.new(
208
- # :name => "*.rb",
209
- # :follow => false,
210
- # :path => ['/usr/local/lib', '/opt/local/lib']
211
- # )
212
- #
213
- # rule.find{ |f|
214
- # puts f
215
- # }
216
- #
217
- def find
218
- results = [] unless block_given?
219
- paths = @path.is_a?(String) ? [@path] : @path # Ruby 1.9.x compatibility
220
-
221
- if @prune
222
- prune_regex = Regexp.new(@prune)
223
- else
224
- prune_regex = nil
225
- end
13
+ # The version of the file-find library
14
+ VERSION = '0.3.5'
15
+
16
+ # :stopdoc:
17
+ VALID_OPTIONS = %w/
18
+ atime
19
+ ctime
20
+ follow
21
+ ftype
22
+ inum
23
+ group
24
+ links
25
+ maxdepth
26
+ mindepth
27
+ mount
28
+ mtime
29
+ name
30
+ pattern
31
+ path
32
+ perm
33
+ prune
34
+ size
35
+ user
36
+ /
37
+ # :startdoc:
38
+
39
+ # The starting path(s) for the search. The default is the current directory.
40
+ # This can be a single path or an array of paths.
41
+ #
42
+ attr_accessor :path
43
+
44
+ # The list of options passed to the constructor and/or used by the
45
+ # File::Find#find method.
46
+ #
47
+ attr_accessor :options
48
+
49
+ # Limits searches by file access time, where the value you supply is the
50
+ # number of days back from the time that the File::Find#find method was
51
+ # called.
52
+ #
53
+ attr_accessor :atime
54
+
55
+ # Limits searches by file change time, where the value you supply is the
56
+ # number of days back from the time that the File::Find#find method was
57
+ # called.
58
+ #
59
+ attr_accessor :ctime
60
+
61
+ # Limits searches to files that belong to a specific group, where the
62
+ # group can be either a group name or ID.
63
+ #
64
+ # Not currently supported on MS Windows.
65
+ #
66
+ attr_accessor :group
67
+
68
+ # An array of two element arrays for storing FileTest methods and their
69
+ # boolean value.
70
+ #
71
+ attr_accessor :filetest
72
+
73
+ # Controls the behavior of how symlinks are followed. If set to true (the
74
+ # default), then follows the file pointed to. If false, it considers the
75
+ # symlink itself.
76
+ #
77
+ attr_accessor :follow
78
+
79
+ # Limits searches to specific types of files. The possible values here are
80
+ # those returned by the File.ftype method.
81
+ #
82
+ attr_accessor :ftype
83
+
84
+ # Limits search to a file with a specific inode number. Ignored on MS
85
+ # Windows.
86
+ #
87
+ attr_accessor :inum
88
+
89
+ # Limits search to files with the specified number of links.
90
+ #
91
+ attr_accessor :links
92
+
93
+ # Limits search to a maximum depth into the tree relative to the starting
94
+ # search directory.
95
+ #
96
+ attr_accessor :maxdepth
97
+
98
+ # Limits searches to a minimum depth into the tree relative to the starting
99
+ # search directory.
100
+ #
101
+ attr_accessor :mindepth
102
+
103
+ # Limits searches to the same filesystem as the specified directory. For
104
+ # Windows users, this refers to the volume.
105
+ #
106
+ attr_reader :mount
107
+
108
+ # Limits searches by file modification time, where the value you supply is
109
+ # the number of days back from the time that the File::Find#find method was
110
+ # called.
111
+ #
112
+ attr_accessor :mtime
113
+
114
+ # The name pattern used to limit file searches. The patterns that are legal
115
+ # for Dir.glob are legal here. The default is '*', i.e. everything.
116
+ #
117
+ attr_accessor :name
118
+
119
+ # Limits searches to files which have permissions that match the octal
120
+ # value that you provide. For purposes of this comparison, only the user,
121
+ # group, and world settings are used. Do not use a leading 0 in the values
122
+ # that you supply, e.g. use 755 not 0755.
123
+ #
124
+ # You may optionally use symbolic permissions, e.g. "g+rw", "u=rwx", etc.
125
+ #
126
+ # Not currently supported on MS Windows.
127
+ #
128
+ attr_accessor :perm
129
+
130
+ # Skips files or directories that match the string provided as an argument.
131
+ #
132
+ attr_accessor :prune
133
+
134
+ # If the value passed is an integer, this option limits searches to files
135
+ # that match the size, in bytes, exactly. If a string is passed, you can
136
+ # use the standard comparable operators to match files, e.g. ">= 200" would
137
+ # limit searches to files greater than or equal to 200 bytes.
138
+ #
139
+ attr_accessor :size
140
+
141
+ # Limits searches to files that belong to a specific user, where the user
142
+ # can be either a user name or an ID.
143
+ #
144
+ # Not currently supported on MS Windows.
145
+ #
146
+ attr_accessor :user
147
+
148
+ # The file that matched previously in the current search.
149
+ #
150
+ attr_reader :previous
151
+
152
+ alias pattern name
153
+ alias pattern= name=
154
+
155
+ # Creates and returns a new File::Find object. The options set for this
156
+ # object serve as the rules for determining what files the File::Find#find
157
+ # method will search for.
158
+ #
159
+ # In addition to the standard list of valid options, you may also use
160
+ # FileTest methods as options, setting their value to true or false.
161
+ #
162
+ # Example:
163
+ #
164
+ # rule = File::Find.new(
165
+ # :name => "*.rb",
166
+ # :follow => false,
167
+ # :path => ['/usr/local/lib', '/opt/local/lib'],
168
+ # :readable? => true
169
+ # )
170
+ #
171
+ def initialize(options = {})
172
+ @options = options
173
+
174
+ @atime = nil
175
+ @ctime = nil
176
+ @ftype = nil
177
+ @group = nil
178
+ @follow = true
179
+ @inum = nil
180
+ @links = nil
181
+ @mount = nil
182
+ @mtime = nil
183
+ @perm = nil
184
+ @prune = nil
185
+ @size = nil
186
+ @user = nil
187
+
188
+ @previous = nil
189
+ @maxdepth = nil
190
+ @mindepth = nil
191
+ @filetest = []
192
+
193
+ validate_and_set_options(options) unless options.empty?
194
+
195
+ @filesystem = File.stat(@mount).dev if @mount
196
+
197
+ @path ||= Dir.pwd
198
+ @name ||= '*'
199
+ end
200
+
201
+ # Executes the find based on the rules you set for the File::Find object.
202
+ # In block form, yields each file in turn that matches the specified rules.
203
+ # In non-block form it will return an array of matches instead.
204
+ #
205
+ # Example:
206
+ #
207
+ # rule = File::Find.new(
208
+ # :name => "*.rb",
209
+ # :follow => false,
210
+ # :path => ['/usr/local/lib', '/opt/local/lib']
211
+ # )
212
+ #
213
+ # rule.find{ |f|
214
+ # puts f
215
+ # }
216
+ #
217
+ def find
218
+ results = [] unless block_given?
219
+ paths = @path.is_a?(String) ? [@path] : @path # Ruby 1.9.x compatibility
220
+
221
+ if @prune
222
+ prune_regex = Regexp.new(@prune)
223
+ else
224
+ prune_regex = nil
225
+ end
226
+
227
+ paths.each{ |path|
228
+ begin
229
+ Dir.foreach(path){ |file|
230
+ next if file == '.'
231
+ next if file == '..'
232
+
233
+ if prune_regex
234
+ next if prune_regex.match(file)
235
+ end
236
+
237
+ orig = file.dup
238
+ file = File.join(path, file)
239
+
240
+ stat_method = @follow ? :stat : :lstat
241
+
242
+ # Skip files we cannot access, stale links, etc.
243
+ begin
244
+ stat_info = File.send(stat_method, file)
245
+ rescue Errno::ENOENT, Errno::EACCES
246
+ next
247
+ rescue Errno::ELOOP
248
+ stat_method = :lstat # Handle recursive symlinks
249
+ retry if stat_method.to_s != 'lstat'
250
+ end
251
+
252
+ glob = File.join(File.dirname(file), @name)
253
+
254
+ # Dir[] doesn't like backslashes
255
+ if File::ALT_SEPARATOR
256
+ file.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
257
+ glob.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
258
+ end
259
+
260
+ if @mount
261
+ next unless stat_info.dev == @filesystem
262
+ end
263
+
264
+ if @links
265
+ next unless stat_info.nlink == @links
266
+ end
267
+
268
+ if @maxdepth || @mindepth
269
+ file_depth = file.split(File::SEPARATOR).length
270
+ path_depth = @path.split(File::SEPARATOR).length
271
+ depth = file_depth - path_depth
272
+
273
+ if @maxdepth && (depth > @maxdepth)
274
+ if File.directory?(file)
275
+ unless paths.include?(file) && depth > @maxdepth
276
+ paths << file
277
+ end
278
+ end
279
+
280
+ next
281
+ end
282
+
283
+ if @mindepth && (depth < @mindepth)
284
+ if File.directory?(file)
285
+ unless paths.include?(file) && depth < @mindepth
286
+ paths << file
287
+ end
288
+ end
289
+
290
+ next
291
+ end
292
+ end
293
+
294
+ # Add directories back onto the list of paths to search unless
295
+ # they've already been added
296
+ #
297
+ if stat_info.directory?
298
+ paths << file unless paths.include?(file)
299
+ end
300
+
301
+ next unless Dir[glob].include?(file)
302
+
303
+ unless @filetest.empty?
304
+ file_test = true
226
305
 
227
- paths.each{ |path|
228
- begin
229
- Dir.foreach(path){ |file|
230
- next if file == '.'
231
- next if file == '..'
232
-
233
- if prune_regex
234
- next if prune_regex.match(file)
235
- end
236
-
237
- orig = file.dup
238
- file = File.join(path, file)
239
-
240
- stat_method = @follow ? :lstat : :stat
241
-
242
- # Skip files we cannot access, stale links, etc.
243
- begin
244
- stat_info = File.send(stat_method, file)
245
- rescue Errno::ENOENT, Errno::EACCES
246
- next
247
- rescue Errno::ELOOP
248
- stat_method = :lstat # Handle recursive symlinks
249
- retry if stat_method.to_s != 'lstat'
250
- end
251
-
252
- glob = File.join(File.dirname(file), @name)
253
-
254
- # Dir[] doesn't like backslashes
255
- if File::ALT_SEPARATOR
256
- file.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
257
- glob.tr!(File::ALT_SEPARATOR, File::SEPARATOR)
258
- end
259
-
260
- if @mount
261
- next unless stat_info.dev == @filesystem
262
- end
263
-
264
- if @links
265
- next unless stat_info.nlink == @links
266
- end
267
-
268
- if @maxdepth || @mindepth
269
- file_depth = file.split(File::SEPARATOR).length
270
- path_depth = @path.split(File::SEPARATOR).length
271
- depth = file_depth - path_depth
272
-
273
- if @maxdepth && (depth > @maxdepth)
274
- if File.directory?(file)
275
- unless paths.include?(file) && depth > @maxdepth
276
- paths << file
277
- end
278
- end
279
-
280
- next
281
- end
282
-
283
- if @mindepth && (depth < @mindepth)
284
- if File.directory?(file)
285
- unless paths.include?(file) && depth < @mindepth
286
- paths << file
287
- end
288
- end
289
-
290
- next
291
- end
292
- end
293
-
294
- # Add directories back onto the list of paths to search unless
295
- # they've already been added
296
- #
297
- if stat_info.directory?
298
- paths << file unless paths.include?(file)
299
- end
300
-
301
- next unless Dir[glob].include?(file)
302
-
303
- unless @filetest.empty?
304
- file_test = true
305
-
306
- @filetest.each{ |array|
307
- meth = array[0]
308
- bool = array[1]
309
-
310
- unless File.send(meth, file) == bool
311
- file_test = false
312
- break
313
- end
314
- }
315
-
316
- next unless file_test
317
- end
318
-
319
- if @atime || @ctime || @mtime
320
- date1 = Date.parse(Time.now.to_s)
321
-
322
- if @atime
323
- date2 = Date.parse(stat_info.atime.to_s)
324
- next unless (date1 - date2).numerator == @atime
325
- end
326
-
327
- if @ctime
328
- date2 = Date.parse(stat_info.ctime.to_s)
329
- next unless (date1 - date2).numerator == @ctime
330
- end
331
-
332
- if @mtime
333
- date2 = Date.parse(stat_info.mtime.to_s)
334
- next unless (date1 - date2).numerator == @mtime
335
- end
336
- end
337
-
338
- if @ftype
339
- next unless File.ftype(file) == @ftype
340
- end
341
-
342
- if @group
343
- if @group.is_a?(String)
344
- next unless get_group(stat_info.gid).name == @group
345
- else
346
- next unless stat_info.gid == @group
347
- end
348
- end
349
-
350
- unless Config::CONFIG['host_os'] =~ /windows|mswin/i
351
- if @inum
352
- next unless stat_info.ino == @inum
353
- end
354
- end
355
-
356
- # This currently doesn't work on MS Windows, even in limited
357
- # fashion for 0666 and 0664, because File.stat.mode doesn't
358
- # return the proper value.
359
- #
360
- if @perm
361
- if @perm.is_a?(String)
362
- octal_perm = sym2oct(@perm)
363
- next unless stat_info.mode & octal_perm == octal_perm
364
- else
365
- next unless sprintf("%o", stat_info.mode & 07777) == @perm.to_s
366
- end
367
- end
368
-
369
- # Allow plain numbers, or strings for comparison operators.
370
- if @size
371
- if @size.is_a?(String)
372
- regex = /^([><=]+)\s*?(\d+)$/
373
- match = regex.match(@size)
374
-
375
- if match.nil? || match.captures.include?(nil)
376
- raise ArgumentError, "invalid size string: '#{@size}'"
377
- end
378
-
379
- operator = match.captures.first.strip
380
- number = match.captures.last.strip.to_i
381
-
382
- next unless stat_info.size.send(operator, number)
383
- else
384
- next unless stat_info.size == @size
385
- end
386
- end
387
-
388
- if @user
389
- if @user.is_a?(String)
390
- next unless get_user(stat_info.uid).name == @user
391
- else
392
- next unless stat_info.uid == @user
393
- end
394
- end
395
-
396
- if block_given?
397
- yield file
398
- else
399
- results << file
400
- end
401
-
402
- @previous = file unless @previous == file
306
+ @filetest.each{ |array|
307
+ meth = array[0]
308
+ bool = array[1]
309
+
310
+ unless File.send(meth, file) == bool
311
+ file_test = false
312
+ break
313
+ end
403
314
  }
404
- rescue Errno::EACCES
405
- next # Skip inaccessible directories
406
- end
407
- }
408
-
409
- block_given? ? nil : results
410
- end
411
-
412
- # Limits searches to the same file system as the specified +mount_point+.
413
- #
414
- def mount=(mount_point)
415
- @mount = mount_point
416
- @filesystem = File.stat(mount_point).dev
417
- end
418
-
419
- private
420
-
421
- # This validates that the keys are valid. If they are, it sets the value
422
- # of that key's corresponding method to the given value. If a key ends
423
- # with a '?', it's validated as a File method.
424
- #
425
- def validate_and_set_options(options)
426
- options.each do |key, value|
427
- key = key.to_s.downcase
428
-
429
- if key[-1].chr == '?'
430
- sym = key.to_sym
431
-
432
- unless File.respond_to?(sym)
433
- raise ArgumentError, "invalid option '#{key}'"
315
+
316
+ next unless file_test
317
+ end
318
+
319
+ if @atime || @ctime || @mtime
320
+ date1 = Date.parse(Time.now.to_s)
321
+
322
+ if @atime
323
+ date2 = Date.parse(stat_info.atime.to_s)
324
+ next unless (date1 - date2).numerator == @atime
434
325
  end
435
326
 
436
- @filetest << [sym, value]
437
- else
438
- unless VALID_OPTIONS.include?(key)
439
- raise ArgumentError, "invalid option '#{key}'"
327
+ if @ctime
328
+ date2 = Date.parse(stat_info.ctime.to_s)
329
+ next unless (date1 - date2).numerator == @ctime
440
330
  end
441
331
 
442
- send("#{key}=", value)
443
- end
444
- end
445
- end
446
-
447
- # Converts a symoblic permissions mode into its octal equivalent.
448
- #--
449
- # Taken almost entirely from ruby-talk: 96956 (Hal Fulton).
450
- #
451
- def sym2oct(str)
452
- left = {'u' => 0700, 'g' => 0070, 'o' => 0007, 'a' => 0777}
453
- right = {'r' => 0444, 'w' => 0222, 'x' => 0111}
454
- regex = /([ugoa]+)([+-=])([rwx]+)/
455
-
456
- cmds = str.split(',')
457
-
458
- perm = 0
459
-
460
- cmds.each do |cmd|
461
- match = cmd.match(regex)
462
- raise "Invalid symbolic permissions: '#{str}'" if match.nil?
463
-
464
- junk, who, what, how = match.to_a
465
-
466
- who = who.split(//).inject(who_num=0){ |num,b| num |= left[b]; num }
467
- how = how.split(//).inject(how_num=0){ |num,b| num |= right[b]; num }
468
- mask = who & how
469
-
470
- case what
471
- when '+'
472
- perm = perm | mask
473
- when '-'
474
- perm = perm & ~mask
475
- when '='
476
- perm = mask
477
- end
332
+ if @mtime
333
+ date2 = Date.parse(stat_info.mtime.to_s)
334
+ next unless (date1 - date2).numerator == @mtime
335
+ end
336
+ end
337
+
338
+ if @ftype
339
+ next unless File.ftype(file) == @ftype
340
+ end
341
+
342
+ if @group
343
+ if @group.is_a?(String)
344
+ next unless get_group(stat_info.gid).name == @group
345
+ else
346
+ next unless stat_info.gid == @group
347
+ end
348
+ end
349
+
350
+ unless Config::CONFIG['host_os'] =~ /windows|mswin/i
351
+ if @inum
352
+ next unless stat_info.ino == @inum
353
+ end
354
+ end
355
+
356
+ # This currently doesn't work on MS Windows, even in limited
357
+ # fashion for 0666 and 0664, because File.stat.mode doesn't
358
+ # return the proper value.
359
+ #
360
+ if @perm
361
+ if @perm.is_a?(String)
362
+ octal_perm = sym2oct(@perm)
363
+ next unless stat_info.mode & octal_perm == octal_perm
364
+ else
365
+ next unless sprintf("%o", stat_info.mode & 07777) == @perm.to_s
366
+ end
367
+ end
368
+
369
+ # Allow plain numbers, or strings for comparison operators.
370
+ if @size
371
+ if @size.is_a?(String)
372
+ regex = /^([><=]+)\s*?(\d+)$/
373
+ match = regex.match(@size)
374
+
375
+ if match.nil? || match.captures.include?(nil)
376
+ raise ArgumentError, "invalid size string: '#{@size}'"
377
+ end
378
+
379
+ operator = match.captures.first.strip
380
+ number = match.captures.last.strip.to_i
381
+
382
+ next unless stat_info.size.send(operator, number)
383
+ else
384
+ next unless stat_info.size == @size
385
+ end
386
+ end
387
+
388
+ if @user
389
+ if @user.is_a?(String)
390
+ next unless get_user(stat_info.uid).name == @user
391
+ else
392
+ next unless stat_info.uid == @user
393
+ end
394
+ end
395
+
396
+ if block_given?
397
+ yield file
398
+ else
399
+ results << file
400
+ end
401
+
402
+ @previous = file unless @previous == file
403
+ }
404
+ rescue Errno::EACCES
405
+ next # Skip inaccessible directories
478
406
  end
407
+ }
408
+
409
+ block_given? ? nil : results
410
+ end
411
+
412
+ # Limits searches to the same file system as the specified +mount_point+.
413
+ #
414
+ def mount=(mount_point)
415
+ @mount = mount_point
416
+ @filesystem = File.stat(mount_point).dev
417
+ end
418
+
419
+ private
479
420
 
480
- perm
481
- end
421
+ # This validates that the keys are valid. If they are, it sets the value
422
+ # of that key's corresponding method to the given value. If a key ends
423
+ # with a '?', it's validated as a File method.
424
+ #
425
+ def validate_and_set_options(options)
426
+ options.each do |key, value|
427
+ key = key.to_s.downcase
482
428
 
483
- # Returns the group object based on the group id. Implemented for the
484
- # sake of platforms that cannot build extensions, such as JRuby.
485
- #
486
- def get_group(gid)
487
- if defined? Sys::Admin
488
- Sys::Admin.get_group(gid)
429
+ if key[-1].chr == '?'
430
+ sym = key.to_sym
431
+
432
+ unless File.respond_to?(sym)
433
+ raise ArgumentError, "invalid option '#{key}'"
434
+ end
435
+
436
+ @filetest << [sym, value]
489
437
  else
490
- Etc.getgrgid(gid)
438
+ unless VALID_OPTIONS.include?(key)
439
+ raise ArgumentError, "invalid option '#{key}'"
440
+ end
441
+
442
+ send("#{key}=", value)
491
443
  end
492
- end
493
-
494
- # Returns the user object based on the group id. Implemented for the
495
- # sake of platforms that cannot build extensions, such as JRuby.
496
- #
497
- def get_user(uid)
498
- if defined? Sys::Admin
499
- Sys::Admin.get_user(uid)
500
- else
501
- Etc.getpwuid(uid)
444
+ end
445
+ end
446
+
447
+ # Converts a symoblic permissions mode into its octal equivalent.
448
+ #--
449
+ # Taken almost entirely from ruby-talk: 96956 (Hal Fulton).
450
+ #
451
+ def sym2oct(str)
452
+ left = {'u' => 0700, 'g' => 0070, 'o' => 0007, 'a' => 0777}
453
+ right = {'r' => 0444, 'w' => 0222, 'x' => 0111}
454
+ regex = /([ugoa]+)([+-=])([rwx]+)/
455
+
456
+ cmds = str.split(',')
457
+
458
+ perm = 0
459
+
460
+ cmds.each do |cmd|
461
+ match = cmd.match(regex)
462
+ raise "Invalid symbolic permissions: '#{str}'" if match.nil?
463
+
464
+ junk, who, what, how = match.to_a
465
+
466
+ who = who.split(//).inject(who_num=0){ |num,b| num |= left[b]; num }
467
+ how = how.split(//).inject(how_num=0){ |num,b| num |= right[b]; num }
468
+ mask = who & how
469
+
470
+ case what
471
+ when '+'
472
+ perm = perm | mask
473
+ when '-'
474
+ perm = perm & ~mask
475
+ when '='
476
+ perm = mask
502
477
  end
503
- end
478
+ end
479
+
480
+ perm
481
+ end
482
+
483
+ # Returns the group object based on the group id. Implemented for the
484
+ # sake of platforms that cannot build extensions, such as JRuby.
485
+ #
486
+ def get_group(gid)
487
+ if defined? Sys::Admin
488
+ Sys::Admin.get_group(gid)
489
+ else
490
+ Etc.getgrgid(gid)
491
+ end
492
+ end
493
+
494
+ # Returns the user object based on the group id. Implemented for the
495
+ # sake of platforms that cannot build extensions, such as JRuby.
496
+ #
497
+ def get_user(uid)
498
+ if defined? Sys::Admin
499
+ Sys::Admin.get_user(uid)
500
+ else
501
+ Etc.getpwuid(uid)
502
+ end
503
+ end
504
504
  end