Question on IO objects, initialize, and yaml

Say I have a class with pseudo code as follows:
class Test
def initialize
  @a=1
  @b=File.open("file+Time.now or similar")
end

Now, imagine that I use this class for a while and then I want to dump it to
a YAML file.

When I restart my code, I want it to read in stuff from the YAML file (if it
exists) and use that as a starting point to work from (basically, I am
saving state in the yaml file). You'll note that I cannot just do this as
is, because reading in from YAML does not initialize the class, so @b would
not be initialized correctly on restart.

I'd like to just initialize the thing, then read in the YAML file to
overwrite the state with the saved values. Is there some extremely obvious
ruby way to do this or something equivalent?

Thanks,
mark

Say I have a class with pseudo code as follows:
class Test
def initialize
@a=1
@b=File.open("file+Time.now or similar")
end

Now, imagine that I use this class for a while and then I want to dump it to
a YAML file.

When I restart my code, I want it to read in stuff from the YAML file (if it
exists) and use that as a starting point to work from (basically, I am
saving state in the yaml file). You'll note that I cannot just do this as
is, because reading in from YAML does not initialize the class, so @b would
not be initialized correctly on restart.

Ruby and YAML provide hooks to cause things to happen when you load from Marshal or YAML. I think #initialize_copy gets called for Marshal, I don't know the YAML ones, but they should be easy to find.

I'd like to just initialize the thing, then read in the YAML file to
overwrite the state with the saved values. Is there some extremely obvious
ruby way to do this or something equivalent?

Don't do this, use the built-in hooks, that's what they're for.

···

On Dec 10, 2006, at 12:19 , Mark Noworolski wrote:

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net

I LIT YOUR GEM ON FIRE!

Mark Noworolski wrote:

Say I have a class with pseudo code as follows:
class Test
def initialize
@a=1
@b=File.open("file+Time.now or similar")
end

Now, imagine that I use this class for a while and then I want to dump it to
a YAML file.

When I restart my code, I want it to read in stuff from the YAML file (if it
exists) and use that as a starting point to work from (basically, I am
saving state in the yaml file). You'll note that I cannot just do this as
is, because reading in from YAML does not initialize the class, so @b would
not be initialized correctly on restart.

I'd like to just initialize the thing, then read in the YAML file to
overwrite the state with the saved values. Is there some extremely obvious
ruby way to do this or something equivalent?

A possible alternative is to be lazy about constructing the non-dumpable stuff:

[~] cat test.rb
require 'yaml'

class Test
   def initialize
     @a = 1
   end
   def b
     @b ||= File.open(__FILE__) # just for testing
   end
end

t = Test.new
t2 = YAML.load(YAML.dump(t))

line = t2.b.gets
p line

[~] ruby test.rb
"require 'yaml'\n

If you do this, other parts of your code shouldn't access @b directly. Instead, call the #b method.

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

I _want_ to use a built-in hook (the ruby way, if you will), if I can figure
out which one and how to use it.

Based on your suggestion, I've googled and found this code:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/104402

Now, I've experimented with initialize_copy on this code and your
recommendation, and I cannot get it to do what I want.

Anybody got any other suggestions, or a concrete example for me?
mark

···

On 12/10/06, Eric Hodel <drbrain@segment7.net> wrote:

On Dec 10, 2006, at 12:19 , Mark Noworolski wrote:

> Say I have a class with pseudo code as follows:
> class Test
> def initialize
> @a=1
> @b=File.open("file+Time.now or similar")
> end
>
> Now, imagine that I use this class for a while and then I want to
> dump it to
> a YAML file.
>
> When I restart my code, I want it to read in stuff from the YAML
> file (if it
> exists) and use that as a starting point to work from (basically, I am
> saving state in the yaml file). You'll note that I cannot just do
> this as
> is, because reading in from YAML does not initialize the class, so
> @b would
> not be initialized correctly on restart.

Ruby and YAML provide hooks to cause things to happen when you load
from Marshal or YAML. I think #initialize_copy gets called for
Marshal, I don't know the YAML ones, but they should be easy to find.

> I'd like to just initialize the thing, then read in the YAML file to
> overwrite the state with the saved values. Is there some extremely
> obvious
> ruby way to do this or something equivalent?

Don't do this, use the built-in hooks, that's what they're for.

--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net

I LIT YOUR GEM ON FIRE!

Joel VanderWerf wrote:

Mark Noworolski wrote:

Say I have a class with pseudo code as follows:
class Test
def initialize
@a=1
@b=File.open("file+Time.now or similar")
end

Now, imagine that I use this class for a while and then I want to dump it to
a YAML file.

When I restart my code, I want it to read in stuff from the YAML file (if it
exists) and use that as a starting point to work from (basically, I am
saving state in the yaml file). You'll note that I cannot just do this as
is, because reading in from YAML does not initialize the class, so @b would
not be initialized correctly on restart.

I'd like to just initialize the thing, then read in the YAML file to
overwrite the state with the saved values. Is there some extremely obvious
ruby way to do this or something equivalent?

A possible alternative is to be lazy about constructing the non-dumpable stuff:

[~] cat test.rb
require 'yaml'

class Test
  def initialize
    @a = 1
  end
  def b
    @b ||= File.open(__FILE__) # just for testing
  end
end

t = Test.new
t2 = YAML.load(YAML.dump(t))

line = t2.b.gets
p line

[~] ruby test.rb
"require 'yaml'\n

If you do this, other parts of your code shouldn't access @b directly. Instead, call the #b method.

That's not quite right. If you use #b and then dump/load and try to use #b on the new object, you get an error:

   t = Test.new

   t2 = YAML.load(YAML.dump(t))
   line = t2.b.gets
   p line

   t3 = YAML.load(YAML.dump(t2))
   line = t3.b.gets
   p line

Output:

   "require 'yaml'\n"
   test.rb:23:in `gets': uninitialized stream (IOError)
           from test.rb:23

You can "fix" this by adding a #to_yaml method to your class that clears @b:

   class Test
     def to_yaml(*)
       @b = nil
       super
     end
   end

Then t3.b.gets doesn't fail. But whether this "fix" is right for your class is another question...

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

>
>Ruby and YAML provide hooks to cause things to happen when you load
>from Marshal or YAML. I think #initialize_copy gets called for
>Marshal, I don't know the YAML ones, but they should be easy to find.
>
I _want_ to use a built-in hook (the ruby way, if you will), if I can figure
out which one and how to use it.

[...]

Now, I've experimented with initialize_copy on this code and your
recommendation, and I cannot get it to do what I want.

Anybody got any other suggestions, or a concrete example for me?
mark

require 'yaml'
class Foo
  def initialize
    @foo = 1
    @bar = 2
  end

  def yaml_initialize(tag, val)
  end
end

foo = Foo.new
foo # => #<Foo:0xa7dfe614 @bar=2, @foo=1>
YAML.load(YAML.dump(foo)) # => #<Foo:0xa7dfa6e0>

class Foo
  def yaml_initialize(tag, val) # !> method redefined; discarding old yaml_initialize
     @foo, @bar = val.values_at("foo", "bar")
  end
end

YAML.load(YAML.dump(foo)) # => #<Foo:0xa7ddcfc8 @bar=2, @foo=1>

···

On Mon, Dec 11, 2006 at 02:43:22PM +0900, Mark Noworolski wrote:

On 12/10/06, Eric Hodel <drbrain@segment7.net> wrote:

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby