Back

ActiveRecord Relations are not Arrays

If you use Ruby on Rails at all, you deal with ActiveRecord regularly and may not even be aware of how much hidden magic there is. But sometimes the magic can breakdown just a little. It is very common to use one of the finder methods to get back a collection of objects and then iterate on them with methods like each or even access individual elements with [ ]. Because of this I used to think that the return type of finder methods was Array, and in some cases, like all, it is an array. However, in many cases, what you actually get back is an ActiveRecord::Relation which does some method missing magic to delegate many array methods. However, as I found out recently, this sometimes breaks down.

I had an set of objects from an ActiveRecord finder and I wanted to remove one of them from the set before doing further processing. I found the object using the detect method and then tried to delete it using the delete method of Array. The code looked something like this.


class MyObject < ActiveRecord::Base
  scope :active, where(:active => true)
end
.
.
.
active_objects = MyObject.active
obj_to_remove = active_objects.detect {|obj| obj.remove? }
active_objects.delete(object_to_remove)
.
.
.

Which seemed okay, but when this code ran, not only was the object not removed from the active_objects collection, it was deleted from the database. This was not at all what I was trying to accomplish. Why did this happen? Because ActiveRecord::Relation has its own delete method, so it does not get delegated to Array. My solution was to make sure it was an array with the to_a method. So the relevant line of code became active_objects = MyObject.active.to_a. I think MyObject.active.all may also have worked, but I haven’t dived deep enough into the ActiveRecord magic to know if there are pros and cons to using that instead.

Leave a Reply

Your email address will not be published. Required fields are marked *