Ruby: Destructors

Ruby: Destructors?

You can use ObjectSpace.define_finalizer when you create the image file, and it will get invoked when the garbage man comes to collect. Just be careful not to reference the object itself in your proc, otherwise it won't be collected by the garbage man. (Won't pick up something that's alive and kicking)

class MyObject
def generate_image
image = ImageMagick.do_some_magick
ObjectSpace.define_finalizer(self, proc { image.self_destruct! })
end
end

ruby equivalent of destructor

There is a hook called ObjectSpace.define_finalizer that is called when an object is destroyed.

How do you test whether a Ruby destructor will be called?

finalize is called in initialize, returns the proc, and is never called again, so you can't expect it to be called at finalization time. It's the proc that's called when the instance is finalized. To check that, have the proc call a method instead of doing the work itself. This passes:

class DataWriter
# initialize and write same as above

def self.finalize(file)
proc { actually_finalize file }
end

def self.actually_finalize(file)
file.close
end

end

RSpec.describe DataWriter do
context 'it should call its destructor' do
it 'calls the destructor' do
data_writer = DataWriter.new('/tmp/example.txt')
expect(DataWriter).to receive(:actually_finalize)
data_writer = nil
GC.start
end
end
end

RAII in Ruby (Or, How to Manage Resources in Ruby)

So that users don't "have to remember to do the whole begin-rescue-ensure chacha" combine rescue/ensure with yield.

class SomeResource
...
def SomeResource.use(*resource_args)
# create resource
resource = SomeResource.new(*resource_args) # pass args direct to constructor
# export it
yield resource
rescue
# known error processing
...
ensure
# close up when done even if unhandled exception thrown from block
resource.close
end
...
end

Client code can use it as follows:

SomeResource.use(connection_string) do | resource |
resource.do_something
... # whatever else
end
# after this point resource has been .close()d

In fact this is how File.open operates - making the first answer confusing at best (well it was to my work colleagues).

File.open("testfile") do |f|
# .. process - may include throwing exceptions
end
# f is guaranteed closed after this point even if exceptions are
# thrown during processing

Is there a way to destroy *self* after a class is instantiated?

Ruby Finalizers Aren't Really Destructors

While you can define finalizers for Ruby objects, they aren't really destructors as such. In fact, they aren't triggered until after the object is destroyed. The docs say:

define_finalizer(obj, aProc=proc())

Adds aProc as a finalizer, to be called after obj was destroyed.

Rethink Your Approach

Rather than instantiating your browser instance with #new, consider a pattern more like:

class Something
attr_accessor :browser

def start_browser
end

def quit_browser
end
end

s = Something.new
s.start_browser
s.quit_browser

Other patterns are also possible, including setting callbacks or timers within your object. Ultimately, the point is that objects should go out of scope and be garbage collected---they can't actually destroy themselves.

Notification of object destruction in Ruby

If you need to control what happens when an object is destroyed, you really should be explicitly destroying it yourself - this is by design. You're not supposed to be able to destroy an object explicitly either - this is also by design.

In other words, from the perspective of your program, an object is never destroyed or destroyable. For these reasons you should re-think the problem (this is not an uncommon need - release of resources when the object is no longer needed) so it fits into the Ruby paradigm.

Setting the object to nil gives a hint to the garbage collector, but does not necessarily immediately destroy it.

However, if you must have the garbage collector handle it, then read on.

There is no direct support for a destructor, but you can have it call a finalizer function when it is destroyed.

According to http://pleac.sourceforge.net/pleac_ruby/classesetc.html it may not be garbage collected if it contains a reference to the original object, so must be a class method and not an instance method.

class MyClass
def initialize
ObjectSpace.define_finalizer(self,
self.class.method(:finalize).to_proc)
end
def MyClass.finalize(id)
puts "Object #{id} dying at #{Time.new}"
end
end


Related Topics



Leave a reply



Submit