Including FileUtils::Verbose crashes Marshal.dump

This is a bug I ran into today. I’m not sure who’s responsible for
fixing it, but I don’t personally feel up to
the task. I could make it a multi-day project I suppose, but I feel I
have better things to do today, and tomorrow,

I’m posting this here in hopes that someone who reads this list will
know what to do to fix it.

The bug exists in 1.9.2, 1.9.3, and 2.0.0.

I had written a class for vocabulary cards, which I Marshal.dump’ed at
the end of each session. Then I thought that
before I casually dumped the list a second or subsequent time, I should
make sure that, should anything bad happen,
I didn’t completely clobber the dump file. So, using fileutils, I made a
backup copy before dumping, and then removed
it if things went well, or used it to restore the original file if
things didn’t go well.

I found the dump worked the first time, but each in each subsequent
session the dump would fail, evidently because
somewhere in my object graph an IO had crept in as an instance variable.
I was pretty sure I hadn’t done that, so I had
to write some code to traverse the code and look for an IO somewhere. I
found it, and present for your perusal a small test
file that illustrates the problem.

My problem was that I read about fileutils in the pickaxe book (for 1.9
and 2.0), and the example included FileUtils::Verbose,
and I figured that would be fine for starters, as I could then see what
it was doing.

If you copy this to a file and run it, the first time it will not find
the file (“aFoo”), won’t perform the backup, and will successfully
dump the object to the file. The second time, however, it will find the
find, use FileUtil’s cp to make a backup copy, and then
try unsuccessfully to dump the file, which fails because the object
contains an instance of IO. In the example, I catch this
exception and then go on to list the instance variables, and Whoa!, what
the devil are those instance variables “@filutiils_label
and “@fileutils_output” doing there?

So evidently calling the verbose version of FileUtils#cp creates,
unbeknownst to the user, a few instance variables for its own
use. I suggest there must be a better place to keep this information.
The problem goes away entirely if you just include FileUtils and
not FileUtils::Verbose.

Thanks for listening, here’s the file. Have a nice day,

Bryan


#!/usr/bin/env ruby
require ‘fileutils’
include FileUtils::Verbose

class Foo
def initialize(str)
@foo = str
end

def make_backup_copy
cp(@foo, “#{@foo}.back”) if File.exist?(@foo)
end

def dump_to_file
make_backup_copy
puts “dumping to file #{@foo}”
begin
File.open(@foo, “w”) { |file| Marshal.dump(self, file) }
rescue Exception => exc
puts exc
return
end
puts “dumped to file #{@foo}”
end

end
f = Foo.new(“aFoo”)
f.dump_to_file
puts “instance variables are now #{f.instance_variables}”

and here’s the output from running it twice:

18:18 cards:>./tester.rb
dumping to file aFoo
dumped to file aFoo
instance variables are now [:@foo]
18:18 cards:>./tester.rb
cp aFoo aFoo.back
dumping to file aFoo
can’t dump IO
instance variables are now [:@foo, :@fileutils_output,
:@fileutils_label]

On 07/01/2013 09:22 AM, Bryan L. wrote:

My problem was that I read about fileutils in the pickaxe book (for 1.9 and
2.0), and the example included FileUtils::Verbose,
and I figured that would be fine for starters, as I could then see what it was
doing.

That example[1] seems to have been written with brevity in mind. It’s ok
in a 10 line script to include modules globally. In general, one must be
careful, for reasons like the one you found. The internal state managed
by those modules can interact badly with the internal state of other
classes. It’s better, though more verbose, to call methods on the
module:

FileUtils.cp(…)

An exception would be if you were designing a class as a command
language, like rake. But in that case, you would only include FileUtils
into that class, not globally.

[1] Here’s the Pickaxe example:

require ‘fileutils’
include FileUtils::Verbose
cd("/tmp") do
cp("/etc/passwd", “tmp_passwd”)
chmod(0666, “tmp_passwd”)
cp_r("/usr/include/net/", “headers”)
rm(“tmp_passwd”)

Tidy up

rm_rf(“headers”)
end

On Mon, Jul 1, 2013 at 6:40 PM, Joel VanderWerf
[email protected]wrote:

in a 10 line script to include modules globally. In general, one must be
careful, for reasons like the one you found. The internal state managed by
those modules can interact badly with the internal state of other classes.
It’s better, though more verbose, to call methods on the module:

FileUtils.cp(…)

An exception would be if you were designing a class as a command language,
like rake. But in that case, you would only include FileUtils into that
class, not globally.

Even there I probably wouldn’t. Here’s a shortened version of what
happens:

$ ruby x.rb
[]
[:@var]
$ cat x.rb

module X
def bar
@var = 123
end
end

include X

class Y
def m
bar
end
end

y = Y.new
p y.instance_variables
y.m
p y.instance_variables
$

This shows the dangers of including modules.

Kind regards

robert