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.
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
+