How to store/load persistent data?

Hello!

In my previous life (in perl-world) I was used to store persistent data
with Data::Dumper and load it back with "require '/path/to/file'".

Now I'm learing ruby and started my first toy-project. I must admit that
I am not very familiar with OO techniques.

AFAICS, the ruby way to store persistent data is YAML. Saving an object
with YAML.dump() works like a charm. But I have trouble to read them
back with YAML.load(). For some reason, the initialize() method of the
loaded object doesn't get called. I don't understand how an object can
properly spring in existance without the initialize method? For example,
I allocate a TkCanvas in the initialize() method. Such an object can not
be loaded back properly, IMHO.

I have one more problem with such an object: There's no destructor. Should
such an object get out of scope, how do I make sure the allocated canvas
is destroyed properly?

I'm sorry if this is a stupid question, but I am new to both, ruby and OO.
Please can somebody give an explanation how such an object is handled in
the ruby-world?

Hello!

In my previous life (in perl-world) I was used to store persistent data
with Data::Dumper and load it back with "require '/path/to/file'".

Now I'm learing ruby and started my first toy-project. I must admit that
I am not very familiar with OO techniques.

AFAICS, the ruby way to store persistent data is YAML. Saving an object
with YAML.dump() works like a charm. But I have trouble to read them
back with YAML.load(). For some reason, the initialize() method of the
loaded object doesn't get called. I don't understand how an object can
properly spring in existance without the initialize method?

Well the general purpose of initialize is to set up some state / instance variables. That's exactly what the serialized form of the object keeps track of anyway.

  For example,
I allocate a TkCanvas in the initialize() method. Such an object can not
be loaded back properly, IMHO.

Some things aren't serializable in a sane way, that's just a fact of life.

I have one more problem with such an object: There's no destructor. Should
such an object get out of scope, how do I make sure the allocated canvas
is destroyed properly?

The garbage collector makes sure that the memory used by objects that go out of scope is reclaimed.

···

On Sep 4, 2006, at 1:30 PM, Josef Wolf wrote:

I'm sorry if this is a stupid question, but I am new to both, ruby and OO.
Please can somebody give an explanation how such an object is handled in
the ruby-world?

[ ... ]

AFAICS, the ruby way to store persistent data is YAML. Saving an
object with YAML.dump() works like a charm. But I have trouble to
read them back with YAML.load(). For some reason, the initialize()
method of the loaded object doesn't get called. I don't understand
how an object can properly spring in existance without the initialize
method?

Well the general purpose of initialize is to set up some state /
instance variables. That's exactly what the serialized form of the
object keeps track of anyway.

But what to do when an object needs some resources (this might be a
file/database opened, or, as in my case, a TkCanvas)?

For example, I allocate a TkCanvas in the initialize() method. Such
an object can not be loaded back properly, IMHO.

Some things aren't serializable in a sane way, that's just a fact of
life.

I understand that this is not serializable. But wouldn't it be good
to have a chance to reconstruct a new (identical) object from the reloaded
state/instance variables? It need not be the original initialize() method.
It would probably be even better would YAML invoke yaml_initialize() or
something.

I have one more problem with such an object: There's no destructor.
Should such an object get out of scope, how do I make sure the
allocated canvas is destroyed properly?

The garbage collector makes sure that the memory used by objects that
go out of scope is reclaimed.

It is clear to me that the ruby object is removed by the garbage
collector. But it is not clear what happens to other recources (a
TkCanvas in this case) allocated by this object. Check this out:

  #! /usr/bin/ruby

  require 'tk'

  root=TkRoot.new

  class Foo
    def initialize
      canvas = TkCanvas.new.pack
      rect = TkcRectangle.new(canvas, 0, 0, 50, 50, "fill"=>"white")
    end
  end

  begin
    x=Foo.new()
  end

  GC.start

  # Object destroyed but canvas and rectangle are still alive

  Tk.mainloop

I can't see a solution for this problem since I am a novice for ruby/OO/Tk.
How do you gurus handle such situations?

···

