ruby woes

Kevin Fries kevin at fries-biro.com
Thu Dec 29 10:14:21 MST 2011


On Wed, 2011-12-28 at 22:56 -0700, Michael Havens wrote:
> I hate  it when you are trying to learn something and what you are
> learning from is incomplete or wrong. This is the beginning of a ruby
> script I am writing which will (in the section shown) erase a file.
> ruby tells me that line 15 is wrong! It says: 
> 
>     ex16.rb:15: undefined local variable or method `file' for
> main:Object (NameError)
> 
> I am guessing that what line 15 is doing is telling the program to
> make file 'target' smaller but it needs to know how small and the guy
> who writes Learn Code the Hard Way forgot to include that in his
> tutorial. Or else I'm wrong and in that case please  show me where I
> am wrong.
> 
> 1 filename = ARGV.first #argument at command line is given a name
> 2 script = $0 #script is now titled  file's name
> 3
> 4 puts "We're going to erase the contents of #{filename}."
> 5 puts "If you don't want to do that hit CTRL-C."
> 6 puts "If you do want to do that then hit RETURN."
> 7
> 8 print "? "
> 9 STDIN.gets #it waits for input before going on
> 10
> 11 puts "Opening the file..."
> 12 target = File.open(filename, 'w') #tells it to open filename or to
> write it if non-existant
> 13
> ^^^^is this right?^^^^^^^^^^
> 14 puts "Truncating the file.  Goodbye!"
> 15 target.truncate(target.size)

My Diagnostic process:

First I input your program without any comments:
---------------------------------------------------------------------------
#!/usr/bin/env ruby

filename = ARGV.first
script = $0

puts "We're going to erase the contents of #{filename}"
puts "If you don't want to do that hit CTRL-C"
puts "If you want to do that then hit ENTER"

print "?"
STDIN.gets

puts "Opening the file..."
target = File.open(filename, 'w')

puts "Truncating the file. Goodbye!"
target.truncate(target.size)
---------------------------------------------------------------------------

Then I ran it without any parameters:
---------------------------------------------------------------------------
kfries at kfries-laptop:Kevin$ ./sample.rb 
We're going to erase the contents of 
If you don't want to do that hit CTRL-C
If you want to do that then hit ENTER
?
Opening the file...
./sample.rb:14:in `initialize': can't convert nil into String
(TypeError)
	from ./sample.rb:14:in `open'
	from ./sample.rb:14
kfries at kfries-laptop:Kevin$ 
---------------------------------------------------------------------------

OK not good form, added a line after the filename = as such:
   raise "Missing parameter\n\n\tUseage: $0 <filename>" if filename.nil?

ran again with no parameter:
---------------------------------------------------------------------------
kfries at kfries-laptop:Kevin$ ./sample.rb 
./sample.rb:4: Missing parameter (RuntimeError)

	Useage: $0 <filename>
kfries at kfries-laptop:Kevin$ 
---------------------------------------------------------------------------

Much better, always remember, any time you ask for input, then use it
(as 
opposed to press enter to continue), validate it.  In this case, I just
verified
that something was entered, not that it was correct.

Next I generated a sample file by doing an ls on /usr/bin, then tried to
truncate
that file with your program.
---------------------------------------------------------------------------
kfries at kfries-laptop:Kevin$ ls /usr/bin > junk.txt 
kfries at kfries-laptop:Kevin$ ./sample.rb junk.txt 
We're going to erase the contents of junk.txt
If you don't want to do that hit CTRL-C
If you want to do that then hit ENTER
?
Opening the file...
Truncating the file. Goodbye!
./sample.rb:18: undefined method `size' for #<File:junk.txt>
(NoMethodError)
kfries at kfries-laptop:Kevin$ ls -l
total 4
-rw-rw-r-- 1 kfries kfries   0 2011-12-29 09:30 junk.txt
-rwxrwxr-x 1 kfries kfries 425 2011-12-29 09:27 sample.rb
kfries at kfries-laptop:Kevin$ 
---------------------------------------------------------------------------

OK, better, it did what it was supposed to do, but then errored out.

The error states that the method .size does not exist on the object
File.

