class ModelObserver < ActiveRecord::Observer observe ActiveRecord::Base # Set this to make the change observer ignore all model attribute changes cattr_accessor :_ignore self._ignore = false cattr_accessor :object_arrays self.object_arrays = [] class << self # All changes to model attributes will be ignored while the given block is executed def ignore(&block) with_ignore(true, &block) end def with_ignore(value) original_value = _ignore self._ignore = value yield ensure self._ignore = original_value end def collect result = [] object_arrays << result yield result ensure object_arrays.delete(result) end def collect_exclusively result = [] original_object_arrays = object_arrays self.object_arrays = [result] with_ignore(false) { yield } result ensure self.object_arrays = original_object_arrays end def process(*objects) return if _ignore || object_arrays.empty? objects = objects.select(&:modified?) object_arrays.each do |array| array.concat(objects) end end end def after_create(object) self.class.process(object) end def after_update(object) self.class.process(object) end end class ActiveRecord::Base class << self def transaction_with_model_observer(&block) result = nil objects = ModelObserver.collect_exclusively do result = transaction_without_model_observer(&block) end ModelObserver.process(*objects) result end alias_method_chain :transaction, :model_observer end end