belongs_to nonintuitive results with assignment?

class Page < ActiveRecord::Base     has_many :lists end

class List < ActiveRecord::Base     belongs_to :page end

######### controller code, inside action:     list = List.find(params[:id])     old_page = list.page     list.page = new_page     # what is the value of old_page now?

old_page now is the same as new_page! what the heck?!?

The only thing i can think of is that old_page is getting assigned some kind of proxy object, rather than the Page object itself, and "list.page = new_page" changes only the actual Page object pointed to by the (same) proxy object. Am I on the right track?

The workaround is easy, of course. But would it really be that hard to maintain normal object referencing behavior?

Not sure if I'm asking a question or just ranting. But if other people don't see this behavior, or if there's an elegant explanation of why it has to be this way, then I'd like to know.

-Brian

Hi --

I got bit by this too, but on reflection (no pun intended :wink: it is correct, I was actually doing it the other way around and assigning the has_many, but it is the same issue.

Just think of it as pointer assignment if you come from c/c++.

One work around in your case is to do this instead...

old_page_id= list.page.id

I did this as it was an array...

old_lists= page.lists.clone

that way I got a clone of the array of lists, not a pointer to the lists has_many proxy. (or whatever it is).

brian.estlin wrote:

Hi --

brian.estlin wrote:

class Page < ActiveRecord::Base     has_many :lists end

class List < ActiveRecord::Base     belongs_to :page end

######### controller code, inside action:     list = List.find(params[:id])     old_page = list.page     list.page = new_page     # what is the value of old_page now?

old_page now is the same as new_page! what the heck?!?

The only thing i can think of is that old_page is getting assigned some kind of proxy object, rather than the Page object itself, and "list.page = new_page" changes only the actual Page object pointed to by the (same) proxy object. Am I on the right track?

The workaround is easy, of course. But would it really be that hard to maintain normal object referencing behavior?

Not sure if I'm asking a question or just ranting. But if other people don't see this behavior, or if there's an elegant explanation of why it has to be this way, then I'd like to know.

I got bit by this too, but on reflection (no pun intended :wink: it is correct, I was actually doing it the other way around and assigning the has_many, but it is the same issue.

Just think of it as pointer assignment if you come from c/c++.

I come from Ruby :slight_smile: It seems very odd to me.

One work around in your case is to do this instead...

old_page_id= list.page.id

Hey, enjoy the syntactic sugar:

   old_page_id = list.page.id

:slight_smile:

I did this as it was an array...

old_lists= page.lists.clone

that way I got a clone of the array of lists, not a pointer to the lists has_many proxy. (or whatever it is).

This makes for the (to me) strange situation where:

   a = list.page    b = Page.find(a.id)

lead to two different behaviors in the face of changing list.page. I'm not seeing what the purpose or advantage of this is.

David

dblack@wobblini.net wrote:

Just think of it as pointer assignment if you come from c/c++.

I come from Ruby :slight_smile: It seems very odd to me.

One work around in your case is to do this instead...

old_page_id= list.page.id

Hey, enjoy the syntactic sugar:

   old_page_id = list.page.id

:slight_smile:

I did this as it was an array...

old_lists= page.lists.clone

that way I got a clone of the array of lists, not a pointer to the lists has_many proxy. (or whatever it is).

This makes for the (to me) strange situation where:

   a = list.page    b = Page.find(a.id)

lead to two different behaviors in the face of changing list.page. I'm not seeing what the purpose or advantage of this is.

Agreed it threw me, but I gave up worrying about the why's a long time ago, I'm just thankful I got around the problem without spending hours on it :wink: As a sage once said "I am's What I am's", or in this case "it is as it is".

Well, I'm not going to spend too much time worrying about it either. But here's the thing that IMO makes it a bad design. If I show you the following code:

old_page = list.page list.page = new_page # what is the value of old_page now?

your answer (as to what old_page is) will be *different* depending on whether "list.page" represents an ActiveRecord association or not. I.e., you can't tell, just by looking at the above code. You have to go track down what "list" is and look in it's class definition to see whether "page" is an association.

Of course, in a small single-developer application, which mine is, it's not really an issue. But if you're handing a 50,000-line codebase to another developer, it's kind of unfortunate. So IMO it's a code readability/maintainability turkey.

In C pointer assignment, you at least can make a good guess from all the little *'s what's actually going on. :wink:

-Brian

brian.estlin wrote:

Well, I'm not going to spend too much time worrying about it either. But here's the thing that IMO makes it a bad design. If I show you the following code:

old_page = list.page list.page = new_page # what is the value of old_page now?

your answer (as to what old_page is) will be *different* depending on whether "list.page" represents an ActiveRecord association or not. I.e., you can't tell, just by looking at the above code. You have to go track down what "list" is and look in it's class definition to see whether "page" is an association.

Of course, in a small single-developer application, which mine is, it's not really an issue. But if you're handing a 50,000-line codebase to another developer, it's kind of unfortunate. So IMO it's a code readability/maintainability turkey.

In C pointer assignment, you at least can make a good guess from all the little *'s what's actually going on. :wink:

-Brian

I fully agree, it is non-intuitive and very confusing, I wonder what it would take to fix it, without breaking a bunch of other stuff?

Hi --