A plan
has many plan_dates
. This is a trivial example from my console
a = Plan.create
a.plan_dates.create( ddate: Date.today)
a.plan_dates.create( ddate: Date.today + 1.days )
a.plan_dates.create( ddate: Date.today + 2.days )
a.plan_dates.count
# => 3
a.plan_dates.each { |pd| puts pd.ddate }
# => 2015-06-06 00:00:00 UTC
# => 2015-06-07 00:00:00 UTC
# => 2015-06-08 00:00:00 UTC
When I destroy a plan_date, count
keeps track of it, but each
does not:
a.plan_dates.find_by_ddate(Date.today.to_datetime).destroy
a.plan_dates.count
# => 2
a.plan_dates.each { |pd| puts pd.ddate }
# => 2015-06-06 00:00:00 UTC
# => 2015-06-07 00:00:00 UTC
# => 2015-06-08 00:00:00 UTC
a.plan_dates[0].ddate
# => Sat, 06 Jun 2015 00:00:00 UTC +00:00
a.plan_dates[1].ddate
# => Sun, 07 Jun 2015 00:00:00 UTC +00:00
a.plan_dates[2].ddate
# => Mon, 08 Jun 2015 00:00:00 UTC +00:00
I get that Ruby removes records from the db but freezes objects, so they're still there, although incidentally:
a.plan_dates.each { |pd| puts pd.frozen? }
# => false
# => false
# => false
I would have expected it to be true
for the first pd that I destroyed. Just like how:
a.destroy
a.frozen?
# => true
What's the method to use to iterate over only the existing records? Something like each_non_frozen
. Also, how is the object actually deleted from the array? I call methods with specific plan_dates like a.plan_date[0]
, and I'd either want to see nil
or Sun, 07 Jun 2015 00:00:00 UTC +00:00
returned.
First, let me explain the behaviour with the array preserving destroyed elements. This case is possible due to rails caching mechanism.
As you see, the first two rows initiate SQL queries. But the last one does not! It uses the cached array from the previous request to
plan_dates.each
. More on that can be found at controlling caching (3.1) section of reference: ActiveRecord Associations.Here is how you force your array to fetch data from the database again:
As of freezing objects, Rails does freeze array elements which manually received
destroy
method call, but has no idea of what's going on when you call that on completely different object:This would work as you expect called with finders of
Enumerable
orArray
rather thenActiveRecord
: