Uploaded image for project: 'Mongoid'
  1. Mongoid
  2. MONGOID-848

Odd after_delete callback issue with embedded document

    • Type: Icon: Task Task
    • Resolution: Done
    • 2.3.2
    • Affects Version/s: None
    • Component/s: None

      I just spent more than hour debugging an odd issue with an embedded document's after_delete callback where it wasn't
      working properly when calling destroy_all on the collection, but did work when calling each(&:destroy).

      It's hard to explain without an example, so here's what I used:

      <pre># Gemfile is using mongoid's Git HEAD
      require 'bundler/setup'
      require 'mongoid'

      Mongoid.configure do |config|
      config.master = Mongo::Connection.new.db('test')
      end

      class User
      include Mongoid::Document

      field :name, type: String

      embeds_many :favorites
      end

      class Favorite
      include Mongoid::Document

      embedded_in :user

      field :name, type: String

      after_destroy :decrement_count
      after_create :increment_count

      protected

      def decrement_count
      FavoriteCount.decrement(self.name)
      end

      def increment_count
      FavoriteCount.increment(self.name)
      end
      end

      class FavoriteCount
      include Mongoid::Document

      field :name, type: String
      field :count, type: Integer, default: 0

      def self.decrement(name)
      self.update_count(name, -1)
      end

      def self.increment(name)
      self.update_count(name, 1)
      end

      protected

      def self.update_count(name, value)
      if fc = self.find_or_initialize_by(name: name)
      fc.update_attribute(:count, fc.count + value)
      end
      end
      end

      if $0 == FILE
      require 'rspec'

      describe FavoriteCount do
      after do
      [User, FavoriteCount].each(&:destroy_all)
      end

      before do
      3.times do |i|
      user = User.create(name: "user#

      {i}

      ")
      user.favorites.create(name: 'mongodb')
      end
      end

      it "updates when calling destroy_all" do
      FavoriteCount.where(name: 'mongodb').first.count.should eql(3)
      User.last.favorites.destroy_all

      1. THIS FAILS; expected 2, got 3
        FavoriteCount.where(name: 'mongodb').first.count.should eql(2)
        end

      it "updates when calling each(&:destroy)" do
      FavoriteCount.where(name: 'mongodb').first.count.should eql(3)
      User.last.favorites.each(&:destroy)

      1. THIS PASSES
        FavoriteCount.where(name: 'mongodb').first.count.should eql(2)
        end
        end
        end</pre>

      Both times the callback is being executed, but for some reason only the second way actually has the desired outcome. The only thing I can think of is some kind of weird race condition.

            Assignee:
            Unassigned Unassigned
            Reporter:
            tsigo tsigo
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved: