ptools 1.1.7 → 1.1.9

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,23 @@
1
+ == 1.1.9 - 25-Mar-2010
2
+ * Refactored File.which and File.whereis and added additional tests for each.
3
+ * Removed the block form of File.whereis.
4
+ * Reorganized the Rakefile a bit and put the test tasks under the 'test'
5
+ namespace, and the gem tasks under the 'gem' namespace.
6
+ * Updated the test-unit development dependency.
7
+ * Updates the README.
8
+
9
+ == 1.1.8 - 6-Oct-2009
10
+ * Fixed some bad variable names that crept into the File.nl_convert method.
11
+ * Added the File.null_device alias for File.null.
12
+ * Added cygwin and mingw to Windows detection code.
13
+ * One of the nlconvert tests is now skipped on Windows.
14
+ * Two text files that were previously bundled as part of this library for
15
+ testing purposes have been removed. Those are now dynamically generated
16
+ within the tests themselves.
17
+ * Gemspec updates.
18
+ * Documentation updates.
19
+ * Added the :gem rake task.
20
+
1
21
  == 1.1.7 - 28-Jul-2009
2
22
  * Now compatible with Ruby 1.9.x.
3
23
  * Replaced RUBY_PLATFORM with rbconfig check for the sake of other
data/MANIFEST CHANGED
@@ -15,5 +15,3 @@
15
15
  * test/test_wc.rb
16
16
  * test/test_which.rb
17
17
  * test/test_whereis.rb
18
- * test/test_file.txt
19
- * test/test_file2.txt
data/README CHANGED
@@ -1,76 +1,64 @@
1
1
  == Description
2
- The ptools (power tools) library is an additional set of commands for the
3
- File class based on Unix command line tools.
2
+ The ptools (power tools) library is an additional set of commands for the
3
+ File class based on Unix command line tools.
4
4
 
5
5
  == Prerequisites
6
- Ruby 1.8.0 or later is recommended.
7
- On MS Windows you will need win32-file 0.5.4 or later.
6
+ On MS Windows you will need win32-file 0.5.4 or later.
8
7
 
9
8
  == Installation
10
- === Gem
11
- ruby ptools.gemspec
12
- gem install ptools-x.y.z.gem
13
- === Manual
14
- rake test (optional)
15
- rake install
9
+ gem install ptools
16
10
 
17
11
  == Synopsis
18
- require "ptools"
12
+ require "ptools"
19
13
 
20
- File.which("ruby") # '/usr/local/bin/ruby'
21
- File.whereis("ruby") # ['/usr/local/bin/ruby','/opt/bin/ruby']
14
+ File.which("ruby") # '/usr/local/bin/ruby'
15
+ File.whereis("ruby") # ['/usr/local/bin/ruby','/opt/bin/ruby']
22
16
 
23
- File.head("myfile") # Returns first 10 lines of 'myfile'
24
- File.middle("myfile",8,12) # Returns lines 8-12 of 'myfile'
25
- File.tail("myfile",3) # Returns last 3 lines of 'myfile'
26
- File.wc("myfile",'words') # Returns the number of words in 'myfile'
17
+ File.head("myfile") # Returns first 10 lines of 'myfile'
18
+ File.middle("myfile",8,12) # Returns lines 8-12 of 'myfile'
19
+ File.tail("myfile",3) # Returns last 3 lines of 'myfile'
20
+ File.wc("myfile",'words') # Returns the number of words in 'myfile'
27
21
 
28
- File.touch("newfile") # "newfile" now exists
29
- File.null # '/dev/null' on Unix, 'NUL' on Windows
30
- File.binary?('some_file') # true or false
22
+ File.touch("newfile") # "newfile" now exists
23
+ File.null # '/dev/null' on Unix, 'NUL' on Windows
24
+ File.binary?('some_file') # true or false
31
25
 
32
- # Creates a copy of 'myfile' called 'newfile', in DOS format
33
- File.nl_convert("myfile", "newfile", "dos")
26
+ # Creates a copy of 'myfile' called 'newfile', in DOS format
27
+ File.nl_convert("myfile", "newfile", "dos")
34
28
 
35
29
  == Known Bugs
