ptools 1.1.7 → 1.1.9

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