ActiveRecord Gotcha

I came across this interesting gotcha while debugging through someone else's rails code. When you set a model's one-to-many attribute(an perhaps other association types as well), let's say:

user.friends = new_friend_list

It doesn't always set it. As far as I can figure from my blackbox testing, it does an equal check first between the old value and the new value, and then sets only if they are different.

Now, in Ruby's == is easily overridable, you can easily make it do anything you want. It happens that Array#== returns true iff both arrays have the same number of elements and each element in one array is == to the element of the other array in the same position. It also happens that ActiveRecord's base class' == returns true as long as the objects are of the same class and the primary keys equal.

Which brings us to the gotcha of the day. The code in question built an array, say new_friend_list independently, starting with an id list passed in from a from, finding each friend in the database and adding them to the array, but, in the meantime, modifying a field in each of the friends, say...friend.charma++. It then goes on to set the friends attribute in the manner you'd already seen above, and saves user. Well, the charma field that was modified in each of the friends retrieved did not get saved, because the set did not happen: the new_friend_list pointed to the same list of friends user was already associated to.

Hmm..., yeah. Watch out for this one. Debugging this one was not fun. It definitely didn't fit with the principle of least surprise.

blog comments powered by Disqus