diff --git a/activemodel/lib/active_model/observer_array.rb b/activemodel/lib/active_model/observer_array.rb index b8aa9cc1e2a35..f3b5811b8176b 100644 --- a/activemodel/lib/active_model/observer_array.rb +++ b/activemodel/lib/active_model/observer_array.rb @@ -48,7 +48,7 @@ def enable(*observers, &block) set_enablement(true, observers, &block) end - private + protected def observer_class_for(observer) return observer if observer.is_a?(Class) @@ -61,13 +61,37 @@ def observer_class_for(observer) end end + def start_transaction + disabled_observer_stack.push(disabled_observers.dup) + each_subclass_array do |array| + array.start_transaction + end + end + + def disabled_observer_stack + @disabled_observer_stack ||= [] + end + + def end_transaction + @disabled_observers = disabled_observer_stack.pop + each_subclass_array do |array| + array.end_transaction + end + end + def transaction - orig_disabled_observers = disabled_observers.dup + start_transaction begin yield ensure - @disabled_observers = orig_disabled_observers + end_transaction + end + end + + def each_subclass_array + model_class.subclasses.each do |subclass| + yield self.class.for(subclass) end end @@ -92,6 +116,10 @@ def set_enablement(enabled, observers) disabled_observers << klass end end + + each_subclass_array do |array| + array.set_enablement(enabled, observers) + end end end end diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index e1a2ce218da0f..ba6be46670ced 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -70,6 +70,10 @@ def count_observers observer_instances.size end + def subclasses + @subclasses ||= [] + end + protected def instantiate_observer(observer) #:nodoc: # string/symbol @@ -89,6 +93,7 @@ def instantiate_observer(observer) #:nodoc: # Notify observers when the observed class is subclassed. def inherited(subclass) super + subclasses << subclass notify_observers :observed_class_inherited, subclass end end diff --git a/activemodel/test/cases/observer_array_test.rb b/activemodel/test/cases/observer_array_test.rb index 215ca80bb4e7b..38e4fd59fc019 100644 --- a/activemodel/test/cases/observer_array_test.rb +++ b/activemodel/test/cases/observer_array_test.rb @@ -118,5 +118,44 @@ def assert_observer_not_notified(model_class, observer_class) ORM.observers.disable Widget end end + + test "allows #enable at the superclass level to override #disable at the subclass level when called last" do + Widget.observers.disable :all + ORM.observers.enable :all + + assert_observer_notified Widget, WidgetObserver + assert_observer_notified Budget, BudgetObserver + assert_observer_notified Widget, AuditTrail + assert_observer_notified Budget, AuditTrail + end + + test "allows #disable at the superclass level to override #enable at the subclass level when called last" do + Budget.observers.enable :audit_trail + ORM.observers.disable :audit_trail + + assert_observer_notified Widget, WidgetObserver + assert_observer_notified Budget, BudgetObserver + assert_observer_not_notified Widget, AuditTrail + assert_observer_not_notified Budget, AuditTrail + end + + test "can use the block form at different levels of the hierarchy" do + yielded = false + Widget.observers.disable :all + + ORM.observers.enable :all do + yielded = true + assert_observer_notified Widget, WidgetObserver + assert_observer_notified Budget, BudgetObserver + assert_observer_notified Widget, AuditTrail + assert_observer_notified Budget, AuditTrail + end + + assert yielded + assert_observer_not_notified Widget, WidgetObserver + assert_observer_notified Budget, BudgetObserver + assert_observer_not_notified Widget, AuditTrail + assert_observer_notified Budget, AuditTrail + end end