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 –

On Mon, 30 Oct 2006, brian.estlin wrote:

list = List.find(params[:id])

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 do see it, though I’d never noticed it before. I have no idea what
the rationale for it is.

David


David A. Black | [email protected]
Author of “Ruby for Rails” [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB’s Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

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:

old_page = list.page

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


Jim M., http://blog.wolfman.com

[email protected] wrote:

old_page_id = list.page.id
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”.


Jim M., http://blog.wolfman.com

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

Hi –

On Mon, 30 Oct 2006, Jim M. wrote:

list = List.find(params[:id])

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


David A. Black | [email protected]
Author of “Ruby for Rails” [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB’s Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

Hi –

On Tue, 31 Oct 2006, Jim M. wrote:

your answer (as to what old_page is) will be different depending on
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?

I’d argue that if this were gotten rid of, any changes that had to be
made to things that depended on it would be by way of fixing those
things :slight_smile:

Disclaimer: I’m still open to hearing a rationale for it from the
team.

David


David A. Black | [email protected]
Author of “Ruby for Rails” [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB’s Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

brian.estlin wrote:

I.e., you can’t tell, just by looking at the above code. You have to

-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?


Jim M., http://blog.wolfman.com