I have a class that overrides freeze:
class Foo
def initialize
@x = Container.new
end
def freeze
@x.each { |x| x.freeze }
@x.freeze
super
end
end
However, it's possible that any of the calls to freeze, or even the call
to each(), could throw an exception. In that case, I don't want to
leave my object in a half-frozen state. So I do this:
class Foo
def freeze
undo = []
begin
@x.each do |x|
if not x.frozen? then
x.freeze
undo.push proc { x.unfreeze }
end
end
if not @x.frozen? then
@x.freeze
undo.push proc { @x.unfreeze }
end
super
rescue Exception
undo.each { |u| u.call }
raise $!, $!.message, $!.backtrace
end
end
end
(It's actually a little more complicated than this, since I have to make
sure objects get properly unfrozen even when undo.push fails).
I have implemented a simple unfreeze in an extension. The problem,
though, as someone pointed out to me recently, is that unfreeze is not
safe, particularly on strings. The likelihood of Ruby making a copy of
one of the frozen objects in @x while I'm freezing is low, but it could
happen. So this is not a good solution.
Is there an exception-safe way that I can freeze sub-objects?
Paul