Skip to content
This repository
tree: ad62f19287
Fetching contributors…

Cannot retrieve contributors at this time

file 126 lines (104 sloc) 3.243 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
require 'set'

module ActiveModel
  # Stores the enabled/disabled state of individual observers for
  # a particular model classes.
  class ObserverArray < Array
    INSTANCES = Hash.new do |hash, model_class|
      hash[model_class] = new(model_class)
    end

    def self.for(model_class)
      return nil unless model_class < ActiveModel::Observing
      INSTANCES[model_class]
    end

    # returns false if:
    # - the ObserverArray for the given model's class has the given observer
    # in its disabled_observers set.
    # - or that is the case at any level of the model's superclass chain.
    def self.observer_enabled?(observer, model)
      klass = model.class
      observer_class = observer.class

      loop do
        break unless array = self.for(klass)
        return false if array.disabled_observers.include?(observer_class)
        klass = klass.superclass
      end

      true # observers are enabled by default
    end

    def disabled_observers
      @disabled_observers ||= Set.new
    end

    attr_reader :model_class
    def initialize(model_class, *args)
      @model_class = model_class
      super(*args)
    end

    def disable(*observers, &block)
      set_enablement(false, observers, &block)
    end

    def enable(*observers, &block)
      set_enablement(true, observers, &block)
    end

    protected

      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.subclasses.each do |subclass|
          yield self.class.for(subclass)
        end
      end

      def set_enablement(enabled, observers)
        if block_given?
          transaction do
            set_enablement(enabled, observers)
            yield
          end
        else
          observers = ActiveModel::Observer.all_observers 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
Something went wrong with that request. Please try again.