Try fetching car1 from the database again, I suspect it is just that
the version in memory does not know that you have removed one of its
wheels. I think there is a way of asking it to refresh itself to save
explicitly fetching it, but I cannot bring it to mind at the moment.
Just wonder if there is such a way that it can refresh itself in the
memory?
It is very rarely an issue in a real app. Remember objects typically
only have a very short life cycle as they only exist for a single http
request. I am not saying that it is never an issue, but only rarely.
Interesting. What's *supposed* to happen is that wheel's car_id gets
set to the id of the car. When you do that the second time, that
*should* overwrite the first one, so that the first one's wheels would
no longer include it. Why that's not happening, I'm not sure. Just
tried it with one of my own apps, that has Decisions that belong to
Users. Transcript:
How odd, user_id stays nil! Given that, I'm not sure just how Rails
"knows" that u#.decisions includes d. If anyone here has a clue,
maybe that would clear things up.
Meanwhile, as Colin said, Rails objects usually have a very short
lifespan, so maybe the fact that it was in the console, rather than
over the course of a bunch of HTTP requests, could have something to
do with it. Maybe something gets set when Rails set up or tears down
a request object, that isn't happening here. Try making an app in
which you can CRUD cars and wheels, put wheels on cars, and take
wheels off cars. Let us know what happens when you try to do the same
thing through the app.
Note that car1 wheels STILL contains wheel2 when I reassign wheel2 to
car2.
I expect car1 doesn't have wheel2 anymore...
I don't know why it doesn't get update automatically that car1 shouldn't
contains wheel2 anymore. Can anybody help me to solve this problem?
Interesting. What's *supposed* to happen is that wheel's car_id gets
set to the id of the car. When you do that the second time, that
*should* overwrite the first one, so that the first one's wheels would
no longer include it. Why that's not happening, I'm not sure. Just
tried it with one of my own apps, that has Decisions that belong to
Users. Transcript:
It is not appropriate to set the id values manually. Save them
instead, then rails will allocate the ids.
ruby-1.9.3-head :009 > d = Decision.new
=> #<Decision id: nil, user_id: nil, other details snipped>
ruby-1.9.3-head :020 > d.id = 1000
=> 1000
ruby-1.9.3-head :010 > u1.decisions << d
Again, don't set the ids manually, let rails do it.
=> [#<Decision id: 1000, user_id: nil, other details snipped>]
ruby-1.9.3-head :011 > u2.decisions << d
=> [#<Decision id: 1000, user_id: nil, other details snipped>]
ruby-1.9.3-head :016 > d
=> #<Decision id: 1000, user_id: nil, other details snipped>
ruby-1.9.3-head :021 > u1.decisions
=> [#<Decision id: 1000, user_id: nil, other details snipped>]
ruby-1.9.3-head :022 > u2.decisions
=> [#<Decision id: 1000, user_id: nil, other details snipped>]
How odd, user_id stays nil! Given that, I'm not sure just how Rails
"knows" that u#.decisions includes d. If anyone here has a clue,
maybe that would clear things up.
Try again with letting rails manage the ids and you may get a different result.
It is not appropriate to set the id values manually. Save them
instead, then rails will allocate the ids.
Since it was just for the purpose of a little irb experiment, I didn't
want to go to the bother of setting up a new app or jumping through
the hoops to set the other fields needed to make the record saveable.
Try again with letting rails manage the ids and you may get a different result.
Essentially my advice to the OP! Let's see what he says.
Note that car1 wheels STILL contains wheel2 when I reassign wheel2 to
car2.
I expect car1 doesn't have wheel2 anymore...
I don't know why it doesn't get update automatically that car1 shouldn't
contains wheel2 anymore. Can anybody help me to solve this problem?
The reason this happens is because Rails does not provide inter-model
change notification. Take the line:
car2.wheels << wheel2
Only car2 and wheel2 are involved in this statement. There is no change
notification system in Rails to send a message to car1 informing it that
anything changed. Therefore car1 only knows about the last state change
where it was actually involved. If you want car1 to "wake up" and update
it's internal state then you must do that yourself by either fetching it
again or forcing car1 to reload it's state from the database:
car1.reload
Using reload is typically not necessary in production code because the
next request usually refetches the objects involved anyway. But, it can
be useful in rare situations or when using the Rails console for exactly
the sort of experimenting that you're doing now.
The reason this happens is because Rails does not provide inter-model
change notification. Take the line:
Oh and BTW. If anyone is wondering why not just add a change
notification system to ActiveRecord?Well, How far do you want to take
that. It might be relatively straightforward if you have only a single
instance of an application. However, to support scaling to multiple
instances, maybe on several servers, you'd have to provide some sort of
distributed change notification. Obviously, this complicates matters
extensively. It's very much easier to just go back to the database to
discover changes in the underlying state.