Lets see (using Ruby) what it does support:
---------------------------------------------------------------------------
kfries at kfries-laptop:Kevin$ irb
irb(main):001:0> target = File.open("junk.txt", "w")
=> #<File:junk.txt>
irb(main):002:0> target.size
NoMethodError: undefined method `size' for #<File:junk.txt>
	from (irb):2
	from :0
irb(main):003:0> target.methods
=> ["stat", "fileno", "find", "inspect", "tty?", "tap", "seek", "<<",
"print", "clone", "binmode", "take", "object_id", "__send__",
"public_methods", "read_nonblock", "reject", "lines", "ctime",
"instance_variable_defined?", "pos", "minmax", "readlines", "freeze",
"equal?", "closed?", "member?", "each", "sort", "extend", "partition",
"getc", "each_cons", "readbyte", "send", "pos=", "close_read", "any?",
"each_with_index", "methods", "lstat", "to_io", "detect", "hash",
"putc", "dup", "take_while", "sysseek", "instance_variables", "to_enum",
"bytes", "write_nonblock", "collect", "eof", "min_by", "eql?", "reopen",
"sort_by", "enum_cons", "ungetc", "group_by", "id", "instance_eval",
"truncate", "flock", "close_write", "one?", "enum_with_index", "to_i",
"singleton_methods", "each_line", "fsync", "find_index", "puts",
"taint", "ioctl", "drop", "instance_variable_get", "frozen?",
"enum_for", "chars", "chmod", "readpartial", "map", "display",
"instance_of?", "max_by", "method", "syswrite", "grep", "to_a", "flush",
"first", "instance_exec", "type", "isatty", "none?", "reverse_each",
"protected_methods", "find_all", "each_byte", "atime", "sync", "==",
"min", "gets", "===", "eof?", "fcntl", "drop_while",
"instance_variable_set", "path", "write", "each_slice", "chown",
"sync=", "getbyte", "inject", "respond_to?", "kind_of?", "minmax_by",
"close", "to_s", "sysread", "count", "printf", "tell", "class", "zip",
"private_methods", "=~", "tainted?", "__id__", "each_char", "mtime",
"lineno", "select", "readline", "rewind", "max", "untaint", "nil?",
"entries", "pid", "cycle", "enum_slice", "lineno=", "readchar",
"reduce", "read", "include?", "is_a?", "all?"]
irb(main):004:0> quit
kfries at kfries-laptop:Kevin$ 
---------------------------------------------------------------------------

OK, using IRB, I can confirm that the truncate command should work, and
the error message did not indicate a problem with that.  It also does
not support size, and your error message did indicate that.  So, the
error message is telling you, that size does not make sense on the File
object, and IRB, and the methods command confirms this.

So, what should be in there?

Well, RubyDoc is your friend, far more than any book can be.  I looked
up File::truncate for ruby 1.8.7 (the version on my Ubuntu system) at
the following URL:

   http://ruby-doc.org/core-1.8.7/File.html#method-c-truncate

And it gave me this:
---------------------------------------------------------------------------
truncate(file_name, integer) => 0

Truncates the file file_name to be at most integer bytes long. Not
available on all platforms.

f = File.new("out", "w")
f.write("1234567890")     #=> 10
f.close                   #=> nil
File.truncate("out", 5)   #=> 0
File.size("out")          #=> 5
---------------------------------------------------------------------------

So, it is looking for an integer, to indicate how large you want the
file.  It also shows the file method being used! So what went wrong???
IRB is showing no size method, and the documents shows it valid?

Well, you fell into a common problem of understanding scope.  All
languages deal with this problem, and it manifests itself in lots of
different ways.

In this case, size() is a class method, not an instance method.  What
this means is that, I can call size() against the class, and feed it a
filename, but it is not defined in a derived object. So, instead of
target.size, use File.size(filename).

Making this change the the program gives me this:
---------------------------------------------------------------------------
#!/usr/bin/env ruby

filename = ARGV.first
raise "Missing parameter\n\n\tUseage: $0 <filename>" if filename.nil?
script = $0

puts "We're going to erase the contents of #{filename}"
puts "If you don't want to do that hit CTRL-C"
puts "If you want to do that then hit ENTER"

print "?"
STDIN.gets

puts "Opening the file..."
target = File.open(filename, 'w')

puts "Truncating the file. Goodbye!"
target.truncate(File.size(filename))
---------------------------------------------------------------------------

Now lets test it:
---------------------------------------------------------------------------
kfries at kfries-laptop:Kevin$ ls /usr/bin > junk.txt
kfries at kfries-laptop:Kevin$ ls -la
total 36
drwxrwxr-x  2 kfries kfries  4096 2011-12-29 10:00 .
drwxrwxrwt 22 root   root    4096 2011-12-29 09:46 ..
-rw-rw-r--  1 kfries kfries 22634 2011-12-29 10:02 junk.txt
-rwxrwxr-x  1 kfries kfries   433 2011-12-29 10:00 sample.rb
kfries at kfries-laptop:Kevin$ ./sample.rb junk.txt 
We're going to erase the contents of junk.txt
If you don't want to do that hit CTRL-C
If you want to do that then hit ENTER
?
Opening the file...
Truncating the file. Goodbye!
kfries at kfries-laptop:Kevin$ ls -l
total 4
-rw-rw-r-- 1 kfries kfries   0 2011-12-29 10:03 junk.txt
-rwxrwxr-x 1 kfries kfries 433 2011-12-29 10:00 sample.rb
kfries at kfries-laptop:Kevin$ 
---------------------------------------------------------------------------

SUCCESS!!

I hope seeing me step through the debugging was helpful to you

Kevin




More information about the PLUG-discuss mailing list