From ccecab3ba950a288b61a516bf9b6962e384aae0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Sun, 14 Oct 2012 20:00:57 -0300 Subject: [PATCH 1/2] Remove observers and sweepers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They was extracted from a plugin. See https://github.com/rails/rails-observers [Rafael Mendonça França + Steve Klabnik] --- actionpack/lib/action_controller/caching.rb | 7 +- .../lib/action_controller/caching/sweeping.rb | 116 ------ actionpack/test/abstract_unit.rb | 1 - actionpack/test/controller/filters_test.rb | 41 -- actionpack/test/controller/sweeper_test.rb | 16 - activemodel/lib/active_model.rb | 2 - .../lib/active_model/observer_array.rb | 152 ------- activemodel/lib/active_model/observing.rb | 373 ------------------ activemodel/test/cases/observer_array_test.rb | 220 ----------- activemodel/test/cases/observing_test.rb | 181 --------- activemodel/test/models/observers.rb | 27 -- activerecord/lib/active_record.rb | 1 - activerecord/lib/active_record/base.rb | 2 - activerecord/lib/active_record/observer.rb | 126 ------ activerecord/lib/active_record/persistence.rb | 2 +- activerecord/lib/active_record/railtie.rb | 10 - activerecord/lib/active_record/relation.rb | 9 +- .../observer/observer_generator.rb | 15 - .../observer/templates/observer.rb | 4 - activerecord/test/cases/dirty_test.rb | 2 +- activerecord/test/cases/lifecycle_test.rb | 256 ------------ .../test/cases/transaction_callbacks_test.rb | 81 ---- .../getting_started/config/application.rb | 3 - .../active_record_validations_callbacks.md | 67 +--- guides/source/caching_with_rails.md | 98 ----- guides/source/configuring.md | 4 +- railties/lib/rails/engine.rb | 2 +- railties/lib/rails/generators.rb | 2 - railties/lib/rails/generators/actions.rb | 2 +- railties/lib/rails/generators/named_base.rb | 4 +- .../lib/rails/generators/rails/observer/USAGE | 12 - .../rails/observer/observer_generator.rb | 7 - .../test_unit/observer/observer_generator.rb | 13 - .../test_unit/observer/templates/unit_test.rb | 9 - .../test/application/configuration_test.rb | 21 - railties/test/application/console_test.rb | 17 - railties/test/application/rake_test.rb | 22 -- .../generators/namespaced_generators_test.rb | 21 - .../generators/observer_generator_test.rb | 27 -- 39 files changed, 14 insertions(+), 1961 deletions(-) delete mode 100644 actionpack/lib/action_controller/caching/sweeping.rb delete mode 100644 actionpack/test/controller/sweeper_test.rb delete mode 100644 activemodel/lib/active_model/observer_array.rb delete mode 100644 activemodel/lib/active_model/observing.rb delete mode 100644 activemodel/test/cases/observer_array_test.rb delete mode 100644 activemodel/test/cases/observing_test.rb delete mode 100644 activemodel/test/models/observers.rb delete mode 100644 activerecord/lib/active_record/observer.rb delete mode 100644 activerecord/lib/rails/generators/active_record/observer/observer_generator.rb delete mode 100644 activerecord/lib/rails/generators/active_record/observer/templates/observer.rb delete mode 100644 activerecord/test/cases/lifecycle_test.rb delete mode 100644 railties/lib/rails/generators/rails/observer/USAGE delete mode 100644 railties/lib/rails/generators/rails/observer/observer_generator.rb delete mode 100644 railties/lib/rails/generators/test_unit/observer/observer_generator.rb delete mode 100644 railties/lib/rails/generators/test_unit/observer/templates/unit_test.rb delete mode 100644 railties/test/generators/observer_generator_test.rb diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 462f14737149f..177da1c8a0062 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -6,10 +6,10 @@ module ActionController # \Caching is a cheap way of speeding up slow applications by keeping the result of # calculations, renderings, and database calls around for subsequent requests. # - # You can read more about each approach and the sweeping assistance by clicking the + # You can read more about each approach and the by clicking the # modules below. # - # Note: To turn off all caching and sweeping, set + # Note: To turn off all caching, set # config.action_controller.perform_caching = false. # # == \Caching stores @@ -30,8 +30,6 @@ module Caching eager_autoload do autoload :Fragments - autoload :Sweeper, 'action_controller/caching/sweeping' - autoload :Sweeping, 'action_controller/caching/sweeping' end module ConfigMethods @@ -54,7 +52,6 @@ def cache_configured? include ConfigMethods include Fragments - include Sweeping if defined?(ActiveRecord) included do extend ConfigMethods diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb deleted file mode 100644 index 317ac74b40dae..0000000000000 --- a/actionpack/lib/action_controller/caching/sweeping.rb +++ /dev/null @@ -1,116 +0,0 @@ -module ActionController - module Caching - # Sweepers are the terminators of the caching world and responsible for expiring - # caches when Active Record objects change. They do this by being half-observers, - # half-filters and implementing callbacks for both roles. - # - # class ListSweeper < ActionController::Caching::Sweeper - # observe List, Item - # - # def after_save(record) - # list = record.is_a?(List) ? record : record.list - # expire_page(controller: 'lists', action: %w( show public feed ), id: list.id) - # expire_action(controller: 'lists', action: 'all') - # list.shares.each { |share| expire_page(controller: 'lists', action: 'show', id: share.url_key) } - # end - # end - # - # The sweeper is assigned in the controllers that wish to have its job performed using - # the +cache_sweeper+ class method: - # - # class ListsController < ApplicationController - # caches_action :index, :show, :public, :feed - # cache_sweeper :list_sweeper, only: [ :edit, :destroy, :share ] - # end - # - # In the example above, four actions are cached and three actions are responsible for expiring those caches. - # - # You can also name an explicit class in the declaration of a sweeper, which is needed - # if the sweeper is in a module: - # - # class ListsController < ApplicationController - # caches_action :index, :show, :public, :feed - # cache_sweeper OpenBar::Sweeper, only: [ :edit, :destroy, :share ] - # end - module Sweeping - extend ActiveSupport::Concern - - module ClassMethods # :nodoc: - def cache_sweeper(*sweepers) - configuration = sweepers.extract_options! - - sweepers.each do |sweeper| - ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base) - sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance - - if sweeper_instance.is_a?(Sweeper) - around_filter(sweeper_instance, :only => configuration[:only]) - else - after_filter(sweeper_instance, :only => configuration[:only]) - end - end - end - end - end - - if defined?(ActiveRecord) and defined?(ActiveRecord::Observer) - class Sweeper < ActiveRecord::Observer # :nodoc: - attr_accessor :controller - - def initialize(*args) - super - @controller = nil - end - - def before(controller) - self.controller = controller - callback(:before) if controller.perform_caching - true # before method from sweeper should always return true - end - - def after(controller) - self.controller = controller - callback(:after) if controller.perform_caching - end - - def around(controller) - before(controller) - yield - after(controller) - ensure - clean_up - end - - protected - # gets the action cache path for the given options. - def action_path_for(options) - Actions::ActionCachePath.new(controller, options).path - end - - # Retrieve instance variables set in the controller. - def assigns(key) - controller.instance_variable_get("@#{key}") - end - - private - def clean_up - # Clean up, so that the controller can be collected after this request - self.controller = nil - end - - def callback(timing) - controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}" - action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}" - - __send__(controller_callback_method_name) if respond_to?(controller_callback_method_name, true) - __send__(action_callback_method_name) if respond_to?(action_callback_method_name, true) - end - - def method_missing(method, *arguments, &block) - return super unless @controller - @controller.__send__(method, *arguments, &block) - end - end - end - end -end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 4f5b2895c9b7a..95bff0a204e56 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -25,7 +25,6 @@ require 'active_model' require 'active_record' require 'action_controller/caching' -require 'action_controller/caching/sweeping' require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index d203601771dfa..1c59dd595360e 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -499,18 +499,6 @@ def filter_three end - class ::AppSweeper < ActionController::Caching::Sweeper; end - class SweeperTestController < ActionController::Base - cache_sweeper :app_sweeper - def show - render :text => 'hello world' - end - - def error - raise StandardError.new - end - end - class ImplicitActionsController < ActionController::Base before_filter :find_only, :only => :edit before_filter :find_except, :except => :edit @@ -526,35 +514,6 @@ def find_except end end - def test_sweeper_should_not_ignore_no_method_error - sweeper = ActionController::Caching::Sweeper.send(:new) - assert_raise NoMethodError do - sweeper.send_not_defined - end - end - - def test_sweeper_should_not_block_rendering - response = test_process(SweeperTestController) - assert_equal 'hello world', response.body - end - - def test_sweeper_should_clean_up_if_exception_is_raised - assert_raise StandardError do - test_process(SweeperTestController, 'error') - end - assert_nil AppSweeper.instance.controller - end - - def test_before_method_of_sweeper_should_always_return_true - sweeper = ActionController::Caching::Sweeper.send(:new) - assert sweeper.before(TestController.new) - end - - def test_after_method_of_sweeper_should_always_return_nil - sweeper = ActionController::Caching::Sweeper.send(:new) - assert_nil sweeper.after(TestController.new) - end - def test_non_yielding_around_filters_not_returning_false_do_not_raise controller = NonYieldingAroundFilterController.new controller.instance_variable_set "@filter_return_value", true diff --git a/actionpack/test/controller/sweeper_test.rb b/actionpack/test/controller/sweeper_test.rb deleted file mode 100644 index 0561efc62f5c0..0000000000000 --- a/actionpack/test/controller/sweeper_test.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'abstract_unit' - - -class SweeperTest < ActionController::TestCase - - class ::AppSweeper < ActionController::Caching::Sweeper; end - - def test_sweeper_should_not_ignore_unknown_method_calls - sweeper = ActionController::Caching::Sweeper.send(:new) - assert_raise NameError do - sweeper.instance_eval do - some_method_that_doesnt_exist - end - end - end -end diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index f757ba9843f06..0a32c5af053dc 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -40,8 +40,6 @@ module ActiveModel autoload :DeprecatedMassAssignmentSecurity autoload :Name, 'active_model/naming' autoload :Naming - autoload :Observer, 'active_model/observing' - autoload :Observing autoload :SecurePassword autoload :Serialization autoload :TestCase diff --git a/activemodel/lib/active_model/observer_array.rb b/activemodel/lib/active_model/observer_array.rb deleted file mode 100644 index 77bc0f71e38b5..0000000000000 --- a/activemodel/lib/active_model/observer_array.rb +++ /dev/null @@ -1,152 +0,0 @@ -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) #:nodoc: - @model_class = model_class - super(*args) - end - - # Returns +true+ if the given observer is disabled for the model class, - # +false+ otherwise. - def disabled_for?(observer) #:nodoc: - disabled_observers.include?(observer.class) - end - - # Disables one or more observers. This supports multiple forms: - # - # ORM.observers.disable :all - # # => disables all observers for all models subclassed from - # # an ORM base class that includes ActiveModel::Observing - # # e.g. ActiveRecord::Base - # - # 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. - # - # 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 :all - # # => enables all observers for all models subclassed from - # # an ORM base class that includes ActiveModel::Observing - # # e.g. ActiveRecord::Base - # - # 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. - # - # 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 #:nodoc: - @disabled_observers ||= Set.new - end - - def observer_class_for(observer) #:nodoc: - 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 #:nodoc: - disabled_observer_stack.push(disabled_observers.dup) - each_subclass_array do |array| - array.start_transaction - end - end - - def disabled_observer_stack #:nodoc: - @disabled_observer_stack ||= [] - end - - def end_transaction #:nodoc: - @disabled_observers = disabled_observer_stack.pop - each_subclass_array do |array| - array.end_transaction - end - end - - def transaction #:nodoc: - start_transaction - - begin - yield - ensure - end_transaction - end - end - - def each_subclass_array #:nodoc: - model_class.descendants.each do |subclass| - yield subclass.observers - end - end - - def set_enablement(enabled, observers) #:nodoc: - 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 diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb deleted file mode 100644 index b4e40bada4800..0000000000000 --- a/activemodel/lib/active_model/observing.rb +++ /dev/null @@ -1,373 +0,0 @@ -require 'singleton' -require 'active_model/observer_array' -require 'active_support/core_ext/module/aliasing' -require 'active_support/core_ext/module/remove_method' -require 'active_support/core_ext/string/inflections' -require 'active_support/core_ext/enumerable' -require 'active_support/core_ext/object/try' - -module ActiveModel - # == Active \Model Observers Activation - module Observing - extend ActiveSupport::Concern - - included do - extend ActiveSupport::DescendantsTracker - end - - module ClassMethods - # Activates the observers assigned. - # - # class ORM - # include ActiveModel::Observing - # end - # - # # Calls PersonObserver.instance - # ORM.observers = :person_observer - # - # # Calls Cacher.instance and GarbageCollector.instance - # ORM.observers = :cacher, :garbage_collector - # - # # Same as above, just using explicit class references - # ORM.observers = Cacher, GarbageCollector - # - # Note: Setting this does not instantiate the observers yet. - # instantiate_observers is called during startup, and before - # each development request. - def observers=(*values) - observers.replace(values.flatten) - end - - # Gets an array of observers observing this model. The array also provides - # +enable+ and +disable+ methods that allow you to selectively enable and - # disable observers (see ActiveModel::ObserverArray.enable and - # ActiveModel::ObserverArray.disable for more on this). - # - # class ORM - # include ActiveModel::Observing - # end - # - # ORM.observers = :cacher, :garbage_collector - # ORM.observers # => [:cacher, :garbage_collector] - # ORM.observers.class # => ActiveModel::ObserverArray - def observers - @observers ||= ObserverArray.new(self) - end - - # Returns the current observer instances. - # - # class Foo - # include ActiveModel::Observing - # - # attr_accessor :status - # end - # - # class FooObserver < ActiveModel::Observer - # def on_spec(record, *args) - # record.status = true - # end - # end - # - # Foo.observers = FooObserver - # Foo.instantiate_observers - # - # Foo.observer_instances # => [#] - def observer_instances - @observer_instances ||= [] - end - - # Instantiate the global observers. - # - # class Foo - # include ActiveModel::Observing - # - # attr_accessor :status - # end - # - # class FooObserver < ActiveModel::Observer - # def on_spec(record, *args) - # record.status = true - # end - # end - # - # Foo.observers = FooObserver - # - # foo = Foo.new - # foo.status = false - # foo.notify_observers(:on_spec) - # foo.status # => false - # - # Foo.instantiate_observers # => [FooObserver] - # - # foo = Foo.new - # foo.status = false - # foo.notify_observers(:on_spec) - # foo.status # => true - def instantiate_observers - observers.each { |o| instantiate_observer(o) } - end - - # Add a new observer to the pool. The new observer needs to respond to - # update, otherwise it raises an +ArgumentError+ exception. - # - # class Foo - # include ActiveModel::Observing - # end - # - # class FooObserver < ActiveModel::Observer - # end - # - # Foo.add_observer(FooObserver.instance) - # - # Foo.observers_instance - # # => [#] - def add_observer(observer) - unless observer.respond_to? :update - raise ArgumentError, "observer needs to respond to 'update'" - end - observer_instances << observer - end - - # Fires notifications to model's observers. - # - # def save - # notify_observers(:before_save) - # ... - # notify_observers(:after_save) - # end - # - # Custom notifications can be sent in a similar fashion: - # - # notify_observers(:custom_notification, :foo) - # - # This will call custom_notification, passing as arguments - # the current object and :foo. - def notify_observers(*args) - observer_instances.each { |observer| observer.update(*args) } - end - - # Returns the total number of instantiated observers. - # - # class Foo - # include ActiveModel::Observing - # - # attr_accessor :status - # end - # - # class FooObserver < ActiveModel::Observer - # def on_spec(record, *args) - # record.status = true - # end - # end - # - # Foo.observers = FooObserver - # Foo.observers_count # => 0 - # Foo.instantiate_observers - # Foo.observers_count # => 1 - def observers_count - observer_instances.size - end - - # count_observers is deprecated. Use #observers_count. - def count_observers - msg = "count_observers is deprecated in favor of observers_count" - ActiveSupport::Deprecation.warn msg - observers_count - end - - protected - def instantiate_observer(observer) #:nodoc: - # string/symbol - if observer.respond_to?(:to_sym) - observer = observer.to_s.camelize.constantize - end - if observer.respond_to?(:instance) - observer.instance - else - raise ArgumentError, - "#{observer} must be a lowercase, underscored class name (or " + - "the class itself) responding to the method :instance. " + - "Example: Person.observers = :big_brother # calls " + - "BigBrother.instance" - end - end - - # Notify observers when the observed class is subclassed. - def inherited(subclass) #:nodoc: - super - notify_observers :observed_class_inherited, subclass - end - end - - # Notify a change to the list of observers. - # - # class Foo - # include ActiveModel::Observing - # - # attr_accessor :status - # end - # - # class FooObserver < ActiveModel::Observer - # def on_spec(record, *args) - # record.status = true - # end - # end - # - # Foo.observers = FooObserver - # Foo.instantiate_observers # => [FooObserver] - # - # foo = Foo.new - # foo.status = false - # foo.notify_observers(:on_spec) - # foo.status # => true - # - # See ActiveModel::Observing::ClassMethods.notify_observers for more - # information. - def notify_observers(method, *extra_args) - self.class.notify_observers(method, self, *extra_args) - end - end - - # == Active \Model Observers - # - # 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. - # - # class CommentObserver < ActiveModel::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 < ActiveModel::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 < ActiveModel::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 < ActiveModel::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. - # - # If you're using an Observer in a Rails application with Active Record, be - # sure to read about the necessary configuration in the documentation for - # ActiveRecord::Observer. - class Observer - include Singleton - extend ActiveSupport::DescendantsTracker - - class << self - # Attaches the observer to the supplied model classes. - # - # class AuditObserver < ActiveModel::Observer - # observe :account, :balance - # end - # - # AuditObserver.observed_classes # => [Account, Balance] - def observe(*models) - models.flatten! - models.collect! { |model| model.respond_to?(:to_sym) ? model.to_s.camelize.constantize : model } - singleton_class.redefine_method(:observed_classes) { models } - end - - # Returns an array of Classes to observe. - # - # AccountObserver.observed_classes # => [Account] - # - # You can override this instead of using the +observe+ helper. - # - # class AuditObserver < ActiveModel::Observer - # def self.observed_classes - # [Account, Balance] - # end - # end - def observed_classes - Array(observed_class) - end - - # Returns the class observed by default. It's inferred from the observer's - # class name. - # - # PersonObserver.observed_class # => Person - # AccountObserver.observed_class # => Account - def observed_class - name[/(.*)Observer/, 1].try :constantize - end - end - - # Start observing the declared classes and their subclasses. - # Called automatically by the instance method. - def initialize #:nodoc: - observed_classes.each { |klass| add_observer!(klass) } - end - - def observed_classes #:nodoc: - self.class.observed_classes - end - - # Send observed_method(object) if the method exists and - # the observer is enabled for the given object's class. - def update(observed_method, object, *extra_args, &block) #:nodoc: - return if !respond_to?(observed_method) || disabled_for?(object) - send(observed_method, object, *extra_args, &block) - end - - # Special method sent by the observed class when it is inherited. - # Passes the new subclass. - def observed_class_inherited(subclass) #:nodoc: - self.class.observe(observed_classes + [subclass]) - add_observer!(subclass) - end - - protected - def add_observer!(klass) #:nodoc: - klass.add_observer(self) - end - - # Returns true if notifications are disabled for this object. - def disabled_for?(object) #:nodoc: - klass = object.class - return false unless klass.respond_to?(:observers) - klass.observers.disabled_for?(self) - end - end -end diff --git a/activemodel/test/cases/observer_array_test.rb b/activemodel/test/cases/observer_array_test.rb deleted file mode 100644 index fc5f18008ba4c..0000000000000 --- a/activemodel/test/cases/observer_array_test.rb +++ /dev/null @@ -1,220 +0,0 @@ -require 'cases/helper' -require 'models/observers' - -class ObserverArrayTest < ActiveModel::TestCase - def teardown - ORM.observers.enable :all - Budget.observers.enable :all - Widget.observers.enable :all - end - - def assert_observer_notified(model_class, observer_class) - observer_class.instance.before_save_invocations.clear - model_instance = model_class.new - model_instance.save - assert_equal [model_instance], observer_class.instance.before_save_invocations - end - - def assert_observer_not_notified(model_class, observer_class) - observer_class.instance.before_save_invocations.clear - model_instance = model_class.new - model_instance.save - assert_equal [], observer_class.instance.before_save_invocations - end - - test "all observers are enabled by default" do - assert_observer_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "can disable individual observers using a class constant" do - ORM.observers.disable WidgetObserver - - assert_observer_not_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "can enable individual observers using a class constant" do - ORM.observers.disable :all - ORM.observers.enable AuditTrail - - assert_observer_not_notified Widget, WidgetObserver - assert_observer_not_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "can disable individual observers using a symbol" do - ORM.observers.disable :budget_observer - - assert_observer_notified Widget, WidgetObserver - assert_observer_not_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "can enable individual observers using a symbol" do - ORM.observers.disable :all - ORM.observers.enable :audit_trail - - assert_observer_not_notified Widget, WidgetObserver - assert_observer_not_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "can disable multiple observers at a time" do - ORM.observers.disable :widget_observer, :budget_observer - - assert_observer_not_notified Widget, WidgetObserver - assert_observer_not_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "can enable multiple observers at a time" do - ORM.observers.disable :all - ORM.observers.enable :widget_observer, :budget_observer - - assert_observer_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_not_notified Widget, AuditTrail - assert_observer_not_notified Budget, AuditTrail - end - - test "can disable all observers using :all" do - ORM.observers.disable :all - - assert_observer_not_notified Widget, WidgetObserver - assert_observer_not_notified Budget, BudgetObserver - assert_observer_not_notified Widget, AuditTrail - assert_observer_not_notified Budget, AuditTrail - end - - test "can enable all observers using :all" do - ORM.observers.disable :all - ORM.observers.enable :all - - assert_observer_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "can disable observers on individual models without affecting those observers on other models" do - Widget.observers.disable :all - - assert_observer_not_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_not_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "can enable observers on individual models without affecting those observers on other models" do - ORM.observers.disable :all - Budget.observers.enable AuditTrail - - assert_observer_not_notified Widget, WidgetObserver - assert_observer_not_notified Budget, BudgetObserver - assert_observer_not_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "can disable observers for the duration of a block" do - yielded = false - ORM.observers.disable :budget_observer do - yielded = true - assert_observer_notified Widget, WidgetObserver - assert_observer_not_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - assert yielded - assert_observer_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "can enable observers for the duration of a block" do - yielded = false - Widget.observers.disable :all - - Widget.observers.enable :all do - yielded = true - assert_observer_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - assert yielded - assert_observer_not_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_not_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "raises an appropriate error when a developer accidentally enables or disables the wrong class (i.e. Widget instead of WidgetObserver)" do - assert_raise ArgumentError do - ORM.observers.enable :widget - end - - assert_raise ArgumentError do - ORM.observers.enable Widget - end - - assert_raise ArgumentError do - ORM.observers.disable :widget - end - - assert_raise ArgumentError do - ORM.observers.disable Widget - end - end - - test "allows #enable at the superclass level to override #disable at the subclass level when called last" do - Widget.observers.disable :all - ORM.observers.enable :all - - assert_observer_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - test "allows #disable at the superclass level to override #enable at the subclass level when called last" do - Budget.observers.enable :audit_trail - ORM.observers.disable :audit_trail - - assert_observer_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_not_notified Widget, AuditTrail - assert_observer_not_notified Budget, AuditTrail - end - - test "can use the block form at different levels of the hierarchy" do - yielded = false - Widget.observers.disable :all - - ORM.observers.enable :all do - yielded = true - assert_observer_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end - - assert yielded - assert_observer_not_notified Widget, WidgetObserver - assert_observer_notified Budget, BudgetObserver - assert_observer_not_notified Widget, AuditTrail - assert_observer_notified Budget, AuditTrail - end -end - diff --git a/activemodel/test/cases/observing_test.rb b/activemodel/test/cases/observing_test.rb deleted file mode 100644 index ade6026602901..0000000000000 --- a/activemodel/test/cases/observing_test.rb +++ /dev/null @@ -1,181 +0,0 @@ -require 'cases/helper' - -class ObservedModel - include ActiveModel::Observing - - class Observer - end -end - -class FooObserver < ActiveModel::Observer - class << self - public :new - end - - attr_accessor :stub - - def on_spec(record, *args) - stub.event_with(record, *args) if stub - end - - def around_save(record) - yield :in_around_save - end -end - -class Foo - include ActiveModel::Observing -end - -class ObservingTest < ActiveModel::TestCase - def setup - ObservedModel.observers.clear - end - - test "initializes model with no cached observers" do - assert ObservedModel.observers.empty?, "Not empty: #{ObservedModel.observers.inspect}" - end - - test "stores cached observers in an array" do - ObservedModel.observers << :foo - assert ObservedModel.observers.include?(:foo), ":foo not in #{ObservedModel.observers.inspect}" - end - - test "flattens array of assigned cached observers" do - ObservedModel.observers = [[:foo], :bar] - assert ObservedModel.observers.include?(:foo), ":foo not in #{ObservedModel.observers.inspect}" - assert ObservedModel.observers.include?(:bar), ":bar not in #{ObservedModel.observers.inspect}" - end - - test "uses an ObserverArray so observers can be disabled" do - ObservedModel.observers = [:foo, :bar] - assert ObservedModel.observers.is_a?(ActiveModel::ObserverArray) - end - - test "instantiates observer names passed as strings" do - ObservedModel.observers << 'foo_observer' - FooObserver.expects(:instance) - ObservedModel.instantiate_observers - end - - test "instantiates observer names passed as symbols" do - ObservedModel.observers << :foo_observer - FooObserver.expects(:instance) - ObservedModel.instantiate_observers - end - - test "instantiates observer classes" do - ObservedModel.observers << ObservedModel::Observer - ObservedModel::Observer.expects(:instance) - ObservedModel.instantiate_observers - end - - test "raises an appropriate error when a developer accidentally adds the wrong class (i.e. Widget instead of WidgetObserver)" do - assert_raise ArgumentError do - ObservedModel.observers = ['string'] - ObservedModel.instantiate_observers - end - assert_raise ArgumentError do - ObservedModel.observers = [:string] - ObservedModel.instantiate_observers - end - assert_raise ArgumentError do - ObservedModel.observers = [String] - ObservedModel.instantiate_observers - end - end - - test "passes observers to subclasses" do - FooObserver.instance - bar = Class.new(Foo) - assert_equal Foo.observers_count, bar.observers_count - end -end - -class ObserverTest < ActiveModel::TestCase - def setup - ObservedModel.observers = :foo_observer - FooObserver.singleton_class.instance_eval do - alias_method :original_observed_classes, :observed_classes - end - end - - def teardown - FooObserver.singleton_class.instance_eval do - undef_method :observed_classes - alias_method :observed_classes, :original_observed_classes - end - end - - test "guesses implicit observable model name" do - assert_equal Foo, FooObserver.observed_class - end - - test "tracks implicit observable models" do - instance = FooObserver.new - assert_equal [Foo], instance.observed_classes - end - - test "tracks explicit observed model class" do - FooObserver.observe ObservedModel - instance = FooObserver.new - assert_equal [ObservedModel], instance.observed_classes - end - - test "tracks explicit observed model as string" do - FooObserver.observe 'observed_model' - instance = FooObserver.new - assert_equal [ObservedModel], instance.observed_classes - end - - test "tracks explicit observed model as symbol" do - FooObserver.observe :observed_model - instance = FooObserver.new - assert_equal [ObservedModel], instance.observed_classes - end - - test "calls existing observer event" do - foo = Foo.new - FooObserver.instance.stub = stub - FooObserver.instance.stub.expects(:event_with).with(foo) - Foo.notify_observers(:on_spec, foo) - end - - test "calls existing observer event from the instance" do - foo = Foo.new - FooObserver.instance.stub = stub - FooObserver.instance.stub.expects(:event_with).with(foo) - foo.notify_observers(:on_spec) - end - - test "passes extra arguments" do - foo = Foo.new - FooObserver.instance.stub = stub - FooObserver.instance.stub.expects(:event_with).with(foo, :bar) - Foo.send(:notify_observers, :on_spec, foo, :bar) - end - - test "skips nonexistent observer event" do - foo = Foo.new - Foo.notify_observers(:whatever, foo) - end - - test "update passes a block on to the observer" do - yielded_value = nil - FooObserver.instance.update(:around_save, Foo.new) do |val| - yielded_value = val - end - assert_equal :in_around_save, yielded_value - end - - test "observe redefines observed_classes class method" do - class BarObserver < ActiveModel::Observer - observe :foo - end - - assert_equal [Foo], BarObserver.observed_classes - - BarObserver.observe(ObservedModel) - assert_equal [ObservedModel], BarObserver.observed_classes - end -end diff --git a/activemodel/test/models/observers.rb b/activemodel/test/models/observers.rb deleted file mode 100644 index 3729b3435efa1..0000000000000 --- a/activemodel/test/models/observers.rb +++ /dev/null @@ -1,27 +0,0 @@ -class ORM - include ActiveModel::Observing - - def save - notify_observers :before_save - end - - class Observer < ActiveModel::Observer - def before_save_invocations - @before_save_invocations ||= [] - end - - def before_save(record) - before_save_invocations << record - end - end -end - -class Widget < ORM; end -class Budget < ORM; end -class WidgetObserver < ORM::Observer; end -class BudgetObserver < ORM::Observer; end -class AuditTrail < ORM::Observer - observe :widget, :budget -end - -ORM.instantiate_observers diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 45122539f1e9c..822da84d1959b 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 5eacb8f143259..965fe3f33a3a5 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 Base.logger= 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 @@ class Base 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 6b2f6f98a5f9b..0000000000000 --- 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 config.active_record.observers configuration - # setting in your config/application.rb 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 ModelObserver.instance 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 eed49e17b14fe..81e56a3e2e82f 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -102,7 +102,7 @@ def save!(*) # record's primary key, and no callbacks are executed. # # To enforce the object's +before_destroy+ and +after_destroy+ - # callbacks, Observer methods, or any :dependent association + # callbacks or any :dependent association # options, use #destroy. 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 a73473f59cf8e..624d2784e882d 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -168,15 +168,5 @@ class Railtie < Rails::Railtie # :nodoc: 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 f0f170b684c49..0df895eb67ed3 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -315,11 +315,9 @@ def update(id, attributes) # Destroys the records matching +conditions+ by instantiating each # record and calling its +destroy+ method. Each object's callbacks are - # executed (including :dependent association options and - # +before_destroy+/+after_destroy+ Observer methods). Returns the + # executed (including :dependent 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 @@ def delete_all(conditions = nil) # 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 :dependent association options or - # Observer methods. + # executed, including any :dependent association options. # # You can delete multiple rows at once by passing an Array of ids. # 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 e7445d03a2c7a..0000000000000 --- 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 eaa256a9bd2f4..0000000000000 --- 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 d3cbd839f6e27..55ee066cdaf88 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 0b78f2e46b18c..0000000000000 --- 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 961ba8d9badf8..2ddc449c127ca 100644 --- a/activerecord/test/cases/transaction_callbacks_test.rb +++ b/activerecord/test/cases/transaction_callbacks_test.rb @@ -247,87 +247,6 @@ def @first.last_after_transaction_error; @last_transaction_error; end 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 diff --git a/guides/code/getting_started/config/application.rb b/guides/code/getting_started/config/application.rb index d2cd5c028b16a..d53c9fd8bcdc1 100644 --- a/guides/code/getting_started/config/application.rb +++ b/guides/code/getting_started/config/application.rb @@ -18,9 +18,6 @@ class Application < Rails::Application # Custom directories with classes and modules you want to be autoloadable. # config.autoload_paths += %W(#{config.root}/extras) - # Activate observers that should always be running. - # config.active_record.observers = :cacher, :garbage_collector, :forum_observer - # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # config.time_zone = 'Central Time (US & Canada)' diff --git a/guides/source/active_record_validations_callbacks.md b/guides/source/active_record_validations_callbacks.md index 0f4140b6500ba..4432df5e76aa6 100644 --- a/guides/source/active_record_validations_callbacks.md +++ b/guides/source/active_record_validations_callbacks.md @@ -11,7 +11,6 @@ After reading this guide and trying out the presented concepts, we hope that you * Work with the error messages generated by the validation process * Create callback methods that respond to events in the object life cycle * Create special classes that encapsulate common behavior for your callbacks -* Create Observers that respond to life cycle events outside of the original class -------------------------------------------------------------------------------- @@ -20,7 +19,7 @@ The Object Life Cycle During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this object life cycle so that you can control your application and its data. -Validations allow you to ensure that only valid data is stored in your database. Callbacks and observers allow you to trigger logic before or after an alteration of an object's state. +Validations allow you to ensure that only valid data is stored in your database. Callbacks allow you to trigger logic before or after an alteration of an object's state. Validations Overview -------------------- @@ -1272,70 +1271,6 @@ end You can declare as many callbacks as you want inside your callback classes. -Observers ---------- - -Observers are similar to callbacks, but with important differences. Whereas callbacks can pollute a model with code that isn't directly related to its purpose, observers allow you to add the same functionality without changing the code of the model. For example, it could be argued that a `User` model should not include code to send registration confirmation emails. Whenever you use callbacks with code that isn't directly related to your model, you may want to consider creating an observer instead. - -### Creating Observers - -For example, imagine a `User` model where we want to send an email every time a new user is created. Because sending emails is not directly related to our model's purpose, we should create an observer to contain the code implementing this functionality. - -```bash -$ rails generate observer User -``` - -generates `app/models/user_observer.rb` containing the observer class `UserObserver`: - -```ruby -class UserObserver < ActiveRecord::Observer -end -``` - -You may now add methods to be called at the desired occasions: - -```ruby -class UserObserver < ActiveRecord::Observer - def after_create(model) - # code to send confirmation email... - end -end -``` - -As with callback classes, the observer's methods receive the observed model as a parameter. - -### Registering Observers - -Observers are conventionally placed inside of your `app/models` directory and registered in your application's `config/application.rb` file. For example, the `UserObserver` above would be saved as `app/models/user_observer.rb` and registered in `config/application.rb` this way: - -```ruby -# Activate observers that should always be running. -config.active_record.observers = :user_observer -``` - -As usual, settings in `config/environments` take precedence over those in `config/application.rb`. So, if you prefer that an observer doesn't run in all environments, you can simply register it in a specific environment instead. - -### Sharing Observers - -By default, Rails will simply strip "Observer" from an observer's name to find the model it should observe. However, observers can also be used to add behavior to more than one model, and thus it is possible to explicitly specify the models that our observer should observe: - -```ruby -class MailerObserver < ActiveRecord::Observer - observe :registration, :user - - def after_create(model) - # code to send confirmation email... - end -end -``` - -In this example, the `after_create` method will be called whenever a `Registration` or `User` is created. Note that this new `MailerObserver` would also need to be registered in `config/application.rb` in order to take effect: - -```ruby -# Activate observers that should always be running. -config.active_record.observers = :mailer_observer -``` - Transaction Callbacks --------------------- diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 826ddd264a667..be2cd0e1a9b62 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -67,8 +67,6 @@ class ProductsController < ActionController end ``` -If you want a more complicated expiration scheme, you can use cache sweepers to expire cached objects when things change. This is covered in the section on Sweepers. - By default, page caching automatically gzips files (for example, to `products.html.gz` if user requests `/products`) to reduce the size of data transmitted (web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once, compression ratio is maximum). Nginx is able to serve compressed content directly from disk by enabling `gzip_static`: @@ -176,102 +174,6 @@ This fragment is then available to all actions in the `ProductsController` using expire_fragment('all_available_products') ``` -### Sweepers - -Cache sweeping is a mechanism which allows you to get around having a ton of `expire_{page,action,fragment}` calls in your code. It does this by moving all the work required to expire cached content into an `ActionController::Caching::Sweeper` subclass. This class is an observer and looks for changes to an Active Record object via callbacks, and when a change occurs it expires the caches associated with that object in an around or after filter. - -TIP: Sweepers rely on the use of Active Record and Active Record Observers. The object you are observing must be an Active Record model. - -Continuing with our Product controller example, we could rewrite it with a sweeper like this: - -```ruby -class ProductSweeper < ActionController::Caching::Sweeper - observe Product # This sweeper is going to keep an eye on the Product model - - # If our sweeper detects that a Product was created call this - def after_create(product) - expire_cache_for(product) - end - - # If our sweeper detects that a Product was updated call this - def after_update(product) - expire_cache_for(product) - end - - # If our sweeper detects that a Product was deleted call this - def after_destroy(product) - expire_cache_for(product) - end - - private - def expire_cache_for(product) - # Expire the index page now that we added a new product - expire_page(controller: 'products', action: 'index') - - # Expire a fragment - expire_fragment('all_available_products') - end -end -``` - -You may notice that the actual product gets passed to the sweeper, so if we were caching the edit action for each product, we could add an expire method which specifies the page we want to expire: - -```ruby -expire_action(controller: 'products', action: 'edit', id: product.id) -``` - -Then we add it to our controller to tell it to call the sweeper when certain actions are called. So, if we wanted to expire the cached content for the list and edit actions when the create action was called, we could do the following: - -```ruby -class ProductsController < ActionController - - before_filter :authenticate - caches_action :index - cache_sweeper :product_sweeper - - def index - @products = Product.all - end - -end -``` - -Sometimes it is necessary to disambiguate the controller when you call `expire_action`, such as when there are two identically named controllers in separate namespaces: - -```ruby -class ProductsController < ActionController - caches_action :index - - def index - @products = Product.all - end -end - -module Admin - class ProductsController < ActionController - cache_sweeper :product_sweeper - - def new - @product = Product.new - end - - def create - @product = Product.create(params[:product]) - end - end -end - -class ProductSweeper < ActionController::Caching::Sweeper - observe Product - - def after_create(product) - expire_action(controller: '/products', action: 'index') - end -end -``` - -Note the use of '/products' here rather than 'products'. If you wanted to expire an action cache for the `Admin::ProductsController`, you would use 'admin/products' instead. - ### SQL Caching Query caching is a Rails feature that caches the result set returned by each query so that if Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again. diff --git a/guides/source/configuring.md b/guides/source/configuring.md index ac763d6e0e75a..486dc30acc1d6 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -37,7 +37,7 @@ config.filter_parameters += [:password] This is a setting for Rails itself. If you want to pass settings to individual Rails components, you can do so via the same `config` object in `config/application.rb`: ```ruby -config.active_record.observers = [:hotel_observer, :review_observer] +config.active_record.schema_format = :ruby ``` Rails will use that particular setting to configure Active Record. @@ -614,7 +614,7 @@ Rails.application.config.before_initialize do end ``` -WARNING: Some parts of your application, notably observers and routing, are not yet set up at the point where the `after_initialize` block is called. +WARNING: Some parts of your application, notably routing, are not yet set up at the point where the `after_initialize` block is called. ### `Rails::Railtie#initializer` diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index f6721c617fc2d..725295004fb7e 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -106,7 +106,7 @@ module Rails # # The Application class adds a couple more paths to this set. And as in your # Application, all folders under +app+ are automatically added to the load path. - # If you have an app/observers folder for example, it will be added by default. + # If you have an app/services/tt> folder for example, it will be added by default. # # == Endpoint # diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 367f9288b8df8..d9a91b74d17f6 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -172,13 +172,11 @@ def self.hidden_namespaces "resource_route", "#{orm}:migration", "#{orm}:model", - "#{orm}:observer", "#{test}:controller", "#{test}:helper", "#{test}:integration", "#{test}:mailer", "#{test}:model", - "#{test}:observer", "#{test}:scaffold", "#{test}:view", "#{test}:performance", diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 5c4e81431c689..b96ee9295ed54 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -78,7 +78,7 @@ def add_source(source, options={}) # end # # environment(nil, env: "development") do - # "config.active_record.observers = :cacher" + # "config.autoload_paths += %W(#{config.root}/extras)" # end def environment(data=nil, options={}, &block) sentinel = /class [a-z_:]+ < Rails::Application/i diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb index 84f8f76838e52..cc10fd9177eb3 100644 --- a/railties/lib/rails/generators/named_base.rb +++ b/railties/lib/rails/generators/named_base.rb @@ -169,10 +169,10 @@ def pluralize_table_names? # # ==== Examples # - # check_class_collision suffix: "Observer" + # check_class_collision suffix: "Decorator" # # If the generator is invoked with class name Admin, it will check for - # the presence of "AdminObserver". + # the presence of "AdminDecorator". # def self.check_class_collision(options={}) define_method :check_class_collision do diff --git a/railties/lib/rails/generators/rails/observer/USAGE b/railties/lib/rails/generators/rails/observer/USAGE deleted file mode 100644 index 177ff49e4af75..0000000000000 --- a/railties/lib/rails/generators/rails/observer/USAGE +++ /dev/null @@ -1,12 +0,0 @@ -Description: - Stubs out a new observer. Pass the observer name, either CamelCased or - under_scored, as an argument. - - This generator only invokes your ORM and test framework generators. - -Example: - `rails generate observer Account` - - For ActiveRecord and TestUnit it creates: - Observer: app/models/account_observer.rb - TestUnit: test/models/account_observer_test.rb diff --git a/railties/lib/rails/generators/rails/observer/observer_generator.rb b/railties/lib/rails/generators/rails/observer/observer_generator.rb deleted file mode 100644 index 7a4d701ac609d..0000000000000 --- a/railties/lib/rails/generators/rails/observer/observer_generator.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Rails - module Generators - class ObserverGenerator < NamedBase # :nodoc: - hook_for :orm, required: true - end - end -end diff --git a/railties/lib/rails/generators/test_unit/observer/observer_generator.rb b/railties/lib/rails/generators/test_unit/observer/observer_generator.rb deleted file mode 100644 index 64fe694a8ba3e..0000000000000 --- a/railties/lib/rails/generators/test_unit/observer/observer_generator.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'rails/generators/test_unit' - -module TestUnit # :nodoc: - module Generators # :nodoc: - class ObserverGenerator < Base # :nodoc: - check_class_collision suffix: "ObserverTest" - - def create_test_files - template 'unit_test.rb', File.join('test/models', class_path, "#{file_name}_observer_test.rb") - end - end - end -end diff --git a/railties/lib/rails/generators/test_unit/observer/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/observer/templates/unit_test.rb deleted file mode 100644 index 28aa23626aea5..0000000000000 --- a/railties/lib/rails/generators/test_unit/observer/templates/unit_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'test_helper' - -<% module_namespacing do -%> -class <%= class_name %>ObserverTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end -<% end -%> diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index b9d18f4582843..ae1127b509cac 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -582,27 +582,6 @@ def index assert app.config.colorize_logging end - test "config.active_record.observers" do - add_to_config <<-RUBY - config.active_record.observers = :foo_observer - RUBY - - app_file 'app/models/foo.rb', <<-RUBY - class Foo < ActiveRecord::Base - end - RUBY - - app_file 'app/models/foo_observer.rb', <<-RUBY - class FooObserver < ActiveRecord::Observer - end - RUBY - - require "#{app_path}/config/environment" - - ActiveRecord::Base - assert defined?(FooObserver) - end - test "config.session_store with :active_record_store with activerecord-session_store gem" do begin make_basic_app do |app| diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb index f372afa51c5c8..3cb3643e3a4a8 100644 --- a/railties/test/application/console_test.rb +++ b/railties/test/application/console_test.rb @@ -95,21 +95,4 @@ def test_with_sandbox load_environment(true) assert value end - - def test_active_record_does_not_panic_when_referencing_an_observed_constant - add_to_config "config.active_record.observers = :user_observer" - - app_file "app/models/user.rb", <<-MODEL - class User < ActiveRecord::Base - end - MODEL - - app_file "app/models/user_observer.rb", <<-MODEL - class UserObserver < ActiveRecord::Observer - end - MODEL - - load_environment - assert_nothing_raised { User } - end end diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index 076c5b3bbd9b9..f2234ab111914 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -249,28 +249,6 @@ def test_rake_clear_schema_cache assert !File.exists?(File.join(app_path, 'db', 'schema_cache.dump')) end - def test_load_activerecord_base_when_we_use_observers - Dir.chdir(app_path) do - `bundle exec rails g model user; - bundle exec rake db:migrate; - bundle exec rails g observer user;` - - add_to_config "config.active_record.observers = :user_observer" - - assert_equal "0", `bundle exec rails r "puts User.count"`.strip - - app_file "lib/tasks/count_user.rake", <<-RUBY - namespace :user do - task count: :environment do - puts User.count - end - end - RUBY - - assert_equal "0", `bundle exec rake user:count`.strip - end - end - def test_copy_templates Dir.chdir(app_path) do `bundle exec rake rails:templates:copy` diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb index db268ed6f2bfb..a4d8b3d1b017d 100644 --- a/railties/test/generators/namespaced_generators_test.rb +++ b/railties/test/generators/namespaced_generators_test.rb @@ -1,7 +1,6 @@ require 'generators/generators_test_helper' require 'rails/generators/rails/controller/controller_generator' require 'rails/generators/rails/model/model_generator' -require 'rails/generators/rails/observer/observer_generator' require 'rails/generators/mailer/mailer_generator' require 'rails/generators/rails/scaffold/scaffold_generator' @@ -142,26 +141,6 @@ def test_invokes_default_test_framework end end -class NamespacedObserverGeneratorTest < NamespacedGeneratorTestCase - arguments %w(account) - tests Rails::Generators::ObserverGenerator - - def test_invokes_default_orm - run_generator - assert_file "app/models/test_app/account_observer.rb", /module TestApp/, / class AccountObserver < ActiveRecord::Observer/ - end - - def test_invokes_default_orm_with_class_path - run_generator ["admin/account"] - assert_file "app/models/test_app/admin/account_observer.rb", /module TestApp/, / class Admin::AccountObserver < ActiveRecord::Observer/ - end - - def test_invokes_default_test_framework - run_generator - assert_file "test/models/test_app/account_observer_test.rb", /module TestApp/, / class AccountObserverTest < ActiveSupport::TestCase/ - end -end - class NamespacedMailerGeneratorTest < NamespacedGeneratorTestCase arguments %w(notifier foo bar) tests Rails::Generators::MailerGenerator diff --git a/railties/test/generators/observer_generator_test.rb b/railties/test/generators/observer_generator_test.rb deleted file mode 100644 index 12318274662d5..0000000000000 --- a/railties/test/generators/observer_generator_test.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'generators/generators_test_helper' -require 'rails/generators/rails/observer/observer_generator' - -class ObserverGeneratorTest < Rails::Generators::TestCase - include GeneratorsTestHelper - arguments %w(account) - - def test_invokes_default_orm - run_generator - assert_file "app/models/account_observer.rb", /class AccountObserver < ActiveRecord::Observer/ - end - - def test_invokes_default_orm_with_class_path - run_generator ["admin/account"] - assert_file "app/models/admin/account_observer.rb", /class Admin::AccountObserver < ActiveRecord::Observer/ - end - - def test_invokes_default_test_framework - run_generator - assert_file "test/models/account_observer_test.rb", /class AccountObserverTest < ActiveSupport::TestCase/ - end - - def test_logs_if_the_test_framework_cannot_be_found - content = run_generator ["account", "--test-framework=rspec"] - assert_match(/rspec \[not found\]/, content) - end -end From f862376d169acfe362552787c095ff4b93834f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 28 Nov 2012 21:55:07 -0200 Subject: [PATCH 2/2] Gracefully handle upgrading apps with observers Rather than just raising a NoMethodError when copying the config, this commit adds a warning message until either the rails-observers gem is installed or the relevant config options are removed. --- activerecord/lib/active_record/railtie.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 624d2784e882d..c3f13b2dacf8b 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -115,6 +115,18 @@ class Railtie < Rails::Railtie # :nodoc: See http://edgeguides.rubyonrails.org/security.html#mass-assignment for more information EOF end + + unless app.config.active_record.delete(:observers).nil? + ActiveSupport::Deprecation.warn <<-EOF.strip_heredoc, [] + Active Record Observers has been extracted out of Rails into a gem. + Please use callbaks or add `rails-observers` to your Gemfile to use observers. + + To disable this message remove the `observers` option from your + `config/application.rb` or from your initializers. + + See http://edgeguides.rubyonrails.org/4_0_release_notes.html for more information + EOF + end ensure ActiveSupport::Deprecation.behavior = old_behavior end