Matthew Lindfield Seager

Matthew Lindfield Seager

Ruby discoveries: When `items.first.method` ≠ `i = items.first; i.method`...

I came across something confusing a learning opportunity in Ruby:

items.first.foo # returns bar
items.first.foo = baz
items.first.foo # still returns bar???????

I think what is happening is that because items is a proxy object (in this case a Sequel Dataset), when I call items.first.foo = baz Ruby is effectively making a copy of items.first, changing foo on the copy and then taking out a cigarette lighter and burning the post-it note telling it where it put the copy. The copy was changed but I can never get to it again because the reference to it is gone.

One solution is to store a reference to the copy myself:

i = items.first
i.foo = baz
i.foo # now it's baz

Of course my actual use case wasn’t quite that simple. In my case I was iterating through items and setting them all to inactive… only it turns out I wasn’t. I was setting a copy to be inactive and then burning the post-it note with the location of the copy at the end of each loop:

@items ||= ::Item.where(active: true)
@items.each do |item|
  item.active = false
end
@items.first.active # true???

The next time I tried to access one of them to update it I was actually getting a fresh copy from the underlying source. *sigh*