Skip to content
This repository
Browse code

Remove observers and sweepers

They was extracted from a plugin.

See https://github.com/rails/rails-observers

[Rafael Mendonça França + Steve Klabnik]
commit ccecab3ba950a288b61a516bf9b6962e384aae0b 1 parent e38d310
Rafael Mendonça França rafaelfranca authored

Showing 39 changed files with 14 additions and 1,961 deletions. Show diff stats Hide diff stats

  1. +2 5 actionpack/lib/action_controller/caching.rb
  2. +0 116 actionpack/lib/action_controller/caching/sweeping.rb
  3. +0 1  actionpack/test/abstract_unit.rb
  4. +0 41 actionpack/test/controller/filters_test.rb
  5. +0 16 actionpack/test/controller/sweeper_test.rb
  6. +0 2  activemodel/lib/active_model.rb
  7. +0 152 activemodel/lib/active_model/observer_array.rb
  8. +0 373 activemodel/lib/active_model/observing.rb
  9. +0 220 activemodel/test/cases/observer_array_test.rb
  10. +0 181 activemodel/test/cases/observing_test.rb
  11. +0 27 activemodel/test/models/observers.rb
  12. +0 1  activerecord/lib/active_record.rb
  13. +0 2  activerecord/lib/active_record/base.rb
  14. +0 126 activerecord/lib/active_record/observer.rb
  15. +1 1  activerecord/lib/active_record/persistence.rb
  16. +0 10 activerecord/lib/active_record/railtie.rb
  17. +3 6 activerecord/lib/active_record/relation.rb
  18. +0 15 activerecord/lib/rails/generators/active_record/observer/observer_generator.rb
  19. +0 4 activerecord/lib/rails/generators/active_record/observer/templates/observer.rb
  20. +1 1  activerecord/test/cases/dirty_test.rb
  21. +0 256 activerecord/test/cases/lifecycle_test.rb
  22. +0 81 activerecord/test/cases/transaction_callbacks_test.rb
  23. +0 3  guides/code/getting_started/config/application.rb
  24. +1 66 guides/source/active_record_validations_callbacks.md
  25. +0 98 guides/source/caching_with_rails.md
  26. +2 2 guides/source/configuring.md
  27. +1 1  railties/lib/rails/engine.rb
  28. +0 2  railties/lib/rails/generators.rb
  29. +1 1  railties/lib/rails/generators/actions.rb
  30. +2 2 railties/lib/rails/generators/named_base.rb
  31. +0 12 railties/lib/rails/generators/rails/observer/USAGE
  32. +0 7 railties/lib/rails/generators/rails/observer/observer_generator.rb
  33. +0 13 railties/lib/rails/generators/test_unit/observer/observer_generator.rb
  34. +0 9 railties/lib/rails/generators/test_unit/observer/templates/unit_test.rb
  35. +0 21 railties/test/application/configuration_test.rb
  36. +0 17 railties/test/application/console_test.rb
  37. +0 22 railties/test/application/rake_test.rb
  38. +0 21 railties/test/generators/namespaced_generators_test.rb
  39. +0 27 railties/test/generators/observer_generator_test.rb
7 actionpack/lib/action_controller/caching.rb
@@ -6,10 +6,10 @@ module ActionController
6 6 # \Caching is a cheap way of speeding up slow applications by keeping the result of
7 7 # calculations, renderings, and database calls around for subsequent requests.
8 8 #
9   - # You can read more about each approach and the sweeping assistance by clicking the
  9 + # You can read more about each approach and the by clicking the
10 10 # modules below.
11 11 #
12   - # Note: To turn off all caching and sweeping, set
  12 + # Note: To turn off all caching, set
13 13 # config.action_controller.perform_caching = false.
14 14 #
15 15 # == \Caching stores
@@ -30,8 +30,6 @@ module Caching
30 30
31 31 eager_autoload do
32 32 autoload :Fragments
33   - autoload :Sweeper, 'action_controller/caching/sweeping'
34   - autoload :Sweeping, 'action_controller/caching/sweeping'
35 33 end
36 34
37 35 module ConfigMethods
@@ -54,7 +52,6 @@ def cache_configured?
54 52
55 53 include ConfigMethods
56 54 include Fragments
57   - include Sweeping if defined?(ActiveRecord)
58 55
59 56 included do
60 57 extend ConfigMethods
116 actionpack/lib/action_controller/caching/sweeping.rb
... ... @@ -1,116 +0,0 @@
1   -module ActionController
2   - module Caching
3   - # Sweepers are the terminators of the caching world and responsible for expiring
4   - # caches when Active Record objects change. They do this by being half-observers,
5   - # half-filters and implementing callbacks for both roles.
6   - #
7   - # class ListSweeper < ActionController::Caching::Sweeper
8   - # observe List, Item
9   - #
10   - # def after_save(record)
11   - # list = record.is_a?(List) ? record : record.list
12   - # expire_page(controller: 'lists', action: %w( show public feed ), id: list.id)
13   - # expire_action(controller: 'lists', action: 'all')
14   - # list.shares.each { |share| expire_page(controller: 'lists', action: 'show', id: share.url_key) }
15   - # end
16   - # end
17   - #
18   - # The sweeper is assigned in the controllers that wish to have its job performed using
19   - # the +cache_sweeper+ class method:
20   - #
21   - # class ListsController < ApplicationController
22   - # caches_action :index, :show, :public, :feed
23   - # cache_sweeper :list_sweeper, only: [ :edit, :destroy, :share ]
24   - # end
25   - #
26   - # In the example above, four actions are cached and three actions are responsible for expiring those caches.
27   - #
28   - # You can also name an explicit class in the declaration of a sweeper, which is needed
29   - # if the sweeper is in a module:
30   - #
31   - # class ListsController < ApplicationController
32   - # caches_action :index, :show, :public, :feed
33   - # cache_sweeper OpenBar::Sweeper, only: [ :edit, :destroy, :share ]
34   - # end
35   - module Sweeping
36   - extend ActiveSupport::Concern
37   -
38   - module ClassMethods # :nodoc:
39   - def cache_sweeper(*sweepers)
40   - configuration = sweepers.extract_options!
41   -
42   - sweepers.each do |sweeper|
43   - ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
44   - sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
45   -
46   - if sweeper_instance.is_a?(Sweeper)
47   - around_filter(sweeper_instance, :only => configuration[:only])
48   - else
49   - after_filter(sweeper_instance, :only => configuration[:only])
50   - end
51   - end
52   - end
53   - end
54   - end
55   -
56   - if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
57   - class Sweeper < ActiveRecord::Observer # :nodoc:
58   - attr_accessor :controller
59   -
60   - def initialize(*args)
61   - super
62   - @controller = nil
63   - end
64   -
65   - def before(controller)
66   - self.controller = controller
67   - callback(:before) if controller.perform_caching
68   - true # before method from sweeper should always return true
69   - end
70   -
71   - def after(controller)
72   - self.controller = controller
73   - callback(:after) if controller.perform_caching
74   - end
75   -
76   - def around(controller)
77   - before(controller)
78   - yield
79   - after(controller)
80   - ensure
81   - clean_up
82   - end
83   -
84   - protected
85   - # gets the action cache path for the given options.
86   - def action_path_for(options)
87   - Actions::ActionCachePath.new(controller, options).path
88   - end
89   -
90   - # Retrieve instance variables set in the controller.
91   - def assigns(key)
92   - controller.instance_variable_get("@#{key}")
93   - end
94   -
95   - private
96   - def clean_up
97   - # Clean up, so that the controller can be collected after this request
98   - self.controller = nil
99   - end
100   -
101   - def callback(timing)
102   - controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
103   - action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
104   -
105   - __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
106   - __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true)
107   - end
108   -
109   - def method_missing(method, *arguments, &block)
110   - return super unless @controller
111   - @controller.__send__(method, *arguments, &block)
112   - end
113   - end
114   - end
115   - end
116   -end
1  actionpack/test/abstract_unit.rb
@@ -25,7 +25,6 @@
25 25 require 'active_model'
26 26 require 'active_record'
27 27 require 'action_controller/caching'
28   -require 'action_controller/caching/sweeping'
29 28
30 29 require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
31 30
41 actionpack/test/controller/filters_test.rb
@@ -499,18 +499,6 @@ def filter_three
499 499
500 500 end
501 501
502   - class ::AppSweeper < ActionController::Caching::Sweeper; end
503   - class SweeperTestController < ActionController::Base
504   - cache_sweeper :app_sweeper
505   - def show
506   - render :text => 'hello world'
507   - end
508   -
509   - def error
510   - raise StandardError.new
511   - end
512   - end
513   -
514 502 class ImplicitActionsController < ActionController::Base
515 503 before_filter :find_only, :only => :edit
516 504 before_filter :find_except, :except => :edit
@@ -526,35 +514,6 @@ def find_except
526 514 end
527 515 end
528 516
529   - def test_sweeper_should_not_ignore_no_method_error
530   - sweeper = ActionController::Caching::Sweeper.send(:new)
531   - assert_raise NoMethodError do
532   - sweeper.send_not_defined
533   - end
534   - end
535   -
536   - def test_sweeper_should_not_block_rendering
537   - response = test_process(SweeperTestController)
538   - assert_equal 'hello world', response.body
539   - end
540   -
541   - def test_sweeper_should_clean_up_if_exception_is_raised
542   - assert_raise StandardError do
543   - test_process(SweeperTestController, 'error')
544   - end
545   - assert_nil AppSweeper.instance.controller
546   - end
547   -
548   - def test_before_method_of_sweeper_should_always_return_true
549   - sweeper = ActionController::Caching::Sweeper.send(:new)
550   - assert sweeper.before(TestController.new)
551   - end
552   -
553   - def test_after_method_of_sweeper_should_always_return_nil
554   - sweeper = ActionController::Caching::Sweeper.send(:new)
555   - assert_nil sweeper.after(TestController.new)
556   - end
557   -
558 517 def test_non_yielding_around_filters_not_returning_false_do_not_raise
559 518 controller = NonYieldingAroundFilterController.new
560 519 controller.instance_variable_set "@filter_return_value", true
16 actionpack/test/controller/sweeper_test.rb
... ... @@ -1,16 +0,0 @@
1   -require 'abstract_unit'
2   -
3   -
4   -class SweeperTest < ActionController::TestCase
5   -
6   - class ::AppSweeper < ActionController::Caching::Sweeper; end
7   -
8   - def test_sweeper_should_not_ignore_unknown_method_calls
9   - sweeper = ActionController::Caching::Sweeper.send(:new)
10   - assert_raise NameError do
11   - sweeper.instance_eval do
12   - some_method_that_doesnt_exist
13   - end
14   - end
15   - end
16   -end
2  activemodel/lib/active_model.rb
@@ -40,8 +40,6 @@ module ActiveModel
40 40 autoload :DeprecatedMassAssignmentSecurity
41 41 autoload :Name, 'active_model/naming'
42 42 autoload :Naming
43   - autoload :Observer, 'active_model/observing'
44   - autoload :Observing
45 43 autoload :SecurePassword
46 44 autoload :Serialization
47 45 autoload :TestCase
152 activemodel/lib/active_model/observer_array.rb
... ... @@ -1,152 +0,0 @@
1   -require 'set'
2   -
3   -module ActiveModel
4   - # Stores the enabled/disabled state of individual observers for
5   - # a particular model class.
6   - class ObserverArray < Array
7   - attr_reader :model_class
8   - def initialize(model_class, *args) #:nodoc:
9   - @model_class = model_class
10   - super(*args)
11   - end
12   -
13   - # Returns +true+ if the given observer is disabled for the model class,
14   - # +false+ otherwise.
15   - def disabled_for?(observer) #:nodoc:
16   - disabled_observers.include?(observer.class)
17   - end
18   -
19   - # Disables one or more observers. This supports multiple forms:
20   - #
21   - # ORM.observers.disable :all
22   - # # => disables all observers for all models subclassed from
23   - # # an ORM base class that includes ActiveModel::Observing
24   - # # e.g. ActiveRecord::Base
25   - #
26   - # ORM.observers.disable :user_observer
27   - # # => disables the UserObserver
28   - #
29   - # User.observers.disable AuditTrail
30   - # # => disables the AuditTrail observer for User notifications.
31   - # # Other models will still notify the AuditTrail observer.
32   - #
33   - # ORM.observers.disable :observer_1, :observer_2
34   - # # => disables Observer1 and Observer2 for all models.
35   - #
36   - # User.observers.disable :all do
37   - # # all user observers are disabled for
38   - # # just the duration of the block
39   - # end
40   - def disable(*observers, &block)
41   - set_enablement(false, observers, &block)
42   - end
43   -
44   - # Enables one or more observers. This supports multiple forms:
45   - #
46   - # ORM.observers.enable :all
47   - # # => enables all observers for all models subclassed from
48   - # # an ORM base class that includes ActiveModel::Observing
49   - # # e.g. ActiveRecord::Base
50   - #
51   - # ORM.observers.enable :user_observer
52   - # # => enables the UserObserver
53   - #
54   - # User.observers.enable AuditTrail
55   - # # => enables the AuditTrail observer for User notifications.
56   - # # Other models will not be affected (i.e. they will not
57   - # # trigger notifications to AuditTrail if previously disabled)
58   - #
59   - # ORM.observers.enable :observer_1, :observer_2
60   - # # => enables Observer1 and Observer2 for all models.
61   - #
62   - # User.observers.enable :all do
63   - # # all user observers are enabled for
64   - # # just the duration of the block
65   - # end
66   - #
67   - # Note: all observers are enabled by default. This method is only
68   - # useful when you have previously disabled one or more observers.
69   - def enable(*observers, &block)
70   - set_enablement(true, observers, &block)
71   - end
72   -
73   - protected
74   -
75   - def disabled_observers #:nodoc:
76   - @disabled_observers ||= Set.new
77   - end
78   -
79   - def observer_class_for(observer) #:nodoc:
80   - return observer if observer.is_a?(Class)
81   -
82   - if observer.respond_to?(:to_sym) # string/symbol
83   - observer.to_s.camelize.constantize
84   - else
85   - raise ArgumentError, "#{observer} was not a class or a " +
86   - "lowercase, underscored class name as expected."
87   - end
88   - end
89   -
90   - def start_transaction #:nodoc:
91   - disabled_observer_stack.push(disabled_observers.dup)
92   - each_subclass_array do |array|
93   - array.start_transaction
94   - end
95   - end
96   -
97   - def disabled_observer_stack #:nodoc:
98   - @disabled_observer_stack ||= []
99   - end
100   -
101   - def end_transaction #:nodoc:
102   - @disabled_observers = disabled_observer_stack.pop
103   - each_subclass_array do |array|
104   - array.end_transaction
105   - end
106   - end
107   -
108   - def transaction #:nodoc:
109   - start_transaction
110   -
111   - begin
112   - yield
113   - ensure
114   - end_transaction
115   - end
116   - end
117   -
118   - def each_subclass_array #:nodoc:
119   - model_class.descendants.each do |subclass|
120   - yield subclass.observers
121   - end
122   - end
123   -
124   - def set_enablement(enabled, observers) #:nodoc:
125   - if block_given?
126   - transaction do
127   - set_enablement(enabled, observers)
128   - yield
129   - end
130   - else
131   - observers = ActiveModel::Observer.descendants if observers == [:all]
132   - observers.each do |obs|
133   - klass = observer_class_for(obs)
134   -
135   - unless klass < ActiveModel::Observer
136   - raise ArgumentError.new("#{obs} does not refer to a valid observer")
137   - end
138   -
139   - if enabled
140   - disabled_observers.delete(klass)
141   - else
142   - disabled_observers << klass
143   - end
144   - end
145   -
146   - each_subclass_array do |array|
147   - array.set_enablement(enabled, observers)
148   - end
149   - end
150   - end
151   - end
152   -end
373 activemodel/lib/active_model/observing.rb
... ... @@ -1,373 +0,0 @@
1   -require 'singleton'
2   -require 'active_model/observer_array'
3   -require 'active_support/core_ext/module/aliasing'
4   -require 'active_support/core_ext/module/remove_method'
5   -require 'active_support/core_ext/string/inflections'
6   -require 'active_support/core_ext/enumerable'
7   -require 'active_support/core_ext/object/try'
8   -
9   -module ActiveModel
10   - # == Active \Model Observers Activation
11   - module Observing
12   - extend ActiveSupport::Concern
13   -
14   - included do
15   - extend ActiveSupport::DescendantsTracker
16   - end
17   -
18   - module ClassMethods
19   - # Activates the observers assigned.
20   - #
21   - # class ORM
22   - # include ActiveModel::Observing
23   - # end
24   - #
25   - # # Calls PersonObserver.instance
26   - # ORM.observers = :person_observer
27   - #
28   - # # Calls Cacher.instance and GarbageCollector.instance
29   - # ORM.observers = :cacher, :garbage_collector
30   - #
31   - # # Same as above, just using explicit class references
32   - # ORM.observers = Cacher, GarbageCollector
33   - #
34   - # Note: Setting this does not instantiate the observers yet.
35   - # <tt>instantiate_observers</tt> is called during startup, and before
36   - # each development request.
37   - def observers=(*values)
38   - observers.replace(values.flatten)
39   - end
40   -
41   - # Gets an array of observers observing this model. The array also provides
42   - # +enable+ and +disable+ methods that allow you to selectively enable and
43   - # disable observers (see ActiveModel::ObserverArray.enable and
44   - # ActiveModel::ObserverArray.disable for more on this).
45   - #
46   - # class ORM
47   - # include ActiveModel::Observing
48   - # end
49   - #
50   - # ORM.observers = :cacher, :garbage_collector
51   - # ORM.observers # => [:cacher, :garbage_collector]
52   - # ORM.observers.class # => ActiveModel::ObserverArray
53   - def observers
54   - @observers ||= ObserverArray.new(self)
55   - end
56   -
57   - # Returns the current observer instances.
58   - #
59   - # class Foo
60   - # include ActiveModel::Observing
61   - #
62   - # attr_accessor :status
63   - # end
64   - #
65   - # class FooObserver < ActiveModel::Observer
66   - # def on_spec(record, *args)
67   - # record.status = true
68   - # end
69   - # end
70   - #
71   - # Foo.observers = FooObserver
72   - # Foo.instantiate_observers
73   - #
74   - # Foo.observer_instances # => [#<FooObserver:0x007fc212c40820>]
75   - def observer_instances
76   - @observer_instances ||= []
77   - end
78   -
79   - # Instantiate the global observers.
80   - #
81   - # class Foo
82   - # include ActiveModel::Observing
83   - #
84   - # attr_accessor :status
85   - # end
86   - #
87   - # class FooObserver < ActiveModel::Observer
88   - # def on_spec(record, *args)
89   - # record.status = true
90   - # end
91   - # end
92   - #
93   - # Foo.observers = FooObserver
94   - #
95   - # foo = Foo.new
96   - # foo.status = false
97   - # foo.notify_observers(:on_spec)
98   - # foo.status # => false
99   - #
100   - # Foo.instantiate_observers # => [FooObserver]
101   - #
102   - # foo = Foo.new
103   - # foo.status = false
104   - # foo.notify_observers(:on_spec)
105   - # foo.status # => true
106   - def instantiate_observers
107   - observers.each { |o| instantiate_observer(o) }
108   - end
109   -
110   - # Add a new observer to the pool. The new observer needs to respond to
111   - # <tt>update</tt>, otherwise it raises an +ArgumentError+ exception.
112   - #
113   - # class Foo
114   - # include ActiveModel::Observing
115   - # end
116   - #
117   - # class FooObserver < ActiveModel::Observer
118   - # end
119   - #
120   - # Foo.add_observer(FooObserver.instance)
121   - #
122   - # Foo.observers_instance
123   - # # => [#<FooObserver:0x007fccf55d9390>]
124   - def add_observer(observer)
125   - unless observer.respond_to? :update
126   - raise ArgumentError, "observer needs to respond to 'update'"
127   - end
128   - observer_instances << observer
129   - end
130   -
131   - # Fires notifications to model's observers.
132   - #
133   - # def save
134   - # notify_observers(:before_save)
135   - # ...
136   - # notify_observers(:after_save)
137   - # end
138   - #
139   - # Custom notifications can be sent in a similar fashion:
140   - #
141   - # notify_observers(:custom_notification, :foo)
142   - #
143   - # This will call <tt>custom_notification</tt>, passing as arguments
144   - # the current object and <tt>:foo</tt>.
145   - def notify_observers(*args)
146   - observer_instances.each { |observer| observer.update(*args) }
147   - end
148   -
149   - # Returns the total number of instantiated observers.
150   - #
151   - # class Foo
152   - # include ActiveModel::Observing
153   - #
154   - # attr_accessor :status
155   - # end
156   - #
157   - # class FooObserver < ActiveModel::Observer
158   - # def on_spec(record, *args)
159   - # record.status = true
160   - # end
161   - # end
162   - #
163   - # Foo.observers = FooObserver
164   - # Foo.observers_count # => 0
165   - # Foo.instantiate_observers
166   - # Foo.observers_count # => 1
167   - def observers_count
168   - observer_instances.size
169   - end
170   -
171   - # <tt>count_observers</tt> is deprecated. Use #observers_count.
172   - def count_observers
173   - msg = "count_observers is deprecated in favor of observers_count"
174   - ActiveSupport::Deprecation.warn msg
175   - observers_count
176   - end
177   -
178   - protected
179   - def instantiate_observer(observer) #:nodoc:
180   - # string/symbol
181   - if observer.respond_to?(:to_sym)
182   - observer = observer.to_s.camelize.constantize
183   - end
184   - if observer.respond_to?(:instance)
185   - observer.instance
186   - else
187   - raise ArgumentError,
188   - "#{observer} must be a lowercase, underscored class name (or " +
189   - "the class itself) responding to the method :instance. " +
190   - "Example: Person.observers = :big_brother # calls " +
191   - "BigBrother.instance"
192   - end
193   - end
194   -
195   - # Notify observers when the observed class is subclassed.
196   - def inherited(subclass) #:nodoc:
197   - super
198   - notify_observers :observed_class_inherited, subclass
199   - end
200   - end
201   -
202   - # Notify a change to the list of observers.
203   - #
204   - # class Foo
205   - # include ActiveModel::Observing
206   - #
207   - # attr_accessor :status
208   - # end
209   - #
210   - # class FooObserver < ActiveModel::Observer
211   - # def on_spec(record, *args)
212   - # record.status = true
213   - # end
214   - # end
215   - #
216   - # Foo.observers = FooObserver
217   - # Foo.instantiate_observers # => [FooObserver]
218   - #
219   - # foo = Foo.new
220   - # foo.status = false
221   - # foo.notify_observers(:on_spec)
222   - # foo.status # => true
223   - #
224   - # See ActiveModel::Observing::ClassMethods.notify_observers for more
225   - # information.
226   - def notify_observers(method, *extra_args)
227   - self.class.notify_observers(method, self, *extra_args)
228   - end
229   - end
230   -
231   - # == Active \Model Observers
232   - #
233   - # Observer classes respond to life cycle callbacks to implement trigger-like
234   - # behavior outside the original class. This is a great way to reduce the
235   - # clutter that normally comes when the model class is burdened with
236   - # functionality that doesn't pertain to the core responsibility of the
237   - # class.
238   - #
239   - # class CommentObserver < ActiveModel::Observer
240   - # def after_save(comment)
241   - # Notifications.comment('admin@do.com', 'New comment was posted', comment).deliver
242   - # end
243   - # end
244   - #
245   - # This Observer sends an email when a <tt>Comment#save</tt> is finished.
246   - #
247   - # class ContactObserver < ActiveModel::Observer
248   - # def after_create(contact)
249   - # contact.logger.info('New contact added!')
250   - # end
251   - #
252   - # def after_destroy(contact)
253   - # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
254   - # end
255   - # end
256   - #
257   - # This Observer uses logger to log when specific callbacks are triggered.
258   - #
259   - # == \Observing a class that can't be inferred
260   - #
261   - # Observers will by default be mapped to the class with which they share a
262   - # name. So <tt>CommentObserver</tt> will be tied to observing <tt>Comment</tt>,
263   - # <tt>ProductManagerObserver</tt> to <tt>ProductManager</tt>, and so on. If
264   - # you want to name your observer differently than the class you're interested
265   - # in observing, you can use the <tt>Observer.observe</tt> class method which
266   - # takes either the concrete class (<tt>Product</tt>) or a symbol for that
267   - # class (<tt>:product</tt>):
268   - #
269   - # class AuditObserver < ActiveModel::Observer
270   - # observe :account
271   - #
272   - # def after_update(account)
273   - # AuditTrail.new(account, 'UPDATED')
274   - # end
275   - # end
276   - #
277   - # If the audit observer needs to watch more than one kind of object, this can
278   - # be specified with multiple arguments:
279   - #
280   - # class AuditObserver < ActiveModel::Observer
281   - # observe :account, :balance
282   - #
283   - # def after_update(record)
284   - # AuditTrail.new(record, 'UPDATED')
285   - # end
286   - # end
287   - #
288   - # The <tt>AuditObserver</tt> will now act on both updates to <tt>Account</tt>
289   - # and <tt>Balance</tt> by treating them both as records.
290   - #
291   - # If you're using an Observer in a Rails application with Active Record, be
292   - # sure to read about the necessary configuration in the documentation for
293   - # ActiveRecord::Observer.
294   - class Observer
295   - include Singleton
296   - extend ActiveSupport::DescendantsTracker
297   -
298   - class << self
299   - # Attaches the observer to the supplied model classes.
300   - #
301   - # class AuditObserver < ActiveModel::Observer
302   - # observe :account, :balance
303   - # end
304   - #
305   - # AuditObserver.observed_classes # => [Account, Balance]
306   - def observe(*models)
307   - models.flatten!
308   - models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model }
309   - singleton_class.redefine_method(:observed_classes) { models }
310   - end
311   -
312   - # Returns an array of Classes to observe.
313   - #
314   - # AccountObserver.observed_classes # => [Account]
315   - #
316   - # You can override this instead of using the +observe+ helper.
317   - #
318   - # class AuditObserver < ActiveModel::Observer
319   - # def self.observed_classes
320   - # [Account, Balance]
321   - # end
322   - # end
323   - def observed_classes
324   - Array(observed_class)
325   - end
326   -
327   - # Returns the class observed by default. It's inferred from the observer's
328   - # class name.
329   - #
330   - # PersonObserver.observed_class # => Person
331   - # AccountObserver.observed_class # => Account
332   - def observed_class
333   - name[/(.*)Observer/, 1].try :constantize
334   - end
335   - end
336   -
337   - # Start observing the declared classes and their subclasses.
338   - # Called automatically by the instance method.
339   - def initialize #:nodoc:
340   - observed_classes.each { |klass| add_observer!(klass) }
341   - end
342   -
343   - def observed_classes #:nodoc:
344   - self.class.observed_classes
345   - end
346   -
347   - # Send observed_method(object) if the method exists and
348   - # the observer is enabled for the given object's class.
349   - def update(observed_method, object, *extra_args, &block) #:nodoc:
350   - return if !respond_to?(observed_method) || disabled_for?(object)
351   - send(observed_method, object, *extra_args, &block)
352   - end
353   -
354   - # Special method sent by the observed class when it is inherited.
355   - # Passes the new subclass.
356   - def observed_class_inherited(subclass) #:nodoc:
357   - self.class.observe(observed_classes + [subclass])
358   - add_observer!(subclass)
359   - end
360   -
361   - protected
362   - def add_observer!(klass) #:nodoc:
363   - klass.add_observer(self)
364   - end
365   -
366   - # Returns true if notifications are disabled for this object.
367   - def disabled_for?(object) #:nodoc:
368   - klass = object.class
369   - return false unless klass.respond_to?(:observers)
370   - klass.observers.disabled_for?(self)
371   - end
372   - end
373   -end
220 activemodel/test/cases/observer_array_test.rb
... ... @@ -1,220 +0,0 @@
1   -require 'cases/helper'
2   -require 'models/observers'
3   -
4   -class ObserverArrayTest < ActiveModel::TestCase
5   - def teardown
6   - ORM.observers.enable :all
7   - Budget.observers.enable :all
8   - Widget.observers.enable :all
9   - end
10   -
11   - def assert_observer_notified(model_class, observer_class)
12   - observer_class.instance.before_save_invocations.clear
13   - model_instance = model_class.new
14   - model_instance.save
15   - assert_equal [model_instance], observer_class.instance.before_save_invocations
16   - end
17   -
18   - def assert_observer_not_notified(model_class, observer_class)
19   - observer_class.instance.before_save_invocations.clear
20   - model_instance = model_class.new
21   - model_instance.save
22   - assert_equal [], observer_class.instance.before_save_invocations
23   - end
24   -
25   - test "all observers are enabled by default" do
26   - assert_observer_notified Widget, WidgetObserver
27   - assert_observer_notified Budget, BudgetObserver
28   - assert_observer_notified Widget, AuditTrail
29   - assert_observer_notified Budget, AuditTrail
30   - end
31   -
32   - test "can disable individual observers using a class constant" do
33   - ORM.observers.disable WidgetObserver
34   -
35   - assert_observer_not_notified Widget, WidgetObserver
36   - assert_observer_notified Budget, BudgetObserver
37   - assert_observer_notified Widget, AuditTrail
38   - assert_observer_notified Budget, AuditTrail
39   - end
40   -
41   - test "can enable individual observers using a class constant" do
42   - ORM.observers.disable :all
43   - ORM.observers.enable AuditTrail
44   -
45   - assert_observer_not_notified Widget, WidgetObserver
46   - assert_observer_not_notified Budget, BudgetObserver
47   - assert_observer_notified Widget, AuditTrail
48   - assert_observer_notified Budget, AuditTrail
49   - end
50   -
51   - test "can disable individual observers using a symbol" do
52   - ORM.observers.disable :budget_observer
53   -
54   - assert_observer_notified Widget, WidgetObserver
55   - assert_observer_not_notified Budget, BudgetObserver
56   - assert_observer_notified Widget, AuditTrail
57   - assert_observer_notified Budget, AuditTrail
58   - end
59   -
60   - test "can enable individual observers using a symbol" do
61   - ORM.observers.disable :all
62   - ORM.observers.enable :audit_trail
63   -
64   - assert_observer_not_notified Widget, WidgetObserver
65   - assert_observer_not_notified Budget, BudgetObserver
66   - assert_observer_notified Widget, AuditTrail
67   - assert_observer_notified Budget, AuditTrail
68   - end
69   -
70   - test "can disable multiple observers at a time" do
71   - ORM.observers.disable :widget_observer, :budget_observer
72   -
73   - assert_observer_not_notified Widget, WidgetObserver
74   - assert_observer_not_notified Budget, BudgetObserver
75   - assert_observer_notified Widget, AuditTrail
76   - assert_observer_notified Budget, AuditTrail
77   - end
78   -
79   - test "can enable multiple observers at a time" do
80   - ORM.observers.disable :all
81   - ORM.observers.enable :widget_observer, :budget_observer
82   -
83   - assert_observer_notified Widget, WidgetObserver
84   - assert_observer_notified Budget, BudgetObserver
85   - assert_observer_not_notified Widget, AuditTrail
86   - assert_observer_not_notified Budget, AuditTrail
87   - end
88   -
89   - test "can disable all observers using :all" do
90   - ORM.observers.disable :all
91   -
92   - assert_observer_not_notified Widget, WidgetObserver
93   - assert_observer_not_notified Budget, BudgetObserver
94   - assert_observer_not_notified Widget, AuditTrail
95   - assert_observer_not_notified Budget, AuditTrail
96   - end
97   -
98   - test "can enable all observers using :all" do
99   - ORM.observers.disable :all
100   - ORM.observers.enable :all
101   -
102   - assert_observer_notified Widget, WidgetObserver
103   - assert_observer_notified Budget, BudgetObserver
104   - assert_observer_notified Widget, AuditTrail
105   - assert_observer_notified Budget, AuditTrail
106   - end
107   -
108   - test "can disable observers on individual models without affecting those observers on other models" do
109   - Widget.observers.disable :all
110   -
111   - assert_observer_not_notified Widget, WidgetObserver
112   - assert_observer_notified Budget, BudgetObserver
113   - assert_observer_not_notified Widget, AuditTrail
114   - assert_observer_notified Budget, AuditTrail
115   - end
116   -
117   - test "can enable observers on individual models without affecting those observers on other models" do
118   - ORM.observers.disable :all
119   - Budget.observers.enable AuditTrail
120   -
121   - assert_observer_not_notified Widget, WidgetObserver
122   - assert_observer_not_notified Budget, BudgetObserver
123   - assert_observer_not_notified Widget, AuditTrail
124   - assert_observer_notified Budget, AuditTrail
125   - end
126   -
127   - test "can disable observers for the duration of a block" do
128   - yielded = false
129   - ORM.observers.disable :budget_observer do
130   - yielded = true
131   - assert_observer_notified Widget, WidgetObserver
132   - assert_observer_not_notified Budget, BudgetObserver
133   - assert_observer_notified Widget, AuditTrail
134   - assert_observer_notified Budget, AuditTrail
135   - end
136   -
137   - assert yielded
138   - assert_observer_notified Widget, WidgetObserver
139   - assert_observer_notified Budget, BudgetObserver
140   - assert_observer_notified Widget, AuditTrail
141   - assert_observer_notified Budget, AuditTrail
142   - end
143   -
144   - test "can enable observers for the duration of a block" do
145   - yielded = false
146   - Widget.observers.disable :all
147   -
148   - Widget.observers.enable :all do
149   - yielded = true
150   - assert_observer_notified Widget, WidgetObserver
151   - assert_observer_notified Budget, BudgetObserver
152   - assert_observer_notified Widget, AuditTrail
153   - assert_observer_notified Budget, AuditTrail
154   - end
155   -
156   - assert yielded
157   - assert_observer_not_notified Widget, WidgetObserver
158   - assert_observer_notified Budget, BudgetObserver
159   - assert_observer_not_notified Widget, AuditTrail
160   - assert_observer_notified Budget, AuditTrail
161   - end
162   -
163   - test "raises an appropriate error when a developer accidentally enables or disables the wrong class (i.e. Widget instead of WidgetObserver)" do
164   - assert_raise ArgumentError do
165   - ORM.observers.enable :widget
166   - end
167   -
168   - assert_raise ArgumentError do
169   - ORM.observers.enable Widget
170   - end
171   -
172   - assert_raise ArgumentError do
173   - ORM.observers.disable :widget
174   - end
175   -
176   - assert_raise ArgumentError do
177   - ORM.observers.disable Widget
178   - end
179   - end
180   -
181   - test "allows #enable at the superclass level to override #disable at the subclass level when called last" do
182   - Widget.observers.disable :all
183   - ORM.observers.enable :all
184   -
185   - assert_observer_notified Widget, WidgetObserver
186   - assert_observer_notified Budget, BudgetObserver
187   - assert_observer_notified Widget, AuditTrail
188   - assert_observer_notified Budget, AuditTrail
189   - end
190   -
191   - test "allows #disable at the superclass level to override #enable at the subclass level when called last" do
192   - Budget.observers.enable :audit_trail
193   - ORM.observers.disable :audit_trail
194   -
195   - assert_observer_notified Widget, WidgetObserver
196   - assert_observer_notified Budget, BudgetObserver
197   - assert_observer_not_notified Widget, AuditTrail
198   - assert_observer_not_notified Budget, AuditTrail
199   - end
200   -
201   - test "can use the block form at different levels of the hierarchy" do
202   - yielded = false
203   - Widget.observers.disable :all
204   -
205   - ORM.observers.enable :all do
206   - yielded = true
207   - assert_observer_notified Widget, WidgetObserver
208   - assert_observer_notified Budget, BudgetObserver
209   - assert_observer_notified Widget, AuditTrail
210   - assert_observer_notified Budget, AuditTrail
211   - end
212   -
213   - assert yielded
214   - assert_observer_not_notified Widget, WidgetObserver
215   - assert_observer_notified Budget, BudgetObserver
216   - assert_observer_not_notified Widget, AuditTrail
217   - assert_observer_notified Budget, AuditTrail
218   - end
219   -end
220   -
181 activemodel/test/cases/observing_test.rb
... ... @@ -1,181 +0,0 @@
1   -require 'cases/helper'
2   -
3   -class ObservedModel
4   - include ActiveModel::Observing
5   -
6   - class Observer
7   - end
8   -end
9   -
10   -class FooObserver < ActiveModel::Observer
11   - class << self
12   - public :new
13   - end
14   -
15   - attr_accessor :stub
16   -
17   - def on_spec(record, *args)
18   - stub.event_with(record, *args) if stub
19   - end
20   -
21   - def around_save(record)
22   - yield :in_around_save
23   - end
24   -end
25   -
26   -class Foo
27   - include ActiveModel::Observing
28   -end
29   -
30   -class ObservingTest < ActiveModel::TestCase
31   - def setup
32   - ObservedModel.observers.clear
33   - end
34   -
35   - test "initializes model with no cached observers" do
36   - assert ObservedModel.observers.empty?, "Not empty: #{ObservedModel.observers.inspect}"
37   - end
38   -
39   - test "stores cached observers in an array" do
40   - ObservedModel.observers << :foo
41   - assert ObservedModel.observers.include?(:foo), ":foo not in #{ObservedModel.observers.inspect}"
42   - end
43   -
44   - test "flattens array of assigned cached observers" do
45   - ObservedModel.observers = [[:foo], :bar]
46   - assert ObservedModel.observers.include?(:foo), ":foo not in #{ObservedModel.observers.inspect}"
47   - assert ObservedModel.observers.include?(:bar), ":bar not in #{ObservedModel.observers.inspect}"
48   - end
49   -
50   - test "uses an ObserverArray so observers can be disabled" do
51   - ObservedModel.observers = [:foo, :bar]
52   - assert ObservedModel.observers.is_a?(ActiveModel::ObserverArray)
53   - end
54   -
55   - test "instantiates observer names passed as strings" do
56   - ObservedModel.observers << 'foo_observer'
57   - FooObserver.expects(:instance)
58   - ObservedModel.instantiate_observers
59   - end
60   -
61   - test "instantiates observer names passed as symbols" do
62   - ObservedModel.observers << :foo_observer
63   - FooObserver.expects(:instance)
64   - ObservedModel.instantiate_observers
65   - end
66   -
67   - test "instantiates observer classes" do
68   - ObservedModel.observers << ObservedModel::Observer
69   - ObservedModel::Observer.expects(:instance)
70   - ObservedModel.instantiate_observers
71   - end
72   -
73   - test "raises an appropriate error when a developer accidentally adds the wrong class (i.e. Widget instead of WidgetObserver)" do
74   - assert_raise ArgumentError do
75   - ObservedModel.observers = ['string']
76   - ObservedModel.instantiate_observers
77   - end
78   - assert_raise ArgumentError do
79   - ObservedModel.observers = [:string]
80   - ObservedModel.instantiate_observers
81   - end
82   - assert_raise ArgumentError do
83   - ObservedModel.observers = [String]
84   - ObservedModel.instantiate_observers
85   - end
86   - end
87   -
88   - test "passes observers to subclasses" do
89   - FooObserver.instance
90   - bar = Class.new(Foo)
91   - assert_equal Foo.observers_count, bar.observers_count
92   - end
93   -end
94   -
95   -class ObserverTest < ActiveModel::TestCase
96   - def setup
97   - ObservedModel.observers = :foo_observer
98   - FooObserver.singleton_class.instance_eval do
99   - alias_method :original_observed_classes, :observed_classes
100   - end
101   - end
102   -
103   - def teardown
104   - FooObserver.singleton_class.instance_eval do
105   - undef_method :observed_classes
106   - alias_method :observed_classes, :original_observed_classes
107   - end
108   - end
109   -
110   - test "guesses implicit observable model name" do
111   - assert_equal Foo, FooObserver.observed_class
112