Has_many: unexpected behaviour

I’ve been using has_many for quite a while now and thought I understood
it fairly well (I’m on Edge Rails).

But I’ve come across some unexpected behaviour, and I wonder if anyone
could shed some light on it.

If I assign a collection to a variable seems to change as the collection
changes. IS that expected?

Quick example

Let’s say:

human has_many :cats
cat belongs_to :human
and in this example bob is a Human with id 2 with 1 cat

bob = Human.find(2)
bobs_cats = bob.cats
bobs_cats.size #=> 1
bob.cats.create(:name => “Tibbles”)
bobs_cats.size #=> 2

So, the variable bob_cats is changing as the collection changes.

However if I replace the second line with bobs_cats =
bob.cats.find(:all) it behaves I would expect, i.e. behaves as an array
of Cat objects, and is not affected by changes to the collection.

Can anyone shed any light on this?

yes, they are the same because of this line

bobs_cats = bob.cats

they are the same instance. check the objects id’s and you will see

bob.cats.object_id
=> 18829402

bobs_cats.object_id
=> 18829402

you should see something similar.

if you want a separate object, use .dup for duplicate

bobs_cats = bob.cats.dup
bobs_cats.object_id
=> 19619092

bob.cats.object_id
=> 18829402

now there are two distinct objects

Hi –

On Sun, 17 Dec 2006, Chris H. wrote:

changes. IS that expected?
bobs_cats = bob.cats
Can anyone shed any light on this?
=> 18829402
=> 18829402

now there are two distinct objects

Just to elaborate a little further: this is happening because the
object in question is an association proxy object. It gets created
the first time, and then cached:

=> #<Tag:0xb75f6d1c @attributes={“body”=>“Device”, “id”=>“1”}>

things = tag.things
=> # (Ugh, but that’s another story :slight_smile:
things.object_id
=> -609253418
tag.things.object_id
=> -609253418

And when you call tag.things again, you get the same object:

tag.things.object_id
=> -609253418

It gets a little weirder here:

tag.things.reload
=> [#<Thing:0xb75e99dc @readonly=true, @attributes={“thing_id”=>“4”,
“tag_id”=>“1”, “id”=>“4”}>, #<Thing:0xb75e99c8 @readonly=true,
@attributes={“thing_id”=>“1”, “tag_id”=>“1”, “id”=>“21”}>,
#<Thing:0xb75e99a0 @readonly=true, @attributes={“thing_id”=>“2”,
“tag_id”=>“1”, “id”=>“22”}>]
tag.things.object_id
=> -609268288
things.object_id
=> -609268288

The object_id of things has changed, though things has not been
reassigned. Rather bizarre from the Ruby point of view, and not
particularly to my taste; I prefer to know what’s going to happen when
I assign to a variable, and to be the one who decides if and when the
binding will change. But it’s unlikely to cause trouble in practice,
as far as I can tell.

David


Q. What’s a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (Ruby for Rails)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

OK. That makes sense. Guess I never quite connected (in my mind) the
Array of AR objects that is bob.cats and the bob.cats.create or
whatever. I’m guess the additional has_many methods are available to the
array through some sort of method_missing magic?

Thanks for your help.
Chris

[email protected] wrote:

But I’ve come across some unexpected behaviour, and I wonder if anyone
cat belongs_to :human
However if I replace the second line with bobs_cats =

=> 18829402

things = tag.things

tag.things.reload

David

David

Thanks for this. Have been getting so much into the Ruby aspect of Rails
recently (not least thanks to your excellent book) sort of forgot about
some of the strange things that Rails does.
Cheers
Chris