Nick Hoffman
2009-Jan-27 19:48 UTC
[rspec-users] [RSpec] Cloning objects and leaking state
G''day folks. I''ve been beating my head on this one problem for a couple of hours, and have managed to figure out what''s causing it. However, I don''t understand why it''s happening, nor do I know how to solve or get around it. One of my methods clones an arg, and it seems that doing so causes state to leak out. I discovered this because changing this: new_subtitle = subtitle.clone to this: new_subtitle = subtitle causes the problem to disappear. Before we get into the code snippets, are there any known caveats, warnings, or problems with writing specs that cover the cloning or duplication of objects? Here''s the code, specs, and spec output: http://gist.github.com/53482 As you can see, in the "should clone ..." example, the expectation on line 24 succeeds, but the same expectation on line 28 fails. After that, the same expectation fails in the "should not leak state" example. Any idea what''s going on? I''d appreciate any help at all. Thanks, Nick
David Chelimsky
2009-Jan-28 00:44 UTC
[rspec-users] [RSpec] Cloning objects and leaking state
On Tue, Jan 27, 2009 at 1:48 PM, Nick Hoffman <nick at deadorange.com> wrote:> G''day folks. I''ve been beating my head on this one problem for a couple of > hours, and have managed to figure out what''s causing it. However, I don''t > understand why it''s happening, nor do I know how to solve or get around it. > > One of my methods clones an arg, and it seems that doing so causes state to > leak out. I discovered this because changing this: > new_subtitle = subtitle.clone > to this: > new_subtitle = subtitle > causes the problem to disappear. > > Before we get into the code snippets, are there any known caveats, warnings, > or problems with writing specs that cover the cloning or duplication of > objects? > > Here''s the code, specs, and spec output: > http://gist.github.com/53482 > > As you can see, in the "should clone ..." example, the expectation on line > 24 succeeds, but the same expectation on line 28 fails. After that, the same > expectation fails in the "should not leak state" example.Would you please try doing the same thing without using the clone method specifically. i.e. change the method name in the spec to :copy and then call that in the subject code as well and see if you come up w/ different results. I want to make sure this is really about the clone method itself.> > Any idea what''s going on? I''d appreciate any help at all. Thanks, > Nick > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
Nick Hoffman
2009-Jan-28 03:52 UTC
[rspec-users] [RSpec] Cloning objects and leaking state
On 27/01/2009, at 7:44 PM, David Chelimsky wrote:> On Tue, Jan 27, 2009 at 1:48 PM, Nick Hoffman <nick at deadorange.com> > wrote: >> G''day folks. I''ve been beating my head on this one problem for a >> couple of >> hours, and have managed to figure out what''s causing it. However, I >> don''t >> understand why it''s happening, nor do I know how to solve or get >> around it. >> >> One of my methods clones an arg, and it seems that doing so causes >> state to >> leak out. I discovered this because changing this: >> new_subtitle = subtitle.clone >> to this: >> new_subtitle = subtitle >> causes the problem to disappear. >> >> Before we get into the code snippets, are there any known caveats, >> warnings, >> or problems with writing specs that cover the cloning or >> duplication of >> objects? >> >> Here''s the code, specs, and spec output: >> http://gist.github.com/53482 >> >> As you can see, in the "should clone ..." example, the expectation >> on line >> 24 succeeds, but the same expectation on line 28 fails. After that, >> the same >> expectation fails in the "should not leak state" example. > > Would you please try doing the same thing without using the clone > method specifically. i.e. change the method name in the spec to :copy > and then call that in the subject code as well and see if you come up > w/ different results. I want to make sure this is really about the > clone method itself.I removed the call to #clone from the instance method, and lo and behold, the problem persists: http://tinyurl.com/cl8rvy I don''t know why, because when I posted the first email in this thread, this seemed to resolve it. I also tried replacing #clone with #dup, and the problem still occurs: http://tinyurl.com/dzuvrd Now I''m even more flummoxed. If the #clone isn''t causing this problem, what is? Can I do anything else to help others help me with this? Thanks, guys! Nick
On Tue, Jan 27, 2009 at 2:48 PM, Nick Hoffman <nick at deadorange.com> wrote:> G''day folks. I''ve been beating my head on this one problem for a couple of > hours, and have managed to figure out what''s causing it. However, I don''t > understand why it''s happening, nor do I know how to solve or get around it. > > One of my methods clones an arg, and it seems that doing so causes state to > leak out. I discovered this because changing this: > new_subtitle = subtitle.clone > to this: > new_subtitle = subtitle > causes the problem to disappear. > > Before we get into the code snippets, are there any known caveats, warnings, > or problems with writing specs that cover the cloning or duplication of > objects? > > Here''s the code, specs, and spec output: > http://gist.github.com/53482 > > As you can see, in the "should clone ..." example, the expectation on line > 24 succeeds, but the same expectation on line 28 fails. After that, the same > expectation fails in the "should not leak state" example.What does your full SubtitleFile class look like? It looks like there is an issue with how the #subtitles method is storing information. Since you''re using ActiveRecord::BaseWithoutTable there are probably not going to be any unique primary keys amongst your SubtitleFiles to help differentiate them. So if #subtitles is a has_many, every call to SubtitleFile#subtitles may lookup Subtitles in the exact same way w/o any unique key to use to differentiate which subtitles belong to which subtitle files which would produce the results you are seeing. Is Subtitle a ActiveRecord::BaseWithoutTable or a real ActiveRecord::Base model?> > Any idea what''s going on? I''d appreciate any help at all. Thanks, > Nick > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- Zach Dennis http://www.continuousthinking.com http://www.mutuallyhuman.com
Nick Hoffman
2009-Jan-28 16:30 UTC
[rspec-users] [RSpec] Cloning objects and leaking state
On 28/01/2009, at 7:50 AM, Zach Dennis wrote:> On Tue, Jan 27, 2009 at 2:48 PM, Nick Hoffman <nick at deadorange.com> > wrote: >> G''day folks. I''ve been beating my head on this one problem for a >> couple of >> hours, and have managed to figure out what''s causing it. However, I >> don''t >> understand why it''s happening, nor do I know how to solve or get >> around it. >> >> One of my methods clones an arg, and it seems that doing so causes >> state to >> leak out. I discovered this because changing this: >> new_subtitle = subtitle.clone >> to this: >> new_subtitle = subtitle >> causes the problem to disappear. >> >> Before we get into the code snippets, are there any known caveats, >> warnings, >> or problems with writing specs that cover the cloning or >> duplication of >> objects? >> >> Here''s the code, specs, and spec output: >> http://gist.github.com/53482 >> >> As you can see, in the "should clone ..." example, the expectation >> on line >> 24 succeeds, but the same expectation on line 28 fails. After that, >> the same >> expectation fails in the "should not leak state" example. > > What does your full SubtitleFile class look like? It looks like there > is an issue with how the #subtitles method is storing information. > > Since you''re using ActiveRecord::BaseWithoutTable there are probably > not going to be any unique primary keys amongst your SubtitleFiles to > help differentiate them. So if #subtitles is a has_many, every call to > SubtitleFile#subtitles may lookup Subtitles in the exact same way w/o > any unique key to use to differentiate which subtitles belong to which > subtitle files which would produce the results you are seeing. > > Is Subtitle a ActiveRecord::BaseWithoutTable or a real > ActiveRecord::Base model?Hi Zach. Subtitle is an ActiveRecord::BaseWithoutTable model; it does not inherit from ActiveRecord::Base . The SubtitleFile "subtitles" attribute is actually just an Array. I didn''t bother to setup a has_many relationship with Subtitle. SubtitleFile#subtitles is a standard getter method that ActiveRecord sets up for me. In my app''s console, I tested out a has_many relationship between two ActiveRecord::BaseWithoutTable models, and there weren''t any mixups after creating associations between different model instances. Here''s the source for the SubtitleFile model. The #add_subtitle! in question starts on line 74: http://gist.github.com/54028 Thanks, mate. Nick
On Wed, Jan 28, 2009 at 8:30 AM, Nick Hoffman <nick at deadorange.com> wrote:> On 28/01/2009, at 7:50 AM, Zach Dennis wrote: >> >> On Tue, Jan 27, 2009 at 2:48 PM, Nick Hoffman <nick at deadorange.com> wrote: >>> >>> G''day folks. I''ve been beating my head on this one problem for a couple >>> of >>> hours, and have managed to figure out what''s causing it. However, I don''t >>> understand why it''s happening, nor do I know how to solve or get around >>> it. >>> >>> One of my methods clones an arg, and it seems that doing so causes state >>> to >>> leak out. I discovered this because changing this: >>> new_subtitle = subtitle.clone >>> to this: >>> new_subtitle = subtitle >>> causes the problem to disappear. >>> >>> Before we get into the code snippets, are there any known caveats, >>> warnings, >>> or problems with writing specs that cover the cloning or duplication of >>> objects? >>> >>> Here''s the code, specs, and spec output: >>> http://gist.github.com/53482 >>> >>> As you can see, in the "should clone ..." example, the expectation on >>> line >>> 24 succeeds, but the same expectation on line 28 fails. After that, the >>> same >>> expectation fails in the "should not leak state" example. >> >> What does your full SubtitleFile class look like? It looks like there >> is an issue with how the #subtitles method is storing information. >> >> Since you''re using ActiveRecord::BaseWithoutTable there are probably >> not going to be any unique primary keys amongst your SubtitleFiles to >> help differentiate them. So if #subtitles is a has_many, every call to >> SubtitleFile#subtitles may lookup Subtitles in the exact same way w/o >> any unique key to use to differentiate which subtitles belong to which >> subtitle files which would produce the results you are seeing. >> >> Is Subtitle a ActiveRecord::BaseWithoutTable or a real ActiveRecord::Base >> model? > > Hi Zach. Subtitle is an ActiveRecord::BaseWithoutTable model; it does not > inherit from ActiveRecord::Base . > > The SubtitleFile "subtitles" attribute is actually just an Array. I didn''t > bother to setup a has_many relationship with Subtitle. > SubtitleFile#subtitles is a standard getter method that ActiveRecord sets up > for me. > > In my app''s console, I tested out a has_many relationship between two > ActiveRecord::BaseWithoutTable models, and there weren''t any mixups after > creating associations between different model instances. > > Here''s the source for the SubtitleFile model. The #add_subtitle! in question > starts on line 74: > http://gist.github.com/54028I''m not familiar with this library, so I can''t say for sure, but the line default :subtitles => [] looks very suspicious to me. The underlying library code would have to do a .clone of that default value, otherwise all instances will share the same array (meaning if you push a value onto it, the next instance will have that array with one element). If it doesn''t automatically clone (it may, I don''t know), it probably should provide a lambda syntax so you can do: default :subtitles => lambda { [] } Pat
> If it doesn''t automatically clone (it may, I don''t know), it probably > should provide a lambda syntax so you can do: > default :subtitles => lambda { [] }Yeah, I just looked at the code for ARD and this is the problem. You can pass in immutable objects and they don''t get affected for obvious reasons. But with a mutable object like an array, you need to pass it a lambda so that it''s creating a new instance each time. Pat
Nick Hoffman
2009-Jan-28 17:41 UTC
[rspec-users] [RSpec] Cloning objects and leaking state
On 28/01/2009, at 12:12 PM, Pat Maddox wrote:> On Wed, Jan 28, 2009 at 8:30 AM, Nick Hoffman <nick at deadorange.com> > wrote: >> On 28/01/2009, at 7:50 AM, Zach Dennis wrote: >>> >>> On Tue, Jan 27, 2009 at 2:48 PM, Nick Hoffman >>> <nick at deadorange.com> wrote: >>>> >>>> G''day folks. I''ve been beating my head on this one problem for a >>>> couple >>>> of >>>> hours, and have managed to figure out what''s causing it. However, >>>> I don''t >>>> understand why it''s happening, nor do I know how to solve or get >>>> around >>>> it. >>>> >>>> One of my methods clones an arg, and it seems that doing so >>>> causes state >>>> to >>>> leak out. I discovered this because changing this: >>>> new_subtitle = subtitle.clone >>>> to this: >>>> new_subtitle = subtitle >>>> causes the problem to disappear. >>>> >>>> Before we get into the code snippets, are there any known caveats, >>>> warnings, >>>> or problems with writing specs that cover the cloning or >>>> duplication of >>>> objects? >>>> >>>> Here''s the code, specs, and spec output: >>>> http://gist.github.com/53482 >>>> >>>> As you can see, in the "should clone ..." example, the >>>> expectation on >>>> line >>>> 24 succeeds, but the same expectation on line 28 fails. After >>>> that, the >>>> same >>>> expectation fails in the "should not leak state" example. >>> >>> What does your full SubtitleFile class look like? It looks like >>> there >>> is an issue with how the #subtitles method is storing information. >>> >>> Since you''re using ActiveRecord::BaseWithoutTable there are probably >>> not going to be any unique primary keys amongst your SubtitleFiles >>> to >>> help differentiate them. So if #subtitles is a has_many, every >>> call to >>> SubtitleFile#subtitles may lookup Subtitles in the exact same way >>> w/o >>> any unique key to use to differentiate which subtitles belong to >>> which >>> subtitle files which would produce the results you are seeing. >>> >>> Is Subtitle a ActiveRecord::BaseWithoutTable or a real >>> ActiveRecord::Base >>> model? >> >> Hi Zach. Subtitle is an ActiveRecord::BaseWithoutTable model; it >> does not >> inherit from ActiveRecord::Base . >> >> The SubtitleFile "subtitles" attribute is actually just an Array. I >> didn''t >> bother to setup a has_many relationship with Subtitle. >> SubtitleFile#subtitles is a standard getter method that >> ActiveRecord sets up >> for me. >> >> In my app''s console, I tested out a has_many relationship between two >> ActiveRecord::BaseWithoutTable models, and there weren''t any mixups >> after >> creating associations between different model instances. >> >> Here''s the source for the SubtitleFile model. The #add_subtitle! in >> question >> starts on line 74: >> http://gist.github.com/54028 > > I''m not familiar with this library, so I can''t say for sure, but the > line > default :subtitles => [] > looks very suspicious to me. > > The underlying library code would have to do a .clone of that default > value, otherwise all instances will share the same array (meaning if > you push a value onto it, the next instance will have that array with > one element). > > If it doesn''t automatically clone (it may, I don''t know), it probably > should provide a lambda syntax so you can do: > default :subtitles => lambda { [] } > > PatPat, the lambda fixed the problem. That''s a keen spidey sense you have there! To spell this out in more detail, having defaults :subtitles => [] causes the SubtitleFile class to initialise an Array when the class is defined. From then on, the "subtitles" attribute of all SubtitleFile instances will refer to that one specific array. Thanks for your help with this, mate. It''s much appreciated. Nick
Nick Hoffman
2009-Jan-28 17:49 UTC
[rspec-users] [RSpec] Cloning objects and leaking state
On 28/01/2009, at 12:16 PM, Pat Maddox wrote:>> If it doesn''t automatically clone (it may, I don''t know), it probably >> should provide a lambda syntax so you can do: >> default :subtitles => lambda { [] } > > Yeah, I just looked at the code for ARD and this is the problem. You > can pass in immutable objects and they don''t get affected for obvious > reasons. But with a mutable object like an array, you need to pass it > a lambda so that it''s creating a new instance each time. > > PatThat makes a lot of sense. The active_record_defaults README[1] shows strings being used for default values. The problem that I experienced in this thread will plague the README''s example if a non-assignment operation is performed on the attribute. [1] http://svn.viney.net.nz/things/rails/plugins/active_record_defaults/README Thanks again, Pat, David, and Zach! Nick