Changeset 4521
- Timestamp:
- 06/30/06 04:38:24 (4 years ago)
- Files:
-
- trunk/activerecord/CHANGELOG (modified) (1 diff)
- trunk/activerecord/lib/active_record/observer.rb (modified) (5 diffs)
- trunk/activerecord/test/lifecycle_test.rb (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/activerecord/CHANGELOG
r4511 r4521 1 1 *SVN* 2 3 * Observers also watch subclasses created after they are declared. #5535 [daniels@pronto.com.au] 2 4 3 5 * Removed deprecated timestamps_gmt class methods. [Jeremy Kemper] trunk/activerecord/lib/active_record/observer.rb
r4310 r4521 1 1 require 'singleton' 2 require 'set' 2 3 3 4 module ActiveRecord … … 13 14 # ActiveRecord::Base.observers = :person_observer 14 15 # 15 # # Calls Cacher.instance and GarbageCollector.instance 16 # # Calls Cacher.instance and GarbageCollector.instance 16 17 # ActiveRecord::Base.observers = :cacher, :garbage_collector 17 18 # … … 19 20 # ActiveRecord::Base.observers = Cacher, GarbageCollector 20 21 def observers=(*observers) 21 observers = [ observers ].flatten.each do |observer| 22 observer.is_a?(Symbol) ? 23 observer.to_s.camelize.constantize.instance : 22 observers.flatten.each do |observer| 23 if observer.respond_to?(:to_sym) # Symbol or String 24 observer.to_s.camelize.constantize.instance 25 elsif observer.respond_to?(:instance) 24 26 observer.instance 27 else 28 raise ArgumentError, "#{observer} must be a lowercase, underscored class name (or an instance of the class itself) responding to the instance method. Example: Person.observers = :big_brother # calls BigBrother.instance" 29 end 25 30 end 26 31 end 32 33 protected 34 # Notify observers when the observed class is subclassed. 35 def inherited(subclass) 36 super 37 changed 38 notify_observers :observed_class_inherited, subclass 39 end 27 40 end 28 41 end … … 85 98 # 86 99 # == Storing Observers in Rails 87 # 100 # 88 101 # If you're using Active Record within Rails, observer classes are usually stored in app/models with the 89 102 # naming convention of app/models/audit_observer.rb. 90 103 # 91 104 # == Configuration 92 # 105 # 93 106 # In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration setting in your 94 107 # <tt>config/environment.rb</tt> file. … … 104 117 # when Dependencies.mechanism = :load. 105 118 include Reloadable::Subclasses 106 107 # Attaches the observer to the supplied model classes. 108 def self.observe(*models) 109 define_method(:observed_class) { models } 119 120 class << self 121 # Attaches the observer to the supplied model classes. 122 def observe(*models) 123 define_method(:observed_classes) { Set.new(models) } 124 end 125 126 # The class observed by default is inferred from the observer's class name: 127 # assert_equal [Person], PersonObserver.observed_class 128 def observed_class 129 name.scan(/(.*)Observer/)[0][0].constantize 130 end 110 131 end 111 132 133 # Start observing the declared classes and their subclasses. 112 134 def initialize 113 observed_classes = [ observed_class ].flatten 114 observed_subclasses_class = observed_classes.collect {|c| c.send(:subclasses) }.flatten! 115 (observed_classes + observed_subclasses_class).each do |klass| 135 Set.new(observed_classes + observed_subclasses).each { |klass| add_observer! klass } 136 end 137 138 # Send observed_method(object) if the method exists. 139 def update(observed_method, object) #:nodoc: 140 send(observed_method, object) if respond_to?(observed_method) 141 end 142 143 # Special method sent by the observed class when it is inherited. 144 # Passes the new subclass. 145 def observed_class_inherited(subclass) #:nodoc: 146 self.class.observe(observed_classes + [subclass]) 147 add_observer!(subclass) 148 end 149 150 protected 151 def observed_classes 152 Set.new([self.class.observed_class].flatten) 153 end 154 155 def observed_subclasses 156 observed_classes.sum(&:subclasses) 157 end 158 159 def add_observer!(klass) 116 160 klass.add_observer(self) 117 klass.send(:define_method, :after_find) unless klass.respond_to?(:after_find) 118 end 119 end 120 121 def update(callback_method, object) #:nodoc: 122 send(callback_method, object) if respond_to?(callback_method) 123 end 124 125 private 126 def observed_class 127 if self.class.respond_to? "observed_class" 128 self.class.observed_class 129 else 130 Object.const_get(infer_observed_class_name) 131 end 132 end 133 134 def infer_observed_class_name 135 self.class.name.scan(/(.*)Observer/)[0][0] 161 klass.class_eval 'def after_find() end' unless klass.respond_to?(:after_find) 136 162 end 137 163 end trunk/activerecord/test/lifecycle_test.rb
r2327 r4521 47 47 class MultiObserver < ActiveRecord::Observer 48 48 attr_reader :record 49 49 50 50 def self.observed_class() [ Topic, Developer ] end 51 52 cattr_reader :last_inherited 53 @@last_inherited = nil 54 55 def observed_class_inherited_with_testing(subclass) 56 observed_class_inherited_without_testing(subclass) 57 @@last_inherited = subclass 58 end 59 60 alias_method_chain :observed_class_inherited, :testing 51 61 52 62 def after_find(record) 53 63 @record = record 54 64 end 55 56 65 end 57 66 … … 64 73 assert_equal 0, Topic.count 65 74 end 66 75 67 76 def test_after_save 68 77 ActiveRecord::Base.observers = :topic_manual_observer … … 71 80 topic.title = "hello" 72 81 topic.save 73 82 74 83 assert TopicManualObserver.instance.has_been_notified? 75 84 assert_equal :after_save, TopicManualObserver.instance.callbacks.last["callback_method"] 76 85 end 77 86 78 87 def test_observer_update_on_save 79 88 ActiveRecord::Base.observers = TopicManualObserver 80 89 81 topic = Topic.find(1) 90 topic = Topic.find(1) 82 91 assert TopicManualObserver.instance.has_been_notified? 83 92 assert_equal :after_find, TopicManualObserver.instance.callbacks.first["callback_method"] 84 93 end 85 94 86 95 def test_auto_observer 87 96 topic_observer = TopicaObserver.instance 88 97 89 topic = Topic.find(1) 90 assert_equal topic _observer.topic.title,topic.title98 topic = Topic.find(1) 99 assert_equal topic.title, topic_observer.topic.title 91 100 end 92 93 def test_infer ed_auto_observer101 102 def test_inferred_auto_observer 94 103 topic_observer = TopicObserver.instance 95 104 96 topic = Topic.find(1) 97 assert_equal topic _observer.topic.title,topic.title105 topic = Topic.find(1) 106 assert_equal topic.title, topic_observer.topic.title 98 107 end 99 108 100 109 def test_observing_two_classes 101 110 multi_observer = MultiObserver.instance 102 111 103 112 topic = Topic.find(1) 104 assert_equal multi_observer.record.title, topic.title113 assert_equal topic.title, multi_observer.record.title 105 114 106 developer = Developer.find(1) 107 assert_equal multi_observer.record.name, developer.name115 developer = Developer.find(1) 116 assert_equal developer.name, multi_observer.record.name 108 117 end 109 118 110 119 def test_observing_subclasses 111 120 multi_observer = MultiObserver.instance 112 121 113 122 developer = SpecialDeveloper.find(1) 114 assert_equal multi_observer.record.name, developer.name 123 assert_equal developer.name, multi_observer.record.name 124 125 klass = Class.new(Developer) 126 assert_equal klass, multi_observer.last_inherited 127 128 developer = klass.find(1) 129 assert_equal developer.name, multi_observer.record.name 130 end 131 132 def test_invalid_observer 133 assert_raise(ArgumentError) { Topic.observers = Object.new } 115 134 end 116 135 end