Forum: Ruby Persisting and loading objects trhough Marshal problem

Posted by Damián M. González (igorjorobus)
on 2013-03-06 15:32
Hello people. Today I've found a bug in my source code, been around for
a while until I realized of the problem, here is a bit of code which
represent it:

 *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

 class A
  def initialize
   @var_a = 50
  end
 end

 class B
  attr_accessor :var_b
  def initialize(value)
   @var_b = value
  end
 end

 a = A.new
 b = B.new(a)

 #I 'marshal' both of the variables
 #Then close the app, open it and load again their state, so I expect
 #that the next snippet is true:

 p b.var_b.==(a)
 #=> false

 #They are differents objects and I don't need that.

 *-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

 So how do you think that I can resolve this problem? Been thinking for
a solution but I'm not sure about...
 I'll appreciate your help, thank you very much. Damián.
Posted by Hans Mackowiak (hanmac)
on 2013-03-06 15:51
how do you marshal them? if you marshal them in ONE step it can solve 
your circle
Posted by Damián M. González (igorjorobus)
on 2013-03-06 16:57
Good question. See, I marshal them in differents times. Because I don't
have to persist lot of information I don't use any database like MySQL,
I just save the object in binary files(by marshaling). The application
have a GUI, so when a user do some thing certain object is persisted,
that object is pointing to other objects, like the example of @var_b.
The problem is that when I regenerate the objects, the shown above
happen.
 When in some part of the source code I have to compare in the way above
the are not equals:

 b.var_b.inspect
 #=> <A:0x0145A4>
 a.inspect
 #=> <A:0x457D78>

 They are not the same, perhaps(meaby an obvious thing) because Marshal
reconstruct the object just by looking their atributes saved but Marshal
doesn't bother about create the same object that another that will be
created with the same attributes. Am I right?

 I have to add that I persist them in differents files. I've 
realized(meaby) what you mean: You mean persist both object in ONE step 
like put them into an #Array and then persist the Array? Well that meaby 
can solve the problem, but the meaning of persist the object in 
different steps and differents files is that isn't the same persist 1K 
of information(one object) than persist the whole model, like 50 objects 
at the same time. I didn't see the sense of that because the application 
is just changing one object when the user do a certain movement in the 
GUI, not the 50, so I didn't see the sense in save the whole model.
Posted by Robert Klemme (robert_k78)
on 2013-03-06 17:33
(Received via mailing list)
On Wed, Mar 6, 2013 at 4:58 PM, Damin M. Gonzlez <lists@ruby-forum.com> 
wrote:
>  b.var_b.inspect
>  #=> <A:0x0145A4>
>  a.inspect
>  #=> <A:0x457D78>
>
>  They are not the same, perhaps(meaby an obvious thing) because Marshal
> reconstruct the object just by looking their atributes saved but Marshal
> doesn't bother about create the same object that another that will be
> created with the same attributes. Am I right?

Marshal retains an idea of identity only during _one_ write operation.
 So it will reconstruct graphs properly if you ensure all interesting
instances are referenced from a single instance.

As Hans said: do it in one step for example by placing both in an Array:

irb(main):016:0> x, y = Marshal.load(Marshal.dump([a,b]))
=> [#<A:0x802ba284 @var_a=50>, #<B:0x802ba248 @var_b=#<A:0x802ba284 
@var_a=50>>]
irb(main):017:0> x.equal? y.var_b
=> true

In your case it would also be sufficient to only marshal b because
that references a already and there is an easy way to access it after
deserialization.

Kind regards

robert
Posted by Damián M. González (igorjorobus)
on 2013-03-06 18:35
> Marshal retains an idea of identity only during _one_ write operation.
>  So it will reconstruct graphs properly if you ensure all interesting
> instances are referenced from a single instance.

 I can now clearly see what you say.

> As Hans said: do it in one step for example by placing both in an Array:
>
> irb(main):016:0> x, y = Marshal.load(Marshal.dump([a,b]))
> => [#<A:0x802ba284 @var_a=50>, #<B:0x802ba248 @var_b=#<A:0x802ba284
> @var_a=50>>]
> irb(main):017:0> x.equal? y.var_b
> => true

 Yes, it's what I say above

> In your case it would also be sufficient to only marshal b because
> that references a already and there is an easy way to access it after
> deserialization.

 I understand what you say, but my model is far more complex, I have
many objects with atributtes pointing to anothers objects from
differents clases, it's not so simple. At now the only way that I see
is: in some way try to reconstruct the objects manually, for example,
many of my classes will have only one instance at time in the model, so
I can do something like this(referencing the above example):

-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

 #supposing that before I've loaded the state of ::A1 which is the state
 #of the above a variable

 #monkeypatch
 class B
  def marshal_dump
   [@var_b]
  end

  #Note: I have all the only-one-instance-per-class pointed by a
  #constant in the main scope(don't know if this is a good way just
  #implemented in that way))
  def marshal_load(attributes)
   @var_b = (if attributes[0].is_a?(A) then ::A1 else attributes[0] end)
  end
 end

-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

 Do you think that exist a better and faster way than this to treat with
the problem? I'll appreciate your help, thanks.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.