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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
|