aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record.rb1
-rw-r--r--activerecord/lib/active_record/base.rb2
-rw-r--r--activerecord/lib/active_record/observer.rb126
-rw-r--r--activerecord/lib/active_record/persistence.rb2
-rw-r--r--activerecord/lib/active_record/railtie.rb10
-rw-r--r--activerecord/lib/active_record/relation.rb9
-rw-r--r--activerecord/lib/rails/generators/active_record/observer/observer_generator.rb15
-rw-r--r--activerecord/lib/rails/generators/active_record/observer/templates/observer.rb4
-rw-r--r--activerecord/test/cases/dirty_test.rb2
-rw-r--r--activerecord/test/cases/lifecycle_test.rb256
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb81
11 files changed, 5 insertions, 503 deletions
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 45122539f1..822da84d19 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -45,7 +45,6 @@ module ActiveRecord
autoload :Migrator, 'active_record/migration'
autoload :ModelSchema
autoload :NestedAttributes
- autoload :Observer
autoload :Persistence
autoload :QueryCache
autoload :Querying
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 5eacb8f143..965fe3f33a 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -320,7 +320,6 @@ module ActiveRecord #:nodoc:
# So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
# instances in the current object space.
class Base
- extend ActiveModel::Observing::ClassMethods
extend ActiveModel::Naming
extend ActiveSupport::Benchmarkable
@@ -348,7 +347,6 @@ module ActiveRecord #:nodoc:
include Locking::Pessimistic
include AttributeMethods
include Callbacks
- include ActiveModel::Observing
include Timestamp
include Associations
include ActiveModel::SecurePassword
diff --git a/activerecord/lib/active_record/observer.rb b/activerecord/lib/active_record/observer.rb
deleted file mode 100644
index 6b2f6f98a5..0000000000
--- a/activerecord/lib/active_record/observer.rb
+++ /dev/null
@@ -1,126 +0,0 @@
-
-module ActiveRecord
- # = Active Record Observer
- #
- # Observer classes respond to life cycle callbacks to implement trigger-like
- # behavior outside the original class. This is a great way to reduce the
- # clutter that normally comes when the model class is burdened with
- # functionality that doesn't pertain to the core responsibility of the
- # class. Example:
- #
- # class CommentObserver < ActiveRecord::Observer
- # def after_save(comment)
- # Notifications.comment("admin@do.com", "New comment was posted", comment).deliver
- # end
- # end
- #
- # This Observer sends an email when a Comment#save is finished.
- #
- # class ContactObserver < ActiveRecord::Observer
- # def after_create(contact)
- # contact.logger.info('New contact added!')
- # end
- #
- # def after_destroy(contact)
- # contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
- # end
- # end
- #
- # This Observer uses logger to log when specific callbacks are triggered.
- #
- # == Observing a class that can't be inferred
- #
- # Observers will by default be mapped to the class with which they share a name. So CommentObserver will
- # be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
- # differently than the class you're interested in observing, you can use the Observer.observe class method which takes
- # either the concrete class (Product) or a symbol for that class (:product):
- #
- # class AuditObserver < ActiveRecord::Observer
- # observe :account
- #
- # def after_update(account)
- # AuditTrail.new(account, "UPDATED")
- # end
- # end
- #
- # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
- #
- # class AuditObserver < ActiveRecord::Observer
- # observe :account, :balance
- #
- # def after_update(record)
- # AuditTrail.new(record, "UPDATED")
- # end
- # end
- #
- # The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
- #
- # == Available callback methods
- #
- # The observer can implement callback methods for each of the methods described in the Callbacks module.
- #
- # == Storing Observers in Rails
- #
- # If you're using Active Record within Rails, observer classes are usually stored in app/models with the
- # naming convention of app/models/audit_observer.rb.
- #
- # == Configuration
- #
- # In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration
- # setting in your <tt>config/application.rb</tt> file.
- #
- # config.active_record.observers = :comment_observer, :signup_observer
- #
- # Observers will not be invoked unless you define these in your application configuration.
- #
- # If you are using Active Record outside Rails, activate the observers explicitly in a configuration or
- # environment file:
- #
- # ActiveRecord::Base.add_observer CommentObserver.instance
- # ActiveRecord::Base.add_observer SignupObserver.instance
- #
- # == Loading
- #
- # Observers register themselves in the model class they observe, since it is the class that
- # notifies them of events when they occur. As a side-effect, when an observer is loaded its
- # corresponding model class is loaded.
- #
- # Up to (and including) Rails 2.0.2 observers were instantiated between plugins and
- # application initializers. Now observers are loaded after application initializers,
- # so observed models can make use of extensions.
- #
- # If by any chance you are using observed models in the initialization you can still
- # load their observers by calling <tt>ModelObserver.instance</tt> before. Observers are
- # singletons and that call instantiates and registers them.
- #
- class Observer < ActiveModel::Observer
-
- protected
-
- def observed_classes
- klasses = super
- klasses + klasses.map { |klass| klass.descendants }.flatten
- end
-
- def add_observer!(klass)
- super
- define_callbacks klass
- end
-
- def define_callbacks(klass)
- observer = self
- observer_name = observer.class.name.underscore.gsub('/', '__')
-
- ActiveRecord::Callbacks::CALLBACKS.each do |callback|
- next unless respond_to?(callback)
- callback_meth = :"_notify_#{observer_name}_for_#{callback}"
- unless klass.respond_to?(callback_meth)
- klass.send(:define_method, callback_meth) do |&block|
- observer.update(callback, self, &block)
- end
- klass.send(callback, callback_meth)
- end
- end
- end
- end
-end
diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb
index eed49e17b1..81e56a3e2e 100644
--- a/activerecord/lib/active_record/persistence.rb
+++ b/activerecord/lib/active_record/persistence.rb
@@ -102,7 +102,7 @@ module ActiveRecord
# record's primary key, and no callbacks are executed.
#
# To enforce the object's +before_destroy+ and +after_destroy+
- # callbacks, Observer methods, or any <tt>:dependent</tt> association
+ # callbacks or any <tt>:dependent</tt> association
# options, use <tt>#destroy</tt>.
def delete
self.class.delete(id) if persisted?
diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb
index a73473f59c..624d2784e8 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -168,15 +168,5 @@ module ActiveRecord
path = app.paths["db"].first
config.watchable_files.concat ["#{path}/schema.rb", "#{path}/structure.sql"]
end
-
- config.after_initialize do |app|
- ActiveSupport.on_load(:active_record) do
- instantiate_observers
-
- ActionDispatch::Reloader.to_prepare do
- ActiveRecord::Base.instantiate_observers
- end
- end
- end
end
end
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index f0f170b684..0df895eb67 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -315,11 +315,9 @@ module ActiveRecord
# Destroys the records matching +conditions+ by instantiating each
# record and calling its +destroy+ method. Each object's callbacks are
- # executed (including <tt>:dependent</tt> association options and
- # +before_destroy+/+after_destroy+ Observer methods). Returns the
+ # executed (including <tt>:dependent</tt> association options). Returns the
# collection of objects that were destroyed; each will be frozen, to
- # reflect that no changes should be made (since they can't be
- # persisted).
+ # reflect that no changes should be made (since they can't be persisted).
#
# Note: Instantiation, callback execution, and deletion of each
# record can be time consuming when you're removing many records at
@@ -419,8 +417,7 @@ module ActiveRecord
# Deletes the row with a primary key matching the +id+ argument, using a
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
# Record objects are not instantiated, so the object's callbacks are not
- # executed, including any <tt>:dependent</tt> association options or
- # Observer methods.
+ # executed, including any <tt>:dependent</tt> association options.
#
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
#
diff --git a/activerecord/lib/rails/generators/active_record/observer/observer_generator.rb b/activerecord/lib/rails/generators/active_record/observer/observer_generator.rb
deleted file mode 100644
index e7445d03a2..0000000000
--- a/activerecord/lib/rails/generators/active_record/observer/observer_generator.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require 'rails/generators/active_record'
-
-module ActiveRecord
- module Generators # :nodoc:
- class ObserverGenerator < Base # :nodoc:
- check_class_collision :suffix => "Observer"
-
- def create_observer_file
- template 'observer.rb', File.join('app/models', class_path, "#{file_name}_observer.rb")
- end
-
- hook_for :test_framework
- end
- end
-end
diff --git a/activerecord/lib/rails/generators/active_record/observer/templates/observer.rb b/activerecord/lib/rails/generators/active_record/observer/templates/observer.rb
deleted file mode 100644
index eaa256a9bd..0000000000
--- a/activerecord/lib/rails/generators/active_record/observer/templates/observer.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-<% module_namespacing do -%>
-class <%= class_name %>Observer < ActiveRecord::Observer
-end
-<% end -%>
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index d3cbd839f6..55ee066cda 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -12,7 +12,7 @@ class Pirate # Just reopening it, not defining it
after_update :check_changes
private
- # after_save/update in sweepers, observers, and the model itself
+ # after_save/update and the model itself
# can end up checking dirty status and acting on the results
def check_changes
if self.changed?
diff --git a/activerecord/test/cases/lifecycle_test.rb b/activerecord/test/cases/lifecycle_test.rb
deleted file mode 100644
index 0b78f2e46b..0000000000
--- a/activerecord/test/cases/lifecycle_test.rb
+++ /dev/null
@@ -1,256 +0,0 @@
-require 'cases/helper'
-require 'models/topic'
-require 'models/developer'
-require 'models/reply'
-require 'models/minimalistic'
-require 'models/comment'
-
-class SpecialDeveloper < Developer; end
-
-class DeveloperObserver < ActiveRecord::Observer
- def calls
- @calls ||= []
- end
-
- def before_save(developer)
- calls << developer
- end
-end
-
-class SalaryChecker < ActiveRecord::Observer
- observe :special_developer
- attr_accessor :last_saved
-
- def before_save(developer)
- return developer.salary > 80000
- end
-
- module Implementation
- def after_save(developer)
- self.last_saved = developer
- end
- end
- include Implementation
-
-end
-
-class TopicaAuditor < ActiveRecord::Observer
- observe :topic
-
- attr_reader :topic
-
- def after_find(topic)
- @topic = topic
- end
-end
-
-class TopicObserver < ActiveRecord::Observer
- attr_reader :topic
-
- def after_find(topic)
- @topic = topic
- end
-
- # Create an after_save callback, so a notify_observer hook is created
- # on :topic.
- def after_save(nothing)
- end
-end
-
-class MinimalisticObserver < ActiveRecord::Observer
- attr_reader :minimalistic
-
- def after_find(minimalistic)
- @minimalistic = minimalistic
- end
-end
-
-class MultiObserver < ActiveRecord::Observer
- attr_reader :record
-
- def self.observed_class() [ Topic, Developer ] end
-
- cattr_reader :last_inherited
- @@last_inherited = nil
-
- def observed_class_inherited_with_testing(subclass)
- observed_class_inherited_without_testing(subclass)
- @@last_inherited = subclass
- end
-
- alias_method_chain :observed_class_inherited, :testing
-
- def after_find(record)
- @record = record
- end
-end
-
-class ValidatedComment < Comment
- attr_accessor :callers
-
- before_validation :record_callers
-
- after_validation do
- record_callers
- end
-
- def record_callers
- callers << self.class if callers
- end
-end
-
-class ValidatedCommentObserver < ActiveRecord::Observer
- attr_accessor :callers
-
- def after_validation(model)
- callers << self.class if callers
- end
-end
-
-
-class AroundTopic < Topic
-end
-
-class AroundTopicObserver < ActiveRecord::Observer
- observe :around_topic
- def topic_ids
- @topic_ids ||= []
- end
-
- def around_save(topic)
- topic_ids << topic.id
- yield(topic)
- topic_ids << topic.id
- end
-end
-
-class LifecycleTest < ActiveRecord::TestCase
- fixtures :topics, :developers, :minimalistics
-
- def test_before_destroy
- topic = Topic.find(1)
- assert_difference 'Topic.count', -(1 + topic.replies.size) do
- topic.destroy
- end
- end
-
- def test_auto_observer
- topic_observer = TopicaAuditor.instance
- assert_nil TopicaAuditor.observed_class
- assert_equal [Topic], TopicaAuditor.observed_classes.to_a
-
- topic = Topic.find(1)
- assert_equal topic.title, topic_observer.topic.title
- end
-
- def test_inferred_auto_observer
- topic_observer = TopicObserver.instance
- assert_equal Topic, TopicObserver.observed_class
-
- topic = Topic.find(1)
- assert_equal topic.title, topic_observer.topic.title
- end
-
- def test_observing_two_classes
- multi_observer = MultiObserver.instance
-
- topic = Topic.find(1)
- assert_equal topic.title, multi_observer.record.title
-
- developer = Developer.find(1)
- assert_equal developer.name, multi_observer.record.name
- end
-
- def test_observing_subclasses
- multi_observer = MultiObserver.instance
-
- developer = SpecialDeveloper.find(1)
- assert_equal developer.name, multi_observer.record.name
-
- klass = Class.new(Developer)
- assert_equal klass, multi_observer.last_inherited
-
- developer = klass.find(1)
- assert_equal developer.name, multi_observer.record.name
- end
-
- def test_after_find_can_be_observed_when_its_not_defined_on_the_model
- observer = MinimalisticObserver.instance
- assert_equal Minimalistic, MinimalisticObserver.observed_class
-
- minimalistic = Minimalistic.find(1)
- assert_equal minimalistic, observer.minimalistic
- end
-
- def test_after_find_can_be_observed_when_its_defined_on_the_model
- observer = TopicObserver.instance
- assert_equal Topic, TopicObserver.observed_class
-
- topic = Topic.find(1)
- assert_equal topic, observer.topic
- end
-
- def test_invalid_observer
- assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
- end
-
- test "model callbacks fire before observers are notified" do
- callers = []
-
- comment = ValidatedComment.new
- comment.callers = ValidatedCommentObserver.instance.callers = callers
-
- comment.valid?
- assert_equal [ValidatedComment, ValidatedComment, ValidatedCommentObserver], callers,
- "model callbacks did not fire before observers were notified"
- end
-
- test "able to save developer" do
- SalaryChecker.instance # activate
- developer = SpecialDeveloper.new :name => 'Roger', :salary => 100000
- assert developer.save, "developer with normal salary failed to save"
- end
-
- test "unable to save developer with low salary" do
- SalaryChecker.instance # activate
- developer = SpecialDeveloper.new :name => 'Rookie', :salary => 50000
- assert !developer.save, "allowed to save a developer with too low salary"
- end
-
- test "able to call methods defined with included module" do # https://rails.lighthouseapp.com/projects/8994/tickets/6065-activerecordobserver-is-not-aware-of-method-added-by-including-modules
- SalaryChecker.instance # activate
- developer = SpecialDeveloper.create! :name => 'Roger', :salary => 100000
- assert_equal developer, SalaryChecker.instance.last_saved
- end
-
- test "around filter from observer should accept block" do
- observer = AroundTopicObserver.instance
- topic = AroundTopic.new
- topic.save
- assert_nil observer.topic_ids.first
- assert_not_nil observer.topic_ids.last
- end
-
- test "able to disable observers" do
- observer = DeveloperObserver.instance # activate
- observer.calls.clear
-
- ActiveRecord::Base.observers.disable DeveloperObserver do
- Developer.create! :name => 'Ancestor', :salary => 100000
- SpecialDeveloper.create! :name => 'Descendent', :salary => 100000
- end
-
- assert_equal [], observer.calls
- end
-
- def test_observer_is_called_once
- observer = DeveloperObserver.instance # activate
- observer.calls.clear
-
- developer = Developer.create! :name => 'Ancestor', :salary => 100000
- special_developer = SpecialDeveloper.create! :name => 'Descendent', :salary => 100000
-
- assert_equal [developer, special_developer], observer.calls
- end
-
-end
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 961ba8d9ba..2ddc449c12 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -247,87 +247,6 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
end
-class TransactionObserverCallbacksTest < ActiveRecord::TestCase
- self.use_transactional_fixtures = false
- fixtures :topics
-
- class TopicWithObserverAttached < ActiveRecord::Base
- self.table_name = :topics
- def history
- @history ||= []
- end
- end
-
- class TopicWithObserverAttachedObserver < ActiveRecord::Observer
- def after_commit(record)
- record.history.push "after_commit"
- end
-
- def after_rollback(record)
- record.history.push "after_rollback"
- end
- end
-
- def test_after_commit_called
- assert TopicWithObserverAttachedObserver.instance, 'should have observer'
-
- topic = TopicWithObserverAttached.new
- topic.save!
-
- assert_equal %w{ after_commit }, topic.history
- end
-
- def test_after_rollback_called
- assert TopicWithObserverAttachedObserver.instance, 'should have observer'
-
- topic = TopicWithObserverAttached.new
-
- Topic.transaction do
- topic.save!
- raise ActiveRecord::Rollback
- end
-
- assert topic.id.nil?
- assert !topic.persisted?
- assert_equal %w{ after_rollback }, topic.history
- end
-
- class TopicWithManualRollbackObserverAttached < ActiveRecord::Base
- self.table_name = :topics
- def history
- @history ||= []
- end
- end
-
- class TopicWithManualRollbackObserverAttachedObserver < ActiveRecord::Observer
- def after_save(record)
- record.history.push "after_save"
- raise ActiveRecord::Rollback
- end
- end
-
- def test_after_save_called_with_manual_rollback
- assert TopicWithManualRollbackObserverAttachedObserver.instance, 'should have observer'
-
- topic = TopicWithManualRollbackObserverAttached.new
-
- assert !topic.save
- assert_equal nil, topic.id
- assert !topic.persisted?
- assert_equal %w{ after_save }, topic.history
- end
- def test_after_save_called_with_manual_rollback_bang
- assert TopicWithManualRollbackObserverAttachedObserver.instance, 'should have observer'
-
- topic = TopicWithManualRollbackObserverAttached.new
-
- topic.save!
- assert_equal nil, topic.id
- assert !topic.persisted?
- assert_equal %w{ after_save }, topic.history
- end
-end
-
class SaveFromAfterCommitBlockTest < ActiveRecord::TestCase
self.use_transactional_fixtures = false