file-find 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/CHANGES +2 -0
  2. data/MANIFEST +7 -0
  3. data/README +64 -0
  4. data/lib/file/find.rb +206 -0
  5. data/test/tc_find.rb +125 -0
  6. metadata +53 -0
data/CHANGES ADDED
@@ -0,0 +1,2 @@
1
+ == 0.1.0 - 24-Apr-2007
2
+ * Initial release
@@ -0,0 +1,7 @@
1
+ * CHANGES
2
+ * MANIFEST
3
+ * Rakefile
4
+ * README
5
+ * file-find.gemspec
6
+ * lib/file/find.rb
7
+ * test/tc_find.rb
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
@@ -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
@@ -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
+