diff options
Diffstat (limited to 'activemodel/lib/active_model/observer_array.rb')
-rw-r--r-- | activemodel/lib/active_model/observer_array.rb | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/activemodel/lib/active_model/observer_array.rb b/activemodel/lib/active_model/observer_array.rb new file mode 100644 index 0000000000..5fb73f1c78 --- /dev/null +++ b/activemodel/lib/active_model/observer_array.rb @@ -0,0 +1,147 @@ +require 'set' + +module ActiveModel + # Stores the enabled/disabled state of individual observers for + # a particular model class. + class ObserverArray < Array + attr_reader :model_class + def initialize(model_class, *args) + @model_class = model_class + super(*args) + end + + # Returns true if the given observer is disabled for the model class. + def disabled_for?(observer) + disabled_observers.include?(observer.class) + end + + # Disables one or more observers. This supports multiple forms: + # + # ORM.observers.disable :user_observer + # # => disables the UserObserver + # + # User.observers.disable AuditTrail + # # => disables the AuditTrail observer for User notifications. + # # Other models will still notify the AuditTrail observer. + # + # ORM.observers.disable :observer_1, :observer_2 + # # => disables Observer1 and Observer2 for all models. + # + # ORM.observers.disable :all + # # => disables all observers for all models. + # + # User.observers.disable :all do + # # all user observers are disabled for + # # just the duration of the block + # end + def disable(*observers, &block) + set_enablement(false, observers, &block) + end + + # Enables one or more observers. This supports multiple forms: + # + # ORM.observers.enable :user_observer + # # => enables the UserObserver + # + # User.observers.enable AuditTrail + # # => enables the AuditTrail observer for User notifications. + # # Other models will not be affected (i.e. they will not + # # trigger notifications to AuditTrail if previously disabled) + # + # ORM.observers.enable :observer_1, :observer_2 + # # => enables Observer1 and Observer2 for all models. + # + # ORM.observers.enable :all + # # => enables all observers for all models. + # + # User.observers.enable :all do + # # all user observers are enabled for + # # just the duration of the block + # end + # + # Note: all observers are enabled by default. This method is only + # useful when you have previously disabled one or more observers. + def enable(*observers, &block) + set_enablement(true, observers, &block) + end + + protected + + def disabled_observers + @disabled_observers ||= Set.new + end + + def observer_class_for(observer) + return observer if observer.is_a?(Class) + + if observer.respond_to?(:to_sym) # string/symbol + observer.to_s.camelize.constantize + else + raise ArgumentError, "#{observer} was not a class or a " + + "lowercase, underscored class name as expected." + 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 + start_transaction + + begin + yield + ensure + end_transaction + end + end + + def each_subclass_array + model_class.descendants.each do |subclass| + yield subclass.observers + end + end + + def set_enablement(enabled, observers) + if block_given? + transaction do + set_enablement(enabled, observers) + yield + end + else + observers = ActiveModel::Observer.descendants if observers == [:all] + observers.each do |obs| + klass = observer_class_for(obs) + + unless klass < ActiveModel::Observer + raise ArgumentError.new("#{obs} does not refer to a valid observer") + end + + if enabled + disabled_observers.delete(klass) + else + disabled_observers << klass + end + end + + each_subclass_array do |array| + array.set_enablement(enabled, observers) + end + end + end + end +end |