36
- There is a bug in 1.6.x that can cause $\ characters to accumulate when
37
- converting to DOS or MAC format if nl_convert is run multiple times on
38
- the same file. This appears to be fixed in 1.8.x. You're not still running
39
- Ruby 1.6.x are you? I would hope not.
30
+ None known. Please report any bugs on the github or RubyForge project page.
40
31
 
41
- One or two of The File.nl_convert tests may fail on Windows. You can
42
- ignore these.
32
+ http://www.rubyforge.org/projects/shards
33
+ http://www.github.com/djberg96/ptools
43
34
 
44
35
  == Acknowledgements
45
- The which() method was adopted from the FileWhich code posted by Michael
46
- Granger on http://www.rubygarden.org. The 'whereis' command is a minor
47
- modification of that code as well.
36
+ The File.which method was originally adopted from the FileWhich code posted
37
+ by Michael Granger on the now defunct rubygarden.org website. That code was
38
+ later replaced by a version based on the ruby-which library.
48
39
 
49
- The nl_convert() method was adopted (somewhat) from the nlcvt program
50
- found at http://www.perl.com/language/ppt/src/nlcvt/nlcvt, written by
51
- Tom Christiansen.
40
+ The File.nl_convert method is based on the nlcvt program found at
41
+ http://www.perl.com/language/ppt/src/nlcvt/nlcvt, written by Tom Christiansen.
52
42
 
53
- The middle() method was provided by Shashank Date.
43
+ The middle() method was provided by Shashank Date.
54
44
 
55
- The binary?() method was based almost entirely on a blog post by Ryan
56
- Davis (who, in turn, based his code on Perl's -B switch).
45
+ The binary?() method was based almost entirely on a blog post by Ryan
46
+ Davis (who, in turn, based his code on Perl's -B switch).
57
47
 
58
48
  == Future Plans
59
- Add whatever other tools people think might be useful.
49
+ Add whatever other tools people think might be useful.
60
50
 
61
51
  == License
62
- Artistic 2.0
52
+ Artistic 2.0
63
53
 
64
54
  == Copyright
65
- (C) 2003-2009 Daniel J. Berger
66
- All Rights Reserved.
55
+ (C) 2003-2010 Daniel J. Berger
56
+ All Rights Reserved.
67
57
 
68
58
  == Warranty
69
- This package is provided "as is" and without any express or
70
- implied warranties, including, without limitation, the implied
71
- warranties of merchantability and fitness for a particular purpose.
59
+ This package is provided "as is" and without any express or
60
+ implied warranties, including, without limitation, the implied
61
+ warranties of merchantability and fitness for a particular purpose.
72
62
 
73
63
  == Author
74
- Daniel J. Berger
75
- djberg96 at nospam at gmail dot com
76
- imperator on IRC (irc.freenode.net)
64
+ Daniel J. Berger
data/Rakefile ADDED
@@ -0,0 +1,110 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rbconfig'
4
+ include Config
5
+
6
+ desc 'Install the ptools package (non-gem)'
7
+ task :install do
8
+ sitelibdir = CONFIG["sitelibdir"]
9
+ file = "lib/ptools.rb"
10
+ FileUtils.cp(file, sitelibdir, :verbose => true)
11
+ end
12
+
13
+ namespace 'gem' do
14
+ desc 'Create the ptools gem'
15
+ task :create do
16
+ Dir["*.gem"].each{ |f| File.delete(f) } # Clean first
17
+ spec = eval(IO.read('ptools.gemspec'))
18
+ Gem::Builder.new(spec).build
19
+ end
20
+
21
+ desc 'Install the ptools gem'
22
+ task :install => [:create] do
23
+ file = Dir["*.gem"].first
24
+ sh "gem install #{file}"
25
+ end
26
+ end
27
+
28
+ Rake::TestTask.new do |t|
29
+ t.verbose = true
30
+ t.warning = true
31
+ end
32
+
33
+ namespace 'test' do
34
+ Rake::TestTask.new('binary') do |t|
35
+ t.libs << 'test'
36
+ t.verbose = true
37
+ t.warning = true
38
+ t.test_files = FileList['test/test_binary.rb']
39
+ end
40
+
41
+ Rake::TestTask.new('constants') do |t|
42
+ t.libs << 'test'
43
+ t.verbose = true
44
+ t.warning = true
45
+ t.test_files = FileList['test/test_constants.rb']
46
+ end
47
+
48
+ Rake::TestTask.new('head') do |t|
49
+ t.libs << 'test'
50
+ t.verbose = true
51
+ t.warning = true
52
+ t.test_files = FileList['test/test_head.rb']
53
+ end
54
+
55
+ Rake::TestTask.new('middle') do |t|
56
+ t.libs << 'test'
57
+ t.verbose = true
58
+ t.warning = true
59
+ t.test_files = FileList['test/test_middle.rb']
60
+ end
61
+
62
+ Rake::TestTask.new('nlconvert') do |t|
63
+ t.libs << 'test'
64
+ t.verbose = true
65
+ t.warning = true
66
+ t.test_files = FileList['test/test_nlconvert.rb']
67
+ end
68
+
69
+ Rake::TestTask.new('null') do |t|
70
+ t.libs << 'test'
71
+ t.verbose = true
72
+ t.warning = true
73
+ t.test_files = FileList['test/test_null.rb']
74
+ end
75
+
76
+ Rake::TestTask.new('tail') do |t|
77
+ t.libs << 'test'
78
+ t.verbose = true
79
+ t.warning = true
80
+ t.test_files = FileList['test/test_tail.rb']
81
+ end
82
+
83
+ Rake::TestTask.new('touch') do |t|
84
+ t.libs << 'test'
85
+ t.verbose = true
86
+ t.warning = true
87
+ t.test_files = FileList['test/test_touch.rb']
88
+ end
89
+
90
+ Rake::TestTask.new('wc') do |t|
91
+ t.libs << 'test'
92
+ t.verbose = true
93
+ t.warning = true
94
+ t.test_files = FileList['test/test_wc.rb']
95
+ end
96
+
97
+ Rake::TestTask.new('whereis') do |t|
98
+ t.libs << 'test'
99
+ t.verbose = true
100
+ t.warning = true
101
+ t.test_files = FileList['test/test_whereis.rb']
102
+ end
103
+
104
+ Rake::TestTask.new('which') do |t|
105
+ t.libs << 'test'
106
+ t.verbose = true
107
+ t.warning = true
108
+ t.test_files = FileList['test/test_which.rb']
109
+ end
110
+ end
data/lib/ptools.rb CHANGED
@@ -1,152 +1,218 @@
1
1
  require 'rbconfig'
2
- require 'win32/file' if Config::CONFIG['host_os'].match('mswin')
2
+
3
+ if Config::CONFIG['host_os'] =~ /mswin|dos|win32|cygwin|mingw/i
4
+ require 'win32/file'
5
+ end
3
6
 
4
7
  class File
5
- PTOOLS_VERSION = '1.1.7'
8
+ # The version of the ptools library.
9
+ PTOOLS_VERSION = '1.1.9'
10
+
11
+ # :stopdoc:
12
+
13
+ # The WIN32EXTS string is used as part of a Dir[] call in certain methods.
14
+ if Config::CONFIG['host_os'] =~ /mswin|dos|win32|cygwin|mingw/i
15
+ MSWINDOWS = true
16
+ if ENV['PATHEXT']
17
+ WIN32EXTS = ('.{' + ENV['PATHEXT'].tr(';', ',').tr('.','') + '}').downcase
18
+ else
19
+ WIN32EXTS = '.{exe,com,bat}'
20
+ end
21
+ else
22
+ MSWINDOWS = false
23
+ end
24
+
25
+ IMAGE_EXT = %w/.bmp .gif .jpg .jpeg .png/
26
+
27
+ # :startdoc:
28
+
29
+ # Returns whether or not the file is an image. Only JPEG, PNG, BMP and
30
+ # GIF are checked against.
31
+ #
32
+ # This method does some simple read and extension checks. For a version
33
+ # that is more robust, but which depends on a 3rd party C library (and is
34
+ # difficult to build on MS Windows), see the 'filemagic' library, available
35
+ # on the RAA.
36
+ #
37
+ # Examples:
38
+ #
39
+ # File.image?('somefile.jpg') # => true
40
+ # File.image?('somefile.txt') # => true
41
+ #--
42
+ # The approach I used here is based on information found at
43
+ # http://en.wikipedia.org/wiki/Magic_number_(programming)
44
+ #
45
+ def self.image?(file)
46
+ bool = IMAGE_EXT.include?(File.extname(file).downcase) # Match ext
47
+ bool = bmp?(file) || jpg?(file) || png?(file) || gif?(file) # Check data
48
+ bool
49
+ end
50
+
51
+ # Returns the name of the null device (aka bitbucket) on your platform.
52
+ #
53
+ # Examples:
54
+ #
55
+ # # On Linux
56
+ # File.null # => '/dev/null'
57
+ #
58
+ # # On MS Windows
59
+ # File.null # => 'NUL'
60
+ #--
61
+ # The values I used here are based on information from
62
+ # http://en.wikipedia.org/wiki//dev/null
63
+ #
64
+ def self.null
65
+ case Config::CONFIG['host_os']
66
+ when /mswin|win32|msdos|cygwin|mingw/i
67
+ 'NUL'
68
+ when /amiga/i
69
+ 'NIL:'
70
+ when /openvms/i
71
+ 'NL:'
72
+ else
73
+ '/dev/null'
74
+ end
75
+ end
76
+
77
+ class << self
78
+ alias null_device null
79
+ end
6
80
 
7
- # :stopdoc:
81
+ # Returns whether or not +file+ is a binary file. Note that this is
82
+ # not guaranteed to be 100% accurate. It performs a "best guess" based
83
+ # on a simple test of the first +File.blksize+ characters.
84
+ #
85
+ # Example:
86
+ #
87
+ # File.binary?('somefile.exe') # => true
88
+ # File.binary?('somefile.txt') # => false
89
+ #--
90
+ # Based on code originally provided by Ryan Davis (which, in turn, is
91
+ # based on Perl's -B switch).
92
+ #
93
+ def self.binary?(file)
94
+ s = (File.read(file, File.stat(file).blksize) || "").split(//)
95
+ ((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
96
+ end
97
+
98
+ # Looks for the first occurrence of +program+ within +path+.
99
+ #
100
+ # On Windows, it looks for executables ending with the suffixes defined
101
+ # in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
102
+ # that isn't defined, which you may optionally include in +program+.
103
+ #
104
+ # Returns nil if not found.
105
+ #
106
+ # Examples:
107
+ #
108
+ # File.which('ruby') # => '/usr/local/bin/ruby'
109
+ # File.which('foo') # => nil
110
+ #
111
+ def self.which(program, path=ENV['PATH'])
112
+ if path.nil? || path.empty?
113
+ raise ArgumentError, "path cannot be empty"
114
+ end
8
115
 
9
- if Config::CONFIG['host_os'].match('mswin')
10
- IS_WINDOWS = true
11
- begin
12
- WIN32EXTS = ENV['PATHEXT'].split(';').map{ |e| e.downcase }
13
- rescue
14
- WIN32EXTS = %w/.exe .com .bat/
116
+ # Bail out early if an absolute path is provided.
117
+ if program =~ /^\/|^[a-z]:[\\\/]/i
118
+ program += WIN32EXTS if MSWINDOWS && File.extname(program).empty?
119
+ found = Dir[program].first
120
+ if found && File.executable?(found) && !File.directory?(found)
121
+ return found
122
+ else
123
+ return nil
15
124
  end
16
- else
17
- IS_WINDOWS = false
18
- end
125
+ end
19
126
 
20
- IMAGE_EXT = %w/.bmp .gif .jpg .jpeg .png/
127
+ # Iterate over each path glob the dir + program.
128
+ path.split(File::PATH_SEPARATOR).each{ |dir|
129
+ next unless File.exists?(dir) # In case of bogus second argument
130
+ file = File.join(dir, program)
21
131
 
22
- # :startdoc:
132
+ # Dir[] doesn't handle backslashes properly, so convert them. Also, if
133
+ # the program name doesn't have an extension, try them all.
134
+ if MSWINDOWS
135
+ file = file.tr("\\", "/")
136
+ file += WIN32EXTS if File.extname(program).empty?
137
+ end
23
138
 
24
- # Returns whether or not the file is an image. Only JPEG, PNG, BMP and
25
- # GIF are checked against.
26
- #
27
- # This method does some simple read and extension checks. For a version
28
- # that is more robust, but which depends on a 3rd party C library (and is
29
- # difficult to build on MS Windows), see the 'filemagic' library, available
30
- # on the RAA.
31
- #--
32
- # Approach used here is based on information found at
33
- # http://en.wikipedia.org/wiki/Magic_number_(programming)
34
- #
35
- def self.image?(file)
36
- bool = IMAGE_EXT.include?(File.extname(file).downcase) # Match ext
37
- bool = bmp?(file) || jpg?(file) || png?(file) || gif?(file) # Check data
38
- bool
39
- end
139
+ found = Dir[file].first
40
140
 
41
- # Returns the null device (aka bitbucket) on your platform. On most
42
- # Unix-like systems this is '/dev/null', on Windows it's 'NUL', etc.
43
- #--
44
- # Based on information from http://en.wikipedia.org/wiki//dev/null
45
- #
46
- def self.null
47
- case Config::CONFIG['host_os']
48
- when /mswin/i
49
- 'NUL'
50
- when /amiga/i
51
- 'NIL:'
52
- when /openvms/i
53
- 'NL:'
54
- else
55
- '/dev/null'
141
+ # Convert all forward slashes to backslashes if supported
142
+ if found && File.executable?(found) && !File.directory?(found)
143
+ found.tr!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
144
+ return found
56
145
  end
57
- end
146
+ }
58
147
 
59
- # Returns whether or not +file+ is a binary file. Note that this is
60
- # not guaranteed to be 100% accurate. It performs a "best guess" based
61
- # on a simple test of the first +File.blksize+ characters.
62
- #--
63
- # Based on code originally provided by Ryan Davis (which, in turn, is
64
- # based on Perl's -B switch).
65
- #
66
- def self.binary?(file)
67
- s = (File.read(file, File.stat(file).blksize) || "").split(//)
68
- ((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
69
- end
70
-
71
- # Looks for the first occurrence of +program+ within +path+.
72
- #
73
- # On Windows, it looks for executables ending with the suffixes defined
74
- # in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
75
- # that isn't defined, which you may optionally include in +program+.
76
- #
77
- # Returns nil if not found.
78
- #
79
- def self.which(program, path=ENV['PATH'])
80
- programs = [program]
81
-
82
- # If no file extension is provided on Windows, try the WIN32EXT's in turn
83
- if IS_WINDOWS && File.extname(program).empty?
84
- unless WIN32EXTS.include?(File.extname(program).downcase)
85
- WIN32EXTS.each{ |ext|
86
- programs.push(program + ext)
87
- }
88
- end
148
+ nil
149
+ end
150
+
151
+ # Returns an array of each +program+ within +path+, or nil if it cannot
152
+ # be found.
153
+ #
154
+ # On Windows, it looks for executables ending with the suffixes defined
155
+ # in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
156
+ # that isn't defined, which you may optionally include in +program+.
157
+ #
158
+ # Examples:
159
+ #
160
+ # File.whereis('ruby') # => ['/usr/bin/ruby', '/usr/local/bin/ruby']
161
+ # File.whereis('foo') # => nil
162
+ #
163
+ def self.whereis(program, path=ENV['PATH'])
164
+ if path.nil? || path.empty?
165
+ raise ArgumentError, "path cannot be empty"
166
+ end
167
+
168
+ paths = []
169
+
170
+ # Bail out early if an absolute path is provided.
171
+ if program =~ /^\/|^[a-z]:[\\\/]/i
172
+ program += WIN32EXTS if MSWINDOWS && File.extname(program).empty?
173
+ found = Dir[program]
174
+ if found[0] && File.executable?(found[0]) && !File.directory?(found[0])
175
+ return found
176
+ else
177
+ return nil
89
178
  end
90
-
91
- # Catch the first path found, or nil
92
- location = catch(:done){
93
- path.split(File::PATH_SEPARATOR).each{ |dir|
94
- programs.each{ |prog|
95
- f = File.join(dir, prog)
96
- if File.executable?(f) && !File.directory?(f)
97
- location = File.join(dir, prog)
98
- location.tr!('/', File::ALT_SEPARATOR) if File::ALT_SEPARATOR
99
- throw(:done, location)
100
- end
101
- }
102
- }
103
- nil # Evaluate to nil if not found
104
- }
179
+ end
105
180
 
106
- location
107
- end
181
+ # Iterate over each path glob the dir + program.
182
+ path.split(File::PATH_SEPARATOR).each{ |dir|
183
+ next unless File.exists?(dir) # In case of bogus second argument
184
+ file = File.join(dir, program)
108
185
 
109
- # In block form, yields each +program+ within +path+. In non-block
110
- # form, returns an array of each +program+ within +path+.
111
- #
112
- # On Windows, it looks for executables ending with the suffixes defined
113
- # in your PATHEXT environment variable, or '.exe', '.bat' and '.com' if
114
- # that isn't defined, which you may optionally include in +program+.
115
- #
116
- # Returns nil if not found.
117
- #
118
- def self.whereis(program, path=ENV['PATH'])
119
- dirs = []
120
- programs = [program]
121
-
122
- # If no file extension is provided on Windows, try the WIN32EXT's in turn
123
- if IS_WINDOWS && File.extname(program).empty?
124
- unless WIN32EXTS.include?(File.extname(program).downcase)
125
- WIN32EXTS.each{ |ext|
126
- programs.push(program + ext)
127
- }
128
- end
186
+ # Dir[] doesn't handle backslashes properly, so convert them. Also, if
187
+ # the program name doesn't have an extension, try them all.
188
+ if MSWINDOWS
189
+ file = file.tr("\\", "/")
190
+ file += WIN32EXTS if File.extname(program).empty?
129
191
  end
130
-
131
- path.split(File::PATH_SEPARATOR).each{ |dir|
132
- programs.each{ |prog|
133
- file = File.join(dir,prog)
134
- file.tr!('/', File::ALT_SEPARATOR) if File::ALT_SEPARATOR
135
- if File.executable?(file) && !File.directory?(file)
136
- if block_given?
137
- yield file
138
- else
139
- dirs << file
140
- end
141
- end
142
- }
143
- }
144
- dirs.empty? ? nil : dirs.uniq
145
- end
192
+
193
+ found = Dir[file].first
194
+
195
+ # Convert all forward slashes to backslashes if supported
196
+ if found && File.executable?(found) && !File.directory?(found)
197
+ found.tr!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
198
+ paths << found
199
+ end
200
+ }
201
+
202
+ paths.empty? ? nil : paths.uniq
203
+ end
146
204
 
147
205
  # In block form, yields the first +num_lines+ from +filename+. In non-block
148
206
  # form, returns an Array of +num_lines+
149
207
  #
208
+ # Examples:
209
+ #
210
+ # # Return an array
211
+ # File.head('somefile.txt') # => ['This is line1', 'This is line2', ...]
212
+ #
213
+ # # Use a block
214
+ # File.head('somefile.txt'){ |line| puts line }
215
+ #
150
216
  def self.head(filename, num_lines=10)
151
217
  a = []
152
218
  IO.foreach(filename){ |line|
@@ -172,26 +238,29 @@ class File
172
238
  end
173
239
  end
174
240
 
175
- # In block form, yields the last +num_lines+ of file +filename+.
176
- # In non-block form, it returns the lines as an array.
177
- #
178
- # Note that this method slurps the entire file, so I don't recommend it
179
- # for very large files. Also note that 'tail -f' functionality is not
180
- # present.
181
- #
182
- def self.tail(filename, num_lines=10)
183
- if block_given?
184
- IO.readlines(filename).reverse[0..num_lines-1].reverse.each{ |line|
185
- yield line
186
- }
187
- else
188
- IO.readlines(filename).reverse[0..num_lines-1].reverse
189
- end
190
- end
241
+ # In block form, yields the last +num_lines+ of file +filename+.
242
+ # In non-block form, it returns the lines as an array.
243
+ #
244
+ # Note that this method slurps the entire file, so I don't recommend it
245
+ # for very large files. Also note that 'tail -f' functionality is not
246
+ # present. See the 'file-tail' library for that.
247
+ #
248
+ # Example:
249
+ #
250
+ # File.tail('somefile.txt') # => ['This is line7', 'This is line8', ...]
251
+ #
252
+ def self.tail(filename, num_lines=10)
253
+ if block_given?
254
+ IO.readlines(filename).reverse[0..num_lines-1].reverse.each{ |line|
255
+ yield line
256
+ }
257
+ else
258
+ IO.readlines(filename).reverse[0..num_lines-1].reverse
259
+ end
260
+ end
191
261
 
192
262
  # Converts a text file from one OS platform format to another, ala
193
- # 'dos2unix'. Valid values for 'format', which are case insensitve,
194
- # include:
263
+ # 'dos2unix'. The possible values for +platform+ include:
195
264
  #
196
265
  # * MS Windows -> dos, windows, win32, mswin
197
266
  # * Unix/BSD -> unix, linux, bsd
@@ -201,12 +270,12 @@ class File
201
270
  # TypeError will be raised. If an invalid format value is received, an
202
271
  # ArgumentError is raised.
203
272
  #
204
- def self.nl_convert(filename, newfilename=filename, platform="dos")
205
- unless File.ftype(filename) == "file"
206
- raise TypeError, "Only valid for plain text files"
273
+ def self.nl_convert(old_file, new_file = old_file, platform = 'dos')
274
+ unless File.file?(old_file)
275
+ raise ArgumentError, 'Only valid for plain text files'
207
276
  end
208
277
 
209
- if platform =~ /dos|windows|win32|mswin/i
278
+ if platform =~ /dos|windows|win32|mswin|cygwin|mingw/i
210
279
  format = "\cM\cJ"
211
280
  elsif platform =~ /unix|linux|bsd/i
212
281
  format = "\cJ"
@@ -219,27 +288,28 @@ class File
219
288
  orig = $\
220
289
  $\ = format
221
290
 
222
- if filename == newfilename
291
+ if old_file == new_file
223
292
  require 'fileutils'
224
293
  require 'tempfile'
225
294
 
226
295
  begin
227
- tf = Tempfile.new('ruby_temp_' + Time.now.to_s)
296
+ temp_name = Time.new.strftime("%Y%m%d%H%M%S")
297
+ tf = Tempfile.new('ruby_temp_' + temp_name)
228
298
  tf.open
229
299
 
230
- IO.foreach(filename){ |line|
300
+ IO.foreach(old_file){ |line|
231
301
  line.chomp!
232
302
  tf.print line
233
303
  }
234
304
  ensure
235
305
  tf.close if tf && !tf.closed?
236
306
  end
237
- File.delete(filename)
238
- FileUtils.cp(tf.path, filename)
307
+ File.delete(old_file)
308
+ FileUtils.cp(tf.path, old_file)
239
309
  else
240
310
  begin
241
- nf = File.new(newfilename, 'w')
242
- IO.foreach(filename){ |line|
311
+ nf = File.new(new_file, 'w')
312
+ IO.foreach(old_file){ |line|
243
313
  line.chomp!
244
314
  nf.print line
245
315
  }
@@ -252,85 +322,85 @@ class File
252
322
  self
253
323
  end
254
324
 
255
- # Changes the access and modification time if present, or creates a 0
256
- # byte file +filename+ if it doesn't already exist.
257
- #
258
- def self.touch(filename)
259
- if File.exists?(filename)
260
- time = Time.now
261
- File.utime(time, time, filename)
262
- else
263
- File.open(filename, 'w'){}
264
- end
265
- self
266
- end
325
+ # Changes the access and modification time if present, or creates a 0
326
+ # byte file +filename+ if it doesn't already exist.
327
+ #
328
+ def self.touch(filename)
329
+ if File.exists?(filename)
330
+ time = Time.now
331
+ File.utime(time, time, filename)
332
+ else
333
+ File.open(filename, 'w'){}
334
+ end
335
+ self
336
+ end
267
337
 
268
- # With no arguments, returns a four element array consisting of the number
269
- # of bytes, characters, words and lines in filename, respectively.
270
- #
271
- # Valid options are 'bytes', 'characters' (or just 'chars'), 'words' and
272
- # 'lines'.
273
- #
274
- def self.wc(filename, option='all')
275
- option.downcase!
276
- valid = %w/all bytes characters chars lines words/
338
+ # With no arguments, returns a four element array consisting of the number
339
+ # of bytes, characters, words and lines in filename, respectively.
340
+ #
341
+ # Valid options are 'bytes', 'characters' (or just 'chars'), 'words' and
342
+ # 'lines'.
343
+ #
344
+ def self.wc(filename, option='all')
345
+ option.downcase!
346
+ valid = %w/all bytes characters chars lines words/
277
347
 
278
- unless valid.include?(option)
279
- raise ArgumentError, "Invalid option: '#{option}'"
280
- end
348
+ unless valid.include?(option)
349
+ raise ArgumentError, "Invalid option: '#{option}'"
350
+ end
281
351
 
282
- n = 0
283
- if option == 'lines'
284
- IO.foreach(filename){ n += 1 }
285
- return n
286
- elsif option == 'bytes'
287
- File.open(filename){ |f|
288
- f.each_byte{ n += 1 }
289
- }
290
- return n
291
- elsif option == 'characters' || option == 'chars'
292
- File.open(filename){ |f|
293
- while f.getc
294
- n += 1
295
- end
296
- }
297
- return n
298
- elsif option == 'words'
299
- IO.foreach(filename){ |line|
300
- n += line.split.length
301
- }
302
- return n
303
- else
304
- bytes,chars,lines,words = 0,0,0,0
305
- IO.foreach(filename){ |line|
306
- lines += 1
307
- words += line.split.length
308
- chars += line.split('').length
309
- }
310
- File.open(filename){ |f|
311
- while f.getc
312
- bytes += 1
313
- end
314
- }
315
- return [bytes,chars,words,lines]
316
- end
317
- end
352
+ n = 0
353
+ if option == 'lines'
354
+ IO.foreach(filename){ n += 1 }
355
+ return n
356
+ elsif option == 'bytes'
357
+ File.open(filename){ |f|
358
+ f.each_byte{ n += 1 }
359
+ }
360
+ return n
361
+ elsif option == 'characters' || option == 'chars'
362
+ File.open(filename){ |f|
363
+ while f.getc
364
+ n += 1
365
+ end
366
+ }
367
+ return n
368
+ elsif option == 'words'
369
+ IO.foreach(filename){ |line|
370
+ n += line.split.length
371
+ }
372
+ return n
373
+ else
374
+ bytes,chars,lines,words = 0,0,0,0
375
+ IO.foreach(filename){ |line|
376
+ lines += 1
377
+ words += line.split.length
378
+ chars += line.split('').length
379
+ }
380
+ File.open(filename){ |f|
381
+ while f.getc
382
+ bytes += 1
383
+ end
384
+ }
385
+ return [bytes,chars,words,lines]
386
+ end
387
+ end
318
388
 
319
- private
389
+ private
320
390
 
321
- def self.bmp?(file)
322
- IO.read(file, 3) == "BM6"
323
- end
391
+ def self.bmp?(file)
392
+ IO.read(file, 3) == "BM6"
393
+ end
324
394
 
325
- def self.jpg?(file)
326
- IO.read(file, 10) == "\377\330\377\340\000\020JFIF"
327
- end
395
+ def self.jpg?(file)
396
+ IO.read(file, 10) == "\377\330\377\340\000\020JFIF"
397
+ end
328
398
 
329
- def self.png?(file)
330
- IO.read(file, 4) == "\211PNG"
331
- end
399
+ def self.png?(file)
400
+ IO.read(file, 4) == "\211PNG"
401
+ end
332
402
 
333
- def self.gif?(file)
334
- ['GIF89a', 'GIF97a'].include?(IO.read(file, 6))
335
- end
403
+ def self.gif?(file)
404
+ ['GIF89a', 'GIF97a'].include?(IO.read(file, 6))
405
+ end
336
406
  end