Yaml object de-serialisation

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 ?

···

--
une bévue

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

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 ?

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 Vallner

Une bévue wrote:

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 ?

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.

···

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

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 ?

···

David Vallner <david@vallner.net> wrote:
--
une bévue

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

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 ?

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 Vallner

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 ?

···

David Vallner <david@vallner.net> 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.

--
une bévue

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

> 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.

···

David Vallner <david@vallner.net> wrote:

I'd never seen this before. Cool!

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

···

On 2/9/06, David Vallner <david@vallner.net> wrote:

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.

--
R. Mark Volkmann
Partner, Object Computing, Inc.

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.

>
> 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.

OK, thanks for all.

···

David Vallner <david@vallner.net> wrote:
--
une bévue

Dňa Štvrtok 09 Február 2006 22:12 Mark Volkmann 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 (tm).

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 Vallner