On Tue, Sep 05, 2006 at 07:10:28AM +0900, Logan Capaldo wrote:

That just means that there is still a reference to those objects. When all the references go away, then and only then ruby will collect them (if it needs to.)

···

On Sep 5, 2006, at 5:40 PM, Josef Wolf wrote:

  class Foo
    def initialize
      canvas = TkCanvas.new.pack
      rect = TkcRectangle.new(canvas, 0, 0, 50, 50, "fill"=>"white")
    end
  end

  begin
    x=Foo.new()
  end

  GC.start

  # Object destroyed but canvas and rectangle are still alive

  Tk.mainloop

I can't see a solution for this problem since I am a novice for ruby/OO/Tk.
How do you gurus handle such situations?

Josef Wolf wrote:

It is clear to me that the ruby object is removed by the garbage
collector. But it is not clear what happens to other recources (a
TkCanvas in this case) allocated by this object. Check this out:

  #! /usr/bin/ruby

  require 'tk'

  root=TkRoot.new

  class Foo
    def initialize
      canvas = TkCanvas.new.pack
      rect = TkcRectangle.new(canvas, 0, 0, 50, 50, "fill"=>"white")
    end
  end

  begin
    x=Foo.new()
  end

  GC.start

  # Object destroyed but canvas and rectangle are still alive

  Tk.mainloop

I can't see a solution for this problem since I am a novice for ruby/OO/Tk.
How do you gurus handle such situations?

There is a well-known Ruby idiom for coding block-scoped resources. There used to be a good page describing this in RubyGarden but alas that page seems to have disappeared. Anyway, here's the general example from that page:

> def Resource.open( identifier ) # :yield: resource
> resource = Resource.new( identifier )
> if block_given?
> begin
> yield resource
> ensure
> resource.close
> end
> else
> return resource
> end
> end

That is, create/open/whatever the resource is and yield to a block. Within the block use the resource. When the block returns, close/destroy the resource. The open method in the IO class is prototypical.

Exactly. The Tk library holds the references. To get rid of them, I
need to call canvas.delete(rect) for the rectangle and canvas.destroy
for the canvas. When I add those two calls, they disappear without
messing with the GC.

···

On Wed, Sep 06, 2006 at 07:10:18AM +0900, Logan Capaldo wrote:

On Sep 5, 2006, at 5:40 PM, Josef Wolf wrote:

> class Foo
> def initialize
> canvas = TkCanvas.new.pack
> rect = TkcRectangle.new(canvas, 0, 0, 50, 50, "fill"=>"white")
> end
> end
>
> begin
> x=Foo.new()
> end
>
> GC.start
>
> # Object destroyed but canvas and rectangle are still alive
>
> Tk.mainloop
>
>I can't see a solution for this problem since I am a novice for
>ruby/OO/Tk.
>How do you gurus handle such situations?

That just means that there is still a reference to those objects.
When all the references go away, then and only then ruby will collect
them (if it needs to.)

I am aware of this idiom. This idiom works very well if you have a strictly
hierachical call structure.

But I just can't see how this can help when dealing with Tk (especially
with canvas items). In this case, objects are created and destroyed
asynchronously. The block that creates a new widget has to return
_before_ the widget is destroyed.

Of course, with widgets like TkDialog, this idiom would work very well
since dialog->Show() blocks until the widget is destroyed. OTOH, TkDialog
don't need this idiom since it is already destroyed when its Show method
returns.

···

On Wed, Sep 06, 2006 at 07:17:46AM +0900, Timothy Hunter wrote:

Josef Wolf wrote:

There is a well-known Ruby idiom for coding block-scoped resources.
There used to be a good page describing this in RubyGarden but alas that
page seems to have disappeared. Anyway, here's the general example from
that page:

> def Resource.open( identifier ) # :yield: resource
> resource = Resource.new( identifier )
> if block_given?
> begin
> yield resource
> ensure
> resource.close
> end
> else
> return resource
> end
> end

That is, create/open/whatever the resource is and yield to a block.
Within the block use the resource. When the block returns, close/destroy
the resource. The open method in the IO class is prototypical.