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
Best Practices For Reusing Code Between Controllers in Ruby on Rails
How Does Load Differ from Require in Ruby
What Evaluates to False in Ruby
Why Are Methods in Ruby Documentation Preceded by a Hash Sign
Find Number of Months Between Two Dates in Ruby on Rails
How to Make an Http Get With Modified Headers
Looking For Suggestions For Building a Secure Rest API Within Ruby on Rails
Putting French (Accented) Characters in Ruby File
Converting Ruby Array to Array of Consecutive Pairs
How to Dynamically Call a Math Operator in Ruby
Serving Static Files With Sinatra
How to Call a Controller'S Method from a View (As We Call from Helper Ideally)
Why Does Ruby'S 'Gets' Includes the Closing Newline
When to Use Each Method of Launching a Subprocess in Ruby
How to Calculate the Day of the Week of a Date in Ruby
Matching Balanced Parenthesis in Ruby Using Recursive Regular Expressions Like Perl
How to Add an Array to Another Array in Ruby and Not End Up With a Multi-Dimensional Result