Yaml object de-serialisation


#1

i’ve a class “Preferences”
which includes other classes for example “Theme”

i save an instance of “Preferences” by writing self.to_yaml into a file.

obviously, i want to retrive this instance back by :
o=YAML::load(File.open("#{PREFS_FILE}"))

PREFS_FILE being the preceedingly saved one.

what’s the best way to “re-populate” my classes ?

for the time being i’m doing :

    o.themes_list.each { |theme|
      t=Theme.new(theme.label)
      t.nb_cols_list=theme.nb_cols_list
      @themes_list << t
    }

[…]
@theme_label=o.theme_label
@nb_cols=o.nb_cols

does exist a more direct way to do the samething ?


#2

DÅ?a Å tvrtok 09 Február 2006 12:48 Une bévue napísal:

what’s the best way to “re-populate” my classes ?
@nb_cols=o.nb_cols

does exist a more direct way to do the samething ?

I thought YAML does deep serialization? It should load this
automatically.

What do you mean by Preferences including Themes? If you mean instances
of
Theme are instance variables of Preferences, or are stored in an Array
in
Preferences, you shouldn’t need to repopulate anything.

I didn’t quite understand the intent of your code, but it looks like
you’re
doing copies of themes from o.themes_list to another Array
@themes_list
Why can’t you keep them in the original object “o” and access them from
there?

David V.


#3

David V. removed_email_address@domain.invalid wrote:

First, thanks for your reply ))

What do you mean by Preferences including Themes? If you mean instances of
Theme are instance variables of Preferences, or are stored in an Array in
Preferences, you shouldn’t need to repopulate anything.

yes i have another class Theme and Preferences class includes an array
of instances of Theme.

I didn’t quite understand the intent of your code, but it looks like you’re
doing copies of themes from o.themes_list to another Array @themes_list
Why can’t you keep them in the original object “o” and access them from
there?

ok, i see xhat you mean.

three cases :

  • first launch of this app whithout any prefs saved:

i create a default prefs by :

prefs=Preferences.new blahblahblah

prefs.save #ie to a yaml file

  • not first launch of this app with an instance of Preferences saved :

prefs=YAML::load(File.open("#{PREFS_FILE}"))

that’s all

now the third case i’ve an old way to save prefs into a yaml file,
basically a hash instead of an instance of Preferences

in that case i only have to add another method to the Preferences class,
saying :

prefs=Preferences.new
prefs.updateFromOlderFileStructure

prefs.save

again, that’s all.

am i right ?


#4

DÅ?a Å tvrtok 09 Február 2006 14:38 Une bévue napísal:

prefs.save

again, that’s all.

am i right ?

Ah, this third case wasn’t quite obvious from your example. But yes,
you’re
stuck to adapting the old preference structure to the new more or less
by
hand. This doesn’t seem like a particularly error prone piece of code,
so I’d
stick with whatever seems to work for now, direct or indirect. As long
as it
works…

You could possibly golf down your script by having the new Theme object
take
as constructor parameters the whole old corresponding object, but I
don’t
quite like this sort of coupling of compatibility code in the main
logic.

David V.


#5

Une bévue wrote:

what’s the best way to “re-populate” my classes ?
@nb_cols=o.nb_cols

does exist a more direct way to do the samething ?

This may be helpful, or at least interesting:

http://raa.ruby-lang.org/project/preferences/
http://redshift.sourceforge.net/preferences/doc/index.html

It’s a way of automating the configuration of objects from a
hierarchical hash stored in a YAML file. It works with existing
accessors, so it’s very easy to use with a GUI toolkit. See
examples/foursplit-prefs.rb for a version of the FXRuby foursplit.rb
example, but using Preferences.


#6

David V. removed_email_address@domain.invalid wrote:

You could possibly golf down your script by having the new Theme object take
as constructor parameters the whole old corresponding object, but I don’t
quite like this sort of coupling of compatibility code in the main logic.

that’s a “small” prob to me, i’ve used java where i might have multiple
constructors…

now to workaround i have build an initialize which returns all of its
attributes to nil or the like ([] in case of arrays)

a defaults method which populate de prefs with default values

an updateFromHash(o) which updates prefs from older structure.

suppose now, in the live of this app, i’ll add some new attributes to
the class Preferences, what is the behaviour of yaml in that case ?

i suppose the new attributes (if an instance of Preferences is loaded
from older attributes list) will be nill ?


#7

DÅ?a Å tvrtok 09 Február 2006 15:33 Une bévue napísal:

David V. removed_email_address@domain.invalid wrote:

You could possibly golf down your script by having the new Theme object
take as constructor parameters the whole old corresponding object, but I
don’t quite like this sort of coupling of compatibility code in the main
logic.

that’s a “small” prob to me, i’ve used java where i might have multiple
constructors…

Constructors are just cleverly disguised initializers. Have #initialize
only
do the completely common code, and then explicitly call other
initializer
methods you define if you want this pattern. I personally consider
method
overloading a slight misfeature of the C++ language family, and have
grown
quite accustomed to using the more flexible “options hash” pattern
instead.
For example, if you have some class with instance variables bar, baz,
and
quux:

class Foo
	DEFAULTS = {
		:bar => 1,
		:baz => 2,
		:quux => 3
	}

	attr :bar
	attr :baz
	attr :quux

	def initialize(params)
		attribs = DEFAULTS.dup.update(params)
		@bar = attribs[:bar]
		@baz = attribs[:baz]
		@quux = attribs[:quux]
	end
end

foo = Foo.new(:bar = "Hello", :quux => "World")

p foo # Outputs #<Foo:0xb7c8004c @bar="Hello", @quux="World", @baz=2>

I find this covers 90% of what you commonly use overloaded constructors
for,
and is a bit more readable too.

now to workaround i have build an initialize which returns all of its
attributes to nil or the like ([] in case of arrays)

a defaults method which populate de prefs with default values

an updateFromHash(o) which updates prefs from older structure.

If the above pattern doesn’t cover what you need, you can always create
more
factory methods to crunch for example the old structure into one the
constructor will like better.

suppose now, in the live of this app, i’ll add some new attributes to
the class Preferences, what is the behaviour of yaml in that case ?

i suppose the new attributes (if an instance of Preferences is loaded
from older attributes list) will be nill ?

Yes, the YAML loader doesn’t know anything about what instance
attributes the
object is supposed to have. AFAIK, it uses Object::allocate to create a
blank
instance of the class, then populates the instance variables via
Object#instance_variable_set, or something equivalent.


#8

On 2/9/06, David V. removed_email_address@domain.invalid wrote:

            }
            end
    end

    foo = Foo.new(:bar = "Hello", :quux => "World")

    p foo # Outputs #<Foo:0xb7c8004c @bar="Hello", @quux="World", @baz=2>

I find this covers 90% of what you commonly use overloaded constructors for,
and is a bit more readable too.

I’d never seen this before. Cool!

I think you can use
attribs = DEFAULTS.merge(param)
instead of
attribs = DEFAULTS.dup.update(params)


#9

David V. removed_email_address@domain.invalid wrote:

If the above pattern doesn’t cover what you need, you can always create more
factory methods to crunch for example the old structure into one the
constructor will like better.

yes, that’s another solution. I’ll think about your above solution
(options hash) because i don’t like the way i’ve done that actually
mostly because, i, my case, the #initialize is more orl es a fake
initialise : it returns somehow an empty object.

Object#instance_variable_set, or something equivalent.
OK, thanks for all.


#10

DÅ?a Å tvrtok 09 Február 2006 22:12 Mark V. napísal:

I’d never seen this before. Cool!

I saw this on some page about ruby idioms somewhere. Might have been the
RubyGarden one. Or the RAA library interface design guidelines. I think
those
are very roughly a ripoff from Perl’s, and I don’t necessarily like
them, and
I didn’t like this idiom at first. But then I saw this other snippet
where
the default parameters were used and suddenly it made Perfect Sense
™.

This is used all over the place in Rails, and the options hash is useful
outside constructors too. I find it generally both more readable and
comfortable to use than having multiple methods accept different
combinations
of parameters, especially since you get the perks of keyword arguments
(arbitrary argument order) along. You might want to check for typos by
being
strict about what keys / combinations of keys you accept in the options
hash,
and convert the keys to symbols or vice versa depending on what you
except.

I think you can use
attribs = DEFAULTS.merge(param)
instead of
attribs = DEFAULTS.dup.update(params)

bangs head against wall

I should really, really start reading ri output better. I knew about
Hash#update, but I didn’t actually go on and notice it’s a synonym for
Hash#merge! when I checked ri. D’oh!

David V.