How to destroy a class correctly

In Ruby, I have a DAO class that is extended by a class that simplifies connection management, which is extended by a class that represents and processes data in a database, which is further extended by another class. To use the animal metaphor, it would look like this:

class Animal ... end class Mammal < Animal ... end class Feline < Mammal ... end class Cat < Feline ... end class Lion < Cat ... end ... 

In PHP, there is a __destruct method that runs when a class is destroyed / deleted. And if this class extends another class, you simply add parent::__destruct() to the __destruct class __destruct as follows:

 public function __destruct() { // Clean up code for this class here ... // Execute clean up code for Parent class parent::__destruct(); } 

I could have a similar method for all classes except Animal . Since it does not expand anything, the line parent::__destruct(); no longer valid.

However, as I understand it, Ruby does not have such a method for its objects. The finalizer can be installed, but I decided to just add a cleanup method, which I can call when I want to destroy / delete the class. This will take care of everything that needed to be done before I set the class to nil .

This creates a new problem. If the method is always called cleanup and I call lion_instance.cleanup , I assume that it calls Lion#cleanup . How then to force it to call cleanup in the Cat class, and then Feline and in the chain?

Or is this the wrong approach and you have an idea?

+4
source share
3 answers

The Ruby hierarchy for this is the exit to the block that works, and when the block returns, do a cleanup. The Ruby built-in "File.open" does this:

 File.open("/tmp/foo") do |file| file.puts "foo" end 

When the block ends, the file is closed for you, without having to do anything. This is a great idiom. Here you can implement something like this:

 class Foo def self.open(*args) foo = new(*args) yield foo foo.close end def initialize # do setup here end def close # do teardown here end end 

And use it:

 Foo.open do |foo| # use foo end 

Foo#close will be called automatically after end


This will also work with a subclass. This is because class methods are inherited in the same way as instance methods. Here's the superclass:

 class Superclass def self.open(*args) o = new(*args) yield o o.close end def initialize # common setup behavior end def close # common cleanup behavior end end 

and two derived classes:

 class Foo < Superclass def initialize super # do subclass specific setup here end def close super # do subclass specific teardown here end end class Bar < Superclass def initialize super # do subclass specific setup here end def close super # do subclass specific teardown here end end 

for use:

 Foo.open do |foo| # use foo end Bar.open do |bar| # use bar end 

If you really need to make sure that the cleanup happens no matter what you do, use the make clause in the class method:

  def self.open(*args) foo = new(*args) begin yield foo ensure foo.close end end 

Thus, cleaning occurs even if there is an exception in the block.

+6
source

You can use ObjectSpace.define_finalizer

Sort of:

 class Animal def initialize ObjectSpace.define_finalizer(self, proc { # your code }) end end 
+2
source

Well, since no one answered your question about a method advancing along the inheritance chain ...

 class Cat def rawr puts "rawr" end end class Kitty < Cat def rawr puts "meow" super end end Cat.new.rawr "Rawr" Kitty.new.rawr "rawr" "meow" 

Within a method, you can access a superclass method of the same name by calling super.

+1
source

Source: https://habr.com/ru/post/1435568/


All Articles