[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

yaml object de-serialisation

pere.noel

2/9/2006 11:46:00 AM

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
9 Answers

David Vallner

2/9/2006 1:20:00 PM

0

Dna Š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


pere.noel

2/9/2006 1:36:00 PM

0

David Vallner <david@vallner.net> 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 ?
--
une bévue

David Vallner

2/9/2006 2:08:00 PM

0

Dna Š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


pere.noel

2/9/2006 2:28:00 PM

0

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.

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 ?
--
une bévue

David Vallner

2/9/2006 8:15:00 PM

0

Dna Štvrtok 09 Február 2006 15:33 Une bévue napísal:
> 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.
>
> 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.


Joel VanderWerf

2/9/2006 8:44:00 PM

0

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

Mark Volkmann

2/9/2006 9:13:00 PM

0

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.

I'd never seen this before. Cool!

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

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


pere.noel

2/9/2006 11:55:00 PM

0

David Vallner <david@vallner.net> 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.
> >
> > 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.
--
une bévue

David Vallner

2/10/2006 1:45:00 AM

0

Dna Š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