Reputation: 4049
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.
Upvotes: 0
Views: 739
Reputation: 3438
First, let me explain the behaviour with the array preserving destroyed elements. This case is possible due to rails caching mechanism.
a.plan_dates.find_by_ddate(Date.today.to_datetime).destroy
# DELETE FROM "plan_dates" WHERE ...
a.plan_dates.count
# SELECT COUNT(*) FROM "plan_dates" WHERE ...
a.plan_dates.each { |pd| puts pd.ddate }
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:
a.plan_dates(true).each { |pd| puts pd.ddate }
# => 2015-06-07 00:00:00 UTC
# => 2015-06-08 00:00:00 UTC
# another way, with the same effect:
a.plan_dates.reload.each { |pd| puts pd.ddate }
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:
a.plan_dates.find_by_ddate(Date.today.to_datetime).destroy
# you have initiated SQL SELECT here!
# the result is not contained in initial array
This would work as you expect called with finders of Enumerable
or Array
rather then ActiveRecord
:
a.plan_dates.find{|i| i.ddate == Date.today}.destroy
a.plan_dates.find{|i| i.ddate == Date.today}.frozen? # => true
Upvotes: 2