Hi -- On Thu, 2 Nov 2006, Rob Biedenharn wrote: > Why is this so hard to understand? Rails isn't really doing anything magic > or really even tricky here -- just "unexpected". > > You are just confusing references and objects. No, that's not what's going on. ActiveRecord is doing something quite tricky. >>> require 'ostruct' > => true >>> o = OpenStruct.new('page' => "original", 'cover' => "hard") > => #<OpenStruct cover="hard", page="original"> >>> p = o.page > => "original" > > No surprise there. Let's change the contents of the page. > >>> o.page.replace("forgery") > => "forgery" There's nothing analogous to this in the ActiveRecord example. > ...and check in with our variable (aka, object reference): > >>> p > => "forgery" > > Yup. Still refers to the same String object as o.page. Now let's do an > assignment: > >>> o.page = "restoration" > => "restoration" >>> p > => "forgery" > > o.page now refers to a new object. But o.page = "restoration" is really just > sugar for o.page=("restoration"), right? That's where ActiveRecord, in the equivalent operation, does something very different. Imagine that when you examined p, you got "restoration". That would be the same as was AR is doing. >>> def o.page=(kind); self.page.replace(kind); end > => nil > > Make p refer to the same String object as o.page again: > >>> p = o.page > => "restoration" >>> p > => "restoration" >>> o.page > => "restoration" > > Now this "assignment" calls the newly defined page= method: > >>> o.page = "imitation" > => "imitation" >>> p > => "imitation" >>> p.replace("faux") > => "faux" >>> o.page > => "faux" > > So the way that variables are references to objects and the syntactic sugar > that makes 'o.page = foo' look like assignment even though it's sending foo > to the page= method of the o object are just colliding in your brain. I'm not sure why you have to express this in terms of confusion on the part of the people who don't like it. I think we're all well aware of =-terminated methods and all the rest of it. So let's leave the editorials about people's brains out of it. Anyway.... No one is saying that you *can't* do this in Ruby, only that what AR does is unexpected, unidiomatic, and inconsistent. The = syntactic sugar does, indeed, allow you to write arbitrary methods: class C def thing=(n) puts "Ha ha!" end end but the reason the sugar exists is to make assignment-like things look like assignments. The thing that AR does is not assignment-like, in any traditional or idiomatic sense. Yes, you *can* do a non-assignment-based operation (like String#replace) in a =-method... but, at the risk of sounding old-fashioned, I simply think that here: old_thing = obj.thing obj.thing = Thing.create it's reasonable to expect old_thing to refer to the original obj.thing, and not the new one. In ActiveRecord, that's not what happens. It's not an object-vs.-reference thing, either. There's a class called ActiveRecord::Associations::AssociationProxy which, I believe, is responsible for the way this works. old_thing is a proxy to obj.thing. If you assign it differently: old_thing = Thing.find(obj.thing.id) obj.thing = Thing.create old_thing does not change, because the proxy class is not involved. All of this is on top of the usual object/reference stuff in Ruby; in fact, it's a kind of super-reference that's being created (which is precisely *not* the model that Ruby is built on -- pointers to pointers, so to speak -- though as I've said the issue is not whether or not it can be done). As I've said before, if there's a rationale for AR doing it the way it does (other than the fact that it's possible to do it that way, which I doubt is what lies behind it), I would of course be very interested in hearing it. David -- David A. Black | dblack / wobblini.net Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3] DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4] [1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com [2] http://dablog.rubypal.com | [4] http://www.rubycentral.org