Doing fixups after deserializing YAML

Hi,
Suppose (for reasons best known to myself) I had the following code:

require 'yaml'

class Squarer
  attr_reader :num, :num_squared

  def initialize(num)
    @num = num
    @num_squared = num * num
  end

  def to_yaml_properties
    # no need to store both num and num_squared...
    %w{ @num }
  end
end

s = Squarer.new(42)
yaml_s = YAML.dump(s)

new_s = YAML.load(yaml_s)
puts "new_s.num = #{new_s.num}"
puts "new_s.num_squared = #{new_s.num_squared}"

Is there any way I can hook up a bit of fixup code to set @num_squared appropriately after deserializing from YAML? If I were using Marshal, I'd just override the marshal_load method - what (if anything) is the YAML equivalent?

- Matthew

Well this solution doesn't involve yaml but...

class Squarer
         def num_squared
              @num_squared ||= @num * @num
         end
end

So it will calculate it only the first time you ask for it.

···

On 5/20/05, Matthew Westcott <gasman@raww.org> wrote:

Hi,
Suppose (for reasons best known to myself) I had the following code:

require 'yaml'

class Squarer
        attr_reader :num, :num_squared

        def initialize(num)
                @num = num
                @num_squared = num * num
        end

        def to_yaml_properties
                # no need to store both num and num_squared...
                %w{ @num }
        end
end

s = Squarer.new(42)
yaml_s = YAML.dump(s)

new_s = YAML.load(yaml_s)
puts "new_s.num = #{new_s.num}"
puts "new_s.num_squared = #{new_s.num_squared}"

Is there any way I can hook up a bit of fixup code to set @num_squared
appropriately after deserializing from YAML? If I were using Marshal,
I'd just override the marshal_load method - what (if anything) is the
YAML equivalent?

- Matthew

Logan Capaldo wrote:

class Squarer
       attr_reader :num, :num_squared

       def initialize(num)
               @num = num
               @num_squared = num * num
       end
end

[serialising / deserialising a Squarer object to YAML without writing the @num_squared variable]

Well this solution doesn't involve yaml but...

class Squarer
         def num_squared
              @num_squared ||= @num * @num
         end
end

So it will calculate it only the first time you ask for it.

Hi,
Thanks very much for the suggestion - as you might expect, though, the example I gave was drastically simplified from the real-life problem I was stuck on. In that situation, it just wasn't feasible to eliminate all redundant internal state from the object itself - but I rather wanted to eliminate it from the object's YAML representation, at least.

In the end I found the bit of documentation I was after <http://yaml4r.sourceforge.net/doc/page/type_families.htm&gt; , and arrived at this solution:

require 'yaml'

class Squarer
  attr_reader :num, :num_squared

  def initialize(num)
    @num = num
    @num_squared = num * num
  end

  def to_yaml_type
    # register this as a custom type
    "!west.co.tt,2005-05-24/squarer"
  end

  def to_yaml_properties
    %w{ @num }
  end
end

# tell the YAML parser how to deal with an object of this type
YAML.add_domain_type( "west.co.tt,2005-05-24", "squarer" ) { |type, val|
  # val is a hash of all the fields we serialised (i.e. just @num)
  Squarer.new(val['num'])

  # here we'd do any more initialisation of the object
  # if necessary
}

···

On 5/20/05, Matthew Westcott <gasman@raww.org> wrote: