I have been reading Rails 3 Way and was checking the behavior of CollectionAssociation.include?
As per description in the book:
Checks to see if the supplied record exists in the association
collection and that it still exists in the underlying database
table.
I make couple of tests to check if DB is checked but looks like it is not checking.
> book = Book.first
=> #<Book id: 1, title: "Programming Ruby"...
> last_chapter = book.chapters.last
=> #<Chapter id: 2, title: "Chapter 2", book_id: 1,
> b.chapters.include?(last_chapter)
=> true
> last_chapter.destroy
DELETE FROM "chapters" WHERE "chapters"."id" = ? [["id", 2]]
=> #<Chapter id: 2, title: "Chapter 2", book_id: 1,
> b.chapters.include?(last_chapter)
=> true
> book.chapters.reload
=> [#<Chapter id: 1, title: "Chapter 1", book_id: 1,...]
> b.chapters.include?(last_chapter)
=> false
I checked source code and found a line load_target if options[:finder_sql] which seems loads the record conditionally.
Do you have any idea when DB will get accessed to check if record is exists in DB?
Rails uses both lazy loading and memoization.
This means that
b.chaptersdoes not load any chapters until you first call the getter.Afterwards, destroying the chapter does not change the
include?test, because of memoization. Rails does not reload what chapters are in the book. Thus, it will not know that the chapter no longer exists in the book.Note that
include?(defined onEnumerable/Arrayobjects, but not implemented directly on theActiveRelationobject) will automatically cause Rails to cast theActiveRelationobjectbook.chaptersto an array. Thus,include?will first get the array of chapters, then check if the chapter is included in the array.Calling
reloadexplicitly (on anyActiveRecordobject) forces rails to reload that particular object, so that the caching of the chapters in the book is thrown away, and the actual chapters are reloaded.reloadeverywhere you need it to reload.includes, eg.book = Book.includes(:chapters).first. This means that Rails will load all the chapters at the same time as it loads the book(s).To delete a chapter, while ensuring that
book.chaptersis up-to-date, you can callbooks.chapters.delete(book.chapters.last)orbooks.chapter_ids.delete(book.chapter_ids.last). Using this method, Rails will not need to query the database again to know it is no longer in the